Examples
Find usages of a function called
open
:$ pyastgrep './/Call/func/Name[@id="open"]' src/pyastgrep/search.py:88:18: with open(path) as f:
Literal numbers:
$ pyastgrep './/Constant[@type="int" or @type="float"]' tests/examples/test_xml/everything.py:5:20: assigned_int = 123 tests/examples/test_xml/everything.py:6:22: assigned_float = 3.14
Function calls where:
the function is named
open
:the second positional argument is a string literal containing the character
b
:
pyastgrep './/Call[./func/Name[@id="open"]][./args/Constant[position()=1][contains(@value, "b")]]'
Usages of
open
that are not in awith
item expression:pyastgrep './/Call[not(ancestor::withitem)]/func/Name[@id="open"]'
Names longer than 42 characters, wherever they are used.
pyastgrep './/Name[string-length(@id) > 42]'
except
clauses that raise a different exception class than they catch:pyastgrep "//ExceptHandler[body//Raise/exc//Name and not(contains(body//Raise/exc//Name/@id, type/Name/@id))]"
Functions whose name contain a certain substring:
pyastgrep './/FunctionDef[contains(@name, "something")]'
Classes whose name matches a regular expression:
pyastgrep ".//ClassDef[re:match('M.*', @name)]"
The above uses the Python re.match method. You can also use
re:search
to use the Python re.search method.Case-insensitive match of names on the left hand side of an assignment containing a certain string. This can be achieved using the
lower-case
function from XPath2:pyastgrep './/Assign/targets//Name[contains(lower-case(@id), "something")]' --xpath2
You can also use regexes, passing the
i
(case-insensitive flag) as below, as described in the Python Regular Expression Syntax docspyastgrep './/Assign/targets//Name[re:search("(?i)something", @id)]'
Assignments to the name
foo
, including type annotated assignments, which useAnnAssign
, and tuple unpacking assignments (while avoiding things likefoo.bar = ...
). Note the use of the|
operator to do a union.pyastgrep '(.//AnnAssign/target|.//Assign/targets|.//Assign/targets/Tuple/elts)/Name[@id="foo"]'
Docstrings of functions/methods whose value contains “hello”:
pyastgrep './/FunctionDef/body/Expr[1]/value/Constant[@type="str"][contains(@value, "hello")]'
For-loop variables called
i
orj
(including those created by tuple unpacking):pyastgrep './/For/target//Name[@id="i" or @id="j"]'
Method calls: These are actually “calls” on objects that are attributes of other objects. This will match the top-level object:
pyastgrep './/Call/func/Attribute'
Individual positional arguments to a method call named
encode
, where the arguments are literal strings or numbers. Note the use ofCall[…]
to match “Call nodes that have descendants that match …”, rather than matching those descendant nodes themselves.pyastgrep './/Call[./func/Attribute[@attr="encode"]]/args/Constant'
For a Django code base, find all
.filter
and.exclude
method calls, and allQ
object calls, which have a keyword argument where the name contains the string"user"
, for finding ORM calls like.filter(user__id__in=...)
orQ(thing__user=...)
:pyastgrep '(.//Call[./func/Attribute[@attr="filter" or @attr="exclude"]] | .//Call[./func/Name[@id="Q"]]) [./keywords/keyword[contains(@arg, "user")]]'
Annotations involving
list
orset
ordict
that are in the body of a class definition that is decorated as@dataclass(frozen=True)
:pyastgrep './/ClassDef/body/AnnAssign/annotation//Name[@id="list" or @id="set" or @id="dict"][ancestor::ClassDef[./decorator_list/Call[./func/Name[@id="dataclass"]][./keywords/keyword[@arg="frozen"]/value/Constant[@value="True"]]]]'
Notes the use of:
//Name
so that we find things likefoo: list
andfoo: tuple[list]
andfoo: list[str]
ancestor
to match back to the context we’re interested in. We could instead have done:pyastgrep './/ClassDef[./decorator_list/Call[./func/Name[@id="dataclass"]][./keywords/keyword[@arg="frozen"]/value/Constant[@value="True"]]][./body/AnnAssign/annotation//Name[@id="list" or @id="set" or @id="dict"]]'
but this will match the
class
definition rather than the bad annotation.