potluck.patterns

Common code patterns for use with the mast AST matching module.

patterns.py

  1"""
  2Common code patterns for use with the `mast` AST matching module.
  3
  4patterns.py
  5"""
  6
  7
  8#-------------------#
  9# Pattern constants #
 10#-------------------#
 11
 12# Common mast pattern for a for loop.
 13FOR_PATTERN = '''
 14for _ in _:
 15    ___
 16'''
 17"""
 18`potluck.mast` pattern for simple `for` loop.
 19"""
 20
 21
 22# For loop w/ else (above pattern doesn't match this).
 23FOR_ELSE_PATTERN = '''
 24for _ in _:
 25    ___
 26else:
 27    ___
 28'''
 29"""
 30`potluck.mast` pattern for a `for` loop with an `else` clause.
 31"""
 32
 33
 34# Both of the above...
 35ALL_FOR_PATTERNS = [ FOR_PATTERN, FOR_ELSE_PATTERN ]
 36"""
 37`potluck.mast` patterns for all `for` loop variants.
 38"""
 39
 40
 41# Pattern for a while loop.
 42WHILE_PATTERN = '''
 43while _:
 44    ___
 45'''
 46"""
 47A `potluck.mast` pattern for a simple `while` loop.
 48"""
 49
 50
 51# While loop with an else.
 52WHILE_ELSE_PATTERN = '''
 53while _:
 54    ___
 55else:
 56    ___
 57'''
 58"""
 59A `potluck.mast` pattern for `while`/`else`.
 60"""
 61
 62
 63# Both of the above...
 64ALL_WHILE_PATTERNS = [ WHILE_PATTERN, WHILE_ELSE_PATTERN ]
 65"""
 66`potluck.mast` patterns for all `while` loop variants.
 67"""
 68
 69
 70# List comprehensions
 71SIMPLE_COMPREHENSION_PATTERN = '''
 72[ _ for _ in _ ]
 73'''
 74"""
 75A `potluck.mast` pattern for a single for loop comprehension without a filter.
 76"""
 77
 78
 79FILTER_COMPREHENSION_PATTERN = '''
 80[ _ for _ in _ if _ ]
 81'''
 82"""
 83A `potluck.mast` pattern for a single for loop comprehension with a filter.
 84"""
 85
 86
 87DOUBLE_COMPREHENSION_PATTERN = '''
 88[ _ for _ in _ for _ in _ ]
 89'''
 90"""
 91A `potluck.mast` pattern for a double for loop comprehension without a filter.
 92"""
 93
 94
 95DOUBLE_FILTER_COMPREHENSION_PATTERN = '''
 96[ _ for _ in _ for _ in _ if _ ]
 97'''
 98"""
 99A `potluck.mast` pattern for a double for loop comprehension with a filter.
100"""
101
102
103TRIPLE_COMPREHENSION_PATTERN = '''
104[ _ for _ in _ for _ in _ for _ in _ ]
105'''
106"""
107A `potluck.mast` pattern for a triple for loop comprehension without a filter.
108"""
109
110
111TRIPLE_FILTER_COMPREHENSION_PATTERN = '''
112[ _ for _ in _ for _ in _ for _ in _ if _ ]
113'''
114"""
115A `potluck.mast` pattern for a triple for loop comprehension with a filter.
116"""
117
118
119SINGLE_LOOP_COMPREHENSION_PATTERNS = [
120    SIMPLE_COMPREHENSION_PATTERN,
121    FILTER_COMPREHENSION_PATTERN
122]
123"""
124`potluck.mast patterns for just single list comprehensions with and
125without filtering.
126"""
127
128ALL_REASONABLE_COMPREHENSION_PATTERNS = [
129    SIMPLE_COMPREHENSION_PATTERN,
130    FILTER_COMPREHENSION_PATTERN,
131    DOUBLE_COMPREHENSION_PATTERN,
132    DOUBLE_FILTER_COMPREHENSION_PATTERN,
133    TRIPLE_COMPREHENSION_PATTERN,
134    TRIPLE_FILTER_COMPREHENSION_PATTERN,
135]
136"""
137`potluck.mast` patterns for single, double, and triple list comprehensions
138with and without filtering.
139
140Note: does not include dictionary comprehensions (see below)
141
142We will simply hope that students do not use quadruple comprehensions...
143"""
144
145ALL_SINGLE_LOOP_GENERATOR_EXPRESSION_PATTERNS = (
146    SINGLE_LOOP_COMPREHENSION_PATTERNS
147  + [
148      p.replace('[', '(').replace(']', ')')
149      for p in SINGLE_LOOP_COMPREHENSION_PATTERNS
150    ] # generator expressions
151  + [
152      p.replace('[', '{').replace(']', '}')
153      for p in SINGLE_LOOP_COMPREHENSION_PATTERNS
154    ] # set comprehensions
155  + [
156      p.replace('[ _', '{ _: _').replace(']', '}')
157      for p in SINGLE_LOOP_COMPREHENSION_PATTERNS
158    ] # dictionary comprehensions
159)
160"""
161`potluck.mast` patterns for single-loop comprehensions including set and
162dictionary comprehensions and generator expressions.
163"""
164
165ALL_GENERATOR_EXPRESSION_PATTERNS = (
166    ALL_REASONABLE_COMPREHENSION_PATTERNS
167  + [
168      p.replace('[', '(').replace(']', ')')
169      for p in ALL_REASONABLE_COMPREHENSION_PATTERNS
170    ] # generator expressions
171  + [
172      p.replace('[', '{').replace(']', '}')
173      for p in ALL_REASONABLE_COMPREHENSION_PATTERNS
174    ] # set comprehensions
175  + [
176      p.replace('[ _', '{ _: _').replace(']', '}')
177      for p in ALL_REASONABLE_COMPREHENSION_PATTERNS
178    ] # dictionary comprehensions
179)
180"""
181Patterns for up to triple-loop list comprehensions, plus equivalent
182generator expressions and set- and dictionary-comprehensions.
183"""
184
185
186# Combination patterns for loops and loops + comprehensions
187ALL_FOR_AND_WHILE_LOOP_PATTERNS = ALL_FOR_PATTERNS + ALL_WHILE_PATTERNS
188"""
189`potluck.mast` patterns for all while/for loop variants.
190"""
191ALL_LOOP_AND_COMPREHENSION_PATTERNS = (
192    ALL_FOR_AND_WHILE_LOOP_PATTERNS
193  + ALL_GENERATOR_EXPRESSION_PATTERNS
194)
195"""
196`potluck.mast` patterns for all reasonable loop/comprehension types,
197although highly-nested comprehensions are not included.
198"""
199ALL_SINGLE_LOOP_AND_COMPREHENSION_PATTERNS = (
200    ALL_FOR_AND_WHILE_LOOP_PATTERNS
201  + ALL_SINGLE_LOOP_GENERATOR_EXPRESSION_PATTERNS
202)
203"""
204`potluck.mast` patterns for all loop/comprehension patterns that are
205single loops, including generator expressions, set comprehensions, and
206dictionary comprehensions.
207"""
208
209
210IF_PATTERN = '''
211if _:
212    ___
213else:
214    ___
215'''
216"""
217Common mast pattern for an if statement (matches even ifs that don't have
218an else, we think; note this also matches elifs, which Python treats as
219nested ifs).
220"""
221
222
223ALL_DEF_PATTERNS = [
224    "def _f_(___):\n  ___",
225    "def _f_(___, ___=_):\n  ___",
226    "def _f_(___=_):\n  ___",
227]
228"""
229Patterns for function definitions (without keyword variables, with both,
230and with only keyword variables). These patterns bind 'f' as the name of
231the function that's defined.
232"""
233
234
235FUNCTION_CALL_PATTERNS = [
236    "_f_(___)",
237    "_f_(___, ___=_)",
238    "_f_(___=_)",
239]
240"""
241Patterns for function calls. These bind 'f' as the function (possibly a
242function expression).
243"""
244
245
246METHOD_CALL_PATTERNS = [
247    "_._f_(___)",
248    "_._f_(___, ___=_)",
249    "_._f_(___=_)",
250]
251"""
252Patterns for method calls specifically. Note that the
253`FUNCTION_CALL_PATTERNS` will find method calls too, but will bind 'f' as
254the entire object + method expression, whereas these patterns will bind
255'f' as the method name (multiple bindings will result from chained method
256calls).
257"""
258
259TRY_EXCEPT_PATTERNS = [
260    "try:\n  ___\nexcept:\n  ___",
261    "try:\n  ___\nexcept:\n  ___\nfinally:\n  ___",
262    "try:\n  ___\nexcept _:\n  ___",
263    "try:\n  ___\nexcept _:\n  ___\nfinally:\n  ___",
264    "try:\n  ___\nexcept _ as e:\n  ___",
265    "try:\n  ___\nexcept _ as e:\n  ___\nfinally:\n  ___",
266    "try:\n  ___\nfinally:\n  ___",
267]
268"""
269Patterns for try/except/finally blocks. Note that these do not include
270matching for a single try with multiple excepts nor do they allow
271matching variable exception names using 'as' (because that's not
272currently supported by `mast`).
273"""
274
275WITH_PATTERNS = [
276    "with _:\n  ___",
277    "with _, _:\n  ___",
278    "with _ as _:\n  ___",
279    "with _ as _, _ as _:\n  ___",
280    "with _ as _, _:\n  ___",
281    "with _, _ as _:\n  ___",
282]
283"""
284Patterns for with blocks with up to two context handlers (TODO: let there
285be an arbitrary number of them...).
286"""
287
288
289#-----------------------------#
290# Pattern-producing functions #
291#-----------------------------#
292
293def function_def_patterns(fn_name, params_pattern=None):
294    """
295    Returns a list of mast pattern strings for definitions of functions
296    with the given name (or list of names). If an args pattern is given,
297    it should be a string containing mast code that goes between the
298    parentheses of a function definition. For example:
299
300    - `"_, _"`
301        There must be exactly two arguments.
302    - `"___, x=3"`
303        There may be any number of positional arguments, and there must
304        be a single keyword argument named 'x' with default value 3.
305    - `"one, two"`
306        There must be exactly two arguments and they must be named 'one'
307        and 'two'.
308
309    The params_pattern may also be a list of strings and any of those
310    alternatives will be accepted, or it may be an integer, in which case
311    that many blank slots will be inserted.
312    """
313    patterns = []
314
315    if isinstance(fn_name, str):
316        names = [fn_name]
317    else:
318        names = list(fn_name)
319
320    if isinstance(params_pattern, int):
321        params_pattern = ', '.join('_' for _ in range(params_pattern))
322
323    for name in names:
324        if params_pattern is None:
325            # kwargs not allowed by default
326            patterns.append("def {}(___):\n  ___".format(name))
327        else:
328            if isinstance(params_pattern, str):
329                patterns.append(
330                    "def {}({}):\n  ___".format(name, params_pattern)
331                )
332            elif not isinstance(params_pattern, (list, tuple)):
333                raise TypeError(
334                    "params_pattern must be either a string or a"
335                    " sequence of strings."
336                )
337            else:
338                patterns.extend(
339                    [
340                        "def {}({}):\n  ___".format(name, params_pat)
341                        for params_pat in params_pattern
342                    ]
343                )
344
345    return patterns
346
347
348def function_call_patterns(fn_name, args_pattern, is_method=False):
349    """
350    Works like function_def_patterns, but produces patterns for calls to
351    a function rather than definitions of it. In this context the args
352    spec is specifying arguments given to the function rather than what
353    parameters it defines.
354
355    If is_method is true, the patterns generated use '_.' to ensure that
356    the function call is as a method of some object.
357    """
358    patterns = []
359    if isinstance(fn_name, str):
360        names = [fn_name]
361    else:
362        names = list(fn_name)
363
364    if isinstance(args_pattern, int):
365        args_pattern = ', '.join('_' for _ in range(args_pattern))
366
367    for name in names:
368        if args_pattern is None:
369            if is_method:
370                patterns.extend(
371                    [
372                        "_.{}(___)".format(name),
373                        "_.{}(___,___=_)".format(name),
374                        "_.{}(___=_)".format(name),
375                    ]
376                )
377            else:
378                patterns.extend(
379                    [
380                        "{}(___)".format(name),
381                        "{}(___,___=_)".format(name),
382                        "{}(___=_)".format(name),
383                    ]
384                )
385        else:
386            if isinstance(args_pattern, str):
387                if is_method:
388                    patterns.append("_.{}({})".format(name, args_pattern))
389                else:
390                    patterns.append("{}({})".format(name, args_pattern))
391            elif not isinstance(args_pattern, (list, tuple)):
392                raise TypeError(
393                    "args_pattern must be either a string or a sequence of "
394                  + "strings."
395                )
396            else:
397                if is_method:
398                    patterns.extend(
399                        [
400                            "_.{}({})".format(name, args_pat)
401                            for args_pat in args_pattern
402                        ]
403                    )
404                else:
405                    patterns.extend(
406                        [
407                            "{}({})".format(name, args_pat)
408                            for args_pat in args_pattern
409                        ]
410                    )
411
412    return patterns
FOR_PATTERN = '\nfor _ in _:\n ___\n'

potluck.mast pattern for simple for loop.

FOR_ELSE_PATTERN = '\nfor _ in _:\n ___\nelse:\n ___\n'

potluck.mast pattern for a for loop with an else clause.

ALL_FOR_PATTERNS = ['\nfor _ in _:\n ___\n', '\nfor _ in _:\n ___\nelse:\n ___\n']

potluck.mast patterns for all for loop variants.

WHILE_PATTERN = '\nwhile _:\n ___\n'

A potluck.mast pattern for a simple while loop.

WHILE_ELSE_PATTERN = '\nwhile _:\n ___\nelse:\n ___\n'

A potluck.mast pattern for while/else.

ALL_WHILE_PATTERNS = ['\nwhile _:\n ___\n', '\nwhile _:\n ___\nelse:\n ___\n']

potluck.mast patterns for all while loop variants.

SIMPLE_COMPREHENSION_PATTERN = '\n[ _ for _ in _ ]\n'

A potluck.mast pattern for a single for loop comprehension without a filter.

FILTER_COMPREHENSION_PATTERN = '\n[ _ for _ in _ if _ ]\n'

A potluck.mast pattern for a single for loop comprehension with a filter.

DOUBLE_COMPREHENSION_PATTERN = '\n[ _ for _ in _ for _ in _ ]\n'

A potluck.mast pattern for a double for loop comprehension without a filter.

DOUBLE_FILTER_COMPREHENSION_PATTERN = '\n[ _ for _ in _ for _ in _ if _ ]\n'

A potluck.mast pattern for a double for loop comprehension with a filter.

TRIPLE_COMPREHENSION_PATTERN = '\n[ _ for _ in _ for _ in _ for _ in _ ]\n'

A potluck.mast pattern for a triple for loop comprehension without a filter.

TRIPLE_FILTER_COMPREHENSION_PATTERN = '\n[ _ for _ in _ for _ in _ for _ in _ if _ ]\n'

A potluck.mast pattern for a triple for loop comprehension with a filter.

SINGLE_LOOP_COMPREHENSION_PATTERNS = ['\n[ _ for _ in _ ]\n', '\n[ _ for _ in _ if _ ]\n']

`potluck.mast patterns for just single list comprehensions with and without filtering.

ALL_REASONABLE_COMPREHENSION_PATTERNS = ['\n[ _ for _ in _ ]\n', '\n[ _ for _ in _ if _ ]\n', '\n[ _ for _ in _ for _ in _ ]\n', '\n[ _ for _ in _ for _ in _ if _ ]\n', '\n[ _ for _ in _ for _ in _ for _ in _ ]\n', '\n[ _ for _ in _ for _ in _ for _ in _ if _ ]\n']

potluck.mast patterns for single, double, and triple list comprehensions with and without filtering.

Note: does not include dictionary comprehensions (see below)

We will simply hope that students do not use quadruple comprehensions...

ALL_SINGLE_LOOP_GENERATOR_EXPRESSION_PATTERNS = ['\n[ _ for _ in _ ]\n', '\n[ _ for _ in _ if _ ]\n', '\n( _ for _ in _ )\n', '\n( _ for _ in _ if _ )\n', '\n{ _ for _ in _ }\n', '\n{ _ for _ in _ if _ }\n', '\n{ _: _ for _ in _ }\n', '\n{ _: _ for _ in _ if _ }\n']

potluck.mast patterns for single-loop comprehensions including set and dictionary comprehensions and generator expressions.

ALL_GENERATOR_EXPRESSION_PATTERNS = ['\n[ _ for _ in _ ]\n', '\n[ _ for _ in _ if _ ]\n', '\n[ _ for _ in _ for _ in _ ]\n', '\n[ _ for _ in _ for _ in _ if _ ]\n', '\n[ _ for _ in _ for _ in _ for _ in _ ]\n', '\n[ _ for _ in _ for _ in _ for _ in _ if _ ]\n', '\n( _ for _ in _ )\n', '\n( _ for _ in _ if _ )\n', '\n( _ for _ in _ for _ in _ )\n', '\n( _ for _ in _ for _ in _ if _ )\n', '\n( _ for _ in _ for _ in _ for _ in _ )\n', '\n( _ for _ in _ for _ in _ for _ in _ if _ )\n', '\n{ _ for _ in _ }\n', '\n{ _ for _ in _ if _ }\n', '\n{ _ for _ in _ for _ in _ }\n', '\n{ _ for _ in _ for _ in _ if _ }\n', '\n{ _ for _ in _ for _ in _ for _ in _ }\n', '\n{ _ for _ in _ for _ in _ for _ in _ if _ }\n', '\n{ _: _ for _ in _ }\n', '\n{ _: _ for _ in _ if _ }\n', '\n{ _: _ for _ in _ for _ in _ }\n', '\n{ _: _ for _ in _ for _ in _ if _ }\n', '\n{ _: _ for _ in _ for _ in _ for _ in _ }\n', '\n{ _: _ for _ in _ for _ in _ for _ in _ if _ }\n']

Patterns for up to triple-loop list comprehensions, plus equivalent generator expressions and set- and dictionary-comprehensions.

ALL_FOR_AND_WHILE_LOOP_PATTERNS = ['\nfor _ in _:\n ___\n', '\nfor _ in _:\n ___\nelse:\n ___\n', '\nwhile _:\n ___\n', '\nwhile _:\n ___\nelse:\n ___\n']

potluck.mast patterns for all while/for loop variants.

ALL_LOOP_AND_COMPREHENSION_PATTERNS = ['\nfor _ in _:\n ___\n', '\nfor _ in _:\n ___\nelse:\n ___\n', '\nwhile _:\n ___\n', '\nwhile _:\n ___\nelse:\n ___\n', '\n[ _ for _ in _ ]\n', '\n[ _ for _ in _ if _ ]\n', '\n[ _ for _ in _ for _ in _ ]\n', '\n[ _ for _ in _ for _ in _ if _ ]\n', '\n[ _ for _ in _ for _ in _ for _ in _ ]\n', '\n[ _ for _ in _ for _ in _ for _ in _ if _ ]\n', '\n( _ for _ in _ )\n', '\n( _ for _ in _ if _ )\n', '\n( _ for _ in _ for _ in _ )\n', '\n( _ for _ in _ for _ in _ if _ )\n', '\n( _ for _ in _ for _ in _ for _ in _ )\n', '\n( _ for _ in _ for _ in _ for _ in _ if _ )\n', '\n{ _ for _ in _ }\n', '\n{ _ for _ in _ if _ }\n', '\n{ _ for _ in _ for _ in _ }\n', '\n{ _ for _ in _ for _ in _ if _ }\n', '\n{ _ for _ in _ for _ in _ for _ in _ }\n', '\n{ _ for _ in _ for _ in _ for _ in _ if _ }\n', '\n{ _: _ for _ in _ }\n', '\n{ _: _ for _ in _ if _ }\n', '\n{ _: _ for _ in _ for _ in _ }\n', '\n{ _: _ for _ in _ for _ in _ if _ }\n', '\n{ _: _ for _ in _ for _ in _ for _ in _ }\n', '\n{ _: _ for _ in _ for _ in _ for _ in _ if _ }\n']

potluck.mast patterns for all reasonable loop/comprehension types, although highly-nested comprehensions are not included.

ALL_SINGLE_LOOP_AND_COMPREHENSION_PATTERNS = ['\nfor _ in _:\n ___\n', '\nfor _ in _:\n ___\nelse:\n ___\n', '\nwhile _:\n ___\n', '\nwhile _:\n ___\nelse:\n ___\n', '\n[ _ for _ in _ ]\n', '\n[ _ for _ in _ if _ ]\n', '\n( _ for _ in _ )\n', '\n( _ for _ in _ if _ )\n', '\n{ _ for _ in _ }\n', '\n{ _ for _ in _ if _ }\n', '\n{ _: _ for _ in _ }\n', '\n{ _: _ for _ in _ if _ }\n']

potluck.mast patterns for all loop/comprehension patterns that are single loops, including generator expressions, set comprehensions, and dictionary comprehensions.

IF_PATTERN = '\nif _:\n ___\nelse:\n ___\n'

Common mast pattern for an if statement (matches even ifs that don't have an else, we think; note this also matches elifs, which Python treats as nested ifs).

ALL_DEF_PATTERNS = ['def _f_(___):\n ___', 'def _f_(___, ___=_):\n ___', 'def _f_(___=_):\n ___']

Patterns for function definitions (without keyword variables, with both, and with only keyword variables). These patterns bind 'f' as the name of the function that's defined.

FUNCTION_CALL_PATTERNS = ['_f_(___)', '_f_(___, ___=_)', '_f_(___=_)']

Patterns for function calls. These bind 'f' as the function (possibly a function expression).

METHOD_CALL_PATTERNS = ['_._f_(___)', '_._f_(___, ___=_)', '_._f_(___=_)']

Patterns for method calls specifically. Note that the FUNCTION_CALL_PATTERNS will find method calls too, but will bind 'f' as the entire object + method expression, whereas these patterns will bind 'f' as the method name (multiple bindings will result from chained method calls).

TRY_EXCEPT_PATTERNS = ['try:\n ___\nexcept:\n ___', 'try:\n ___\nexcept:\n ___\nfinally:\n ___', 'try:\n ___\nexcept _:\n ___', 'try:\n ___\nexcept _:\n ___\nfinally:\n ___', 'try:\n ___\nexcept _ as e:\n ___', 'try:\n ___\nexcept _ as e:\n ___\nfinally:\n ___', 'try:\n ___\nfinally:\n ___']

Patterns for try/except/finally blocks. Note that these do not include matching for a single try with multiple excepts nor do they allow matching variable exception names using 'as' (because that's not currently supported by mast).

WITH_PATTERNS = ['with _:\n ___', 'with _, _:\n ___', 'with _ as _:\n ___', 'with _ as _, _ as _:\n ___', 'with _ as _, _:\n ___', 'with _, _ as _:\n ___']

Patterns for with blocks with up to two context handlers (TODO: let there be an arbitrary number of them...).

def function_def_patterns(fn_name, params_pattern=None):
294def function_def_patterns(fn_name, params_pattern=None):
295    """
296    Returns a list of mast pattern strings for definitions of functions
297    with the given name (or list of names). If an args pattern is given,
298    it should be a string containing mast code that goes between the
299    parentheses of a function definition. For example:
300
301    - `"_, _"`
302        There must be exactly two arguments.
303    - `"___, x=3"`
304        There may be any number of positional arguments, and there must
305        be a single keyword argument named 'x' with default value 3.
306    - `"one, two"`
307        There must be exactly two arguments and they must be named 'one'
308        and 'two'.
309
310    The params_pattern may also be a list of strings and any of those
311    alternatives will be accepted, or it may be an integer, in which case
312    that many blank slots will be inserted.
313    """
314    patterns = []
315
316    if isinstance(fn_name, str):
317        names = [fn_name]
318    else:
319        names = list(fn_name)
320
321    if isinstance(params_pattern, int):
322        params_pattern = ', '.join('_' for _ in range(params_pattern))
323
324    for name in names:
325        if params_pattern is None:
326            # kwargs not allowed by default
327            patterns.append("def {}(___):\n  ___".format(name))
328        else:
329            if isinstance(params_pattern, str):
330                patterns.append(
331                    "def {}({}):\n  ___".format(name, params_pattern)
332                )
333            elif not isinstance(params_pattern, (list, tuple)):
334                raise TypeError(
335                    "params_pattern must be either a string or a"
336                    " sequence of strings."
337                )
338            else:
339                patterns.extend(
340                    [
341                        "def {}({}):\n  ___".format(name, params_pat)
342                        for params_pat in params_pattern
343                    ]
344                )
345
346    return patterns

Returns a list of mast pattern strings for definitions of functions with the given name (or list of names). If an args pattern is given, it should be a string containing mast code that goes between the parentheses of a function definition. For example:

  • "_, _" There must be exactly two arguments.
  • "___, x=3" There may be any number of positional arguments, and there must be a single keyword argument named 'x' with default value 3.
  • "one, two" There must be exactly two arguments and they must be named 'one' and 'two'.

The params_pattern may also be a list of strings and any of those alternatives will be accepted, or it may be an integer, in which case that many blank slots will be inserted.

def function_call_patterns(fn_name, args_pattern, is_method=False):
349def function_call_patterns(fn_name, args_pattern, is_method=False):
350    """
351    Works like function_def_patterns, but produces patterns for calls to
352    a function rather than definitions of it. In this context the args
353    spec is specifying arguments given to the function rather than what
354    parameters it defines.
355
356    If is_method is true, the patterns generated use '_.' to ensure that
357    the function call is as a method of some object.
358    """
359    patterns = []
360    if isinstance(fn_name, str):
361        names = [fn_name]
362    else:
363        names = list(fn_name)
364
365    if isinstance(args_pattern, int):
366        args_pattern = ', '.join('_' for _ in range(args_pattern))
367
368    for name in names:
369        if args_pattern is None:
370            if is_method:
371                patterns.extend(
372                    [
373                        "_.{}(___)".format(name),
374                        "_.{}(___,___=_)".format(name),
375                        "_.{}(___=_)".format(name),
376                    ]
377                )
378            else:
379                patterns.extend(
380                    [
381                        "{}(___)".format(name),
382                        "{}(___,___=_)".format(name),
383                        "{}(___=_)".format(name),
384                    ]
385                )
386        else:
387            if isinstance(args_pattern, str):
388                if is_method:
389                    patterns.append("_.{}({})".format(name, args_pattern))
390                else:
391                    patterns.append("{}({})".format(name, args_pattern))
392            elif not isinstance(args_pattern, (list, tuple)):
393                raise TypeError(
394                    "args_pattern must be either a string or a sequence of "
395                  + "strings."
396                )
397            else:
398                if is_method:
399                    patterns.extend(
400                        [
401                            "_.{}({})".format(name, args_pat)
402                            for args_pat in args_pattern
403                        ]
404                    )
405                else:
406                    patterns.extend(
407                        [
408                            "{}({})".format(name, args_pat)
409                            for args_pat in args_pattern
410                        ]
411                    )
412
413    return patterns

Works like function_def_patterns, but produces patterns for calls to a function rather than definitions of it. In this context the args spec is specifying arguments given to the function rather than what parameters it defines.

If is_method is true, the patterns generated use '_.' to ensure that the function call is as a method of some object.