Using Refex as a Library¶
Alright, your one-line shell script change isn’t enough anymore. What’s next?
Create a searcher¶
The “thing that does a search and a replace” is a searcher: any subclass of
refex.search.AbstractSearcher
. You likely want
PyExprRewritingSearcher
, and, for the replacement, an
instance of refex.python.syntactic_template.PythonExprTemplate
.
This ends up a bit clunky, but you can see how it works in the example at the bottom of the page.
If you want to manipulate, filter, or otherwise look at the replacements being performed, this is where you can hook in: define a new searcher that wraps the old one and modifies its results.
Execute the search / replace¶
Apply a searcher to a string¶
refex.search.rewrite_string()
executes a simple rewrite.
Alternatively, you can collect a list of
Substitution
objects and apply them in a second
pass, using refex.search.find_iter()
and
refex.formatting.apply_substitutions()
– but really, it’s better
to manipulate those substitutions from a custom searcher, since that
searcher can also be used e.g. to create an executable, as the section below
describes.
Create an executable¶
The same colorization, diff display, etc. as the refex command can
be yours: instead of rewriting individual strings, you can pass the searcher
to refex.cli.run()
.
Here’s a complete example:
"""An example binary using refex.cli.run() to run a preprogrammed search/replace.
This is similar in functionality to a shell script that runs:
refex --mode=py.expr hello --sub=world -i "$@"
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import sys
from refex import cli
from refex import search
from refex.python import syntactic_template
from refex.python.matchers import syntax_matchers
def main():
cli.run(
runner=cli.RefexRunner(
searcher=search.PyExprRewritingSearcher.from_matcher(
# --mode=py.expr is equivalent to PyExprRewritingSearcher paired
# with an ExprPattern. However, you can pass any matcher, not just
# an ExprPattern.
syntax_matchers.ExprPattern('hello'),
{
# Using ROOT_LABEL as a key is equivalent to --sub=world.
# To get the equivalent of --named-sub=x=world,
# it would 'x' as a key instead.
#
# The value type corresponds to the --sub-mode. While
# refex on the command line defaults to picking the paired
# --sub-mode that matches the --mode, here there are no
# defaults and you must be explicit.
# e.g. for unsafe textual substitutions, as with
# --sub-mode=sh, you would use formatting.ShTemplate.
search.ROOT_LABEL:
syntactic_template.PythonExprTemplate('world')
},
),
dry_run=False,
),
files=sys.argv[1:],
bug_report_url='<project bug report URL goes here>',
)
if __name__ == '__main__':
main()