├── LICENSE ├── README.md ├── examples ├── py20-0029-list-comprehensions.py ├── py24-0027-generator-expr.py ├── py24-0027-generator-expr2.py ├── py26-0025-print-function1.py ├── py26-0025-print-function2.py ├── py26-0025-print-function3.py ├── py26-0025-print-function4.py ├── py26-0025-print-function5.py ├── py26-0025-print-function6.py ├── py27-0024-as-keyword.py ├── py27-0026-set-literals.py ├── py27-0026-set-literals2.py ├── py30-0004-unpacking-args1.py ├── py30-0013-kwargs-after-star.py ├── py30-0014-raise-from1.py ├── py30-0014-raise-from2.py ├── py30-0015-dict-comprehensions.py ├── py30-0017-nonlocal.py ├── py30-0018-except-as.py ├── py30-0018-except-comma.py ├── py30-0018-except-comma2.py ├── py30-0019-kwonly-args.py ├── py30-0020-exec.py ├── py30-0020-exec2.py ├── py30-0021-set-literals.py ├── py30-0022-backticks.py ├── py30-0022-backticks2.py ├── py30-0022-backticks3.py ├── py30-0022-backticks4.py ├── py30-0022-backticks5.py ├── py30-0022-backticks6.py ├── py30-0022-backticks7.py ├── py30-0022-backticks8.py ├── py30-0022-backticks9.py ├── py30-0023-relative-imports1.py ├── py30-0023-relative-imports2.py ├── py30-0027-type-hints1.py ├── py30-0027-type-hints2.py ├── py30-0027-type-hints3.py ├── py30-0027-type-hints4.py ├── py30-0030-int-literals1.py ├── py30-0030-int-literals2.py ├── py30-0030-int-literals3.py ├── py30-0030-int-literals4.py ├── py30-0030-int-literals5.py ├── py31-0002-with1.py ├── py31-0002-with2.py ├── py31-0002-with3.py ├── py31-0002-with4.py ├── py311-0035-variadic-generics.py ├── py311-0036-except-star.py ├── py312-0037-protocol-generic.py ├── py33-0011-yield-from.py ├── py33-0012-uprefix1.py ├── py35-0008-else-in-call1.py ├── py35-0008-else-in-call2.py ├── py35-0009-async-def.py ├── py35-0010-dict-literals.py ├── py36-0005-types1.py ├── py36-0006-fstrings.py ├── py36-0007-trailing-comma-def.py ├── py36-0028-variable-annotations-pep526.py ├── py36-0031-fstring-yield.py ├── py36-0032-fstring-genexpr.py ├── py36-0033-fstring-star.py ├── py38-0003-walrus1.py ├── py38-0003-walrus2.py ├── py38-0003-walrus3.py ├── py38-0003-walrus4.py └── py38-0034-posonly.py ├── libcst_parse.py ├── metarun.py └── run.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Tim Hatch 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Here's a high-level log of grammar changes by version, with the most effort 2 | sppent analyzing 3.6 and before. (The release listed is from tags on master, 3 | and probably doesn't take into account backports where the commit id is 4 | different.) 5 | 6 | | Release | Commit | Feature | With `__future__` | 7 | |---------|----------|---------|-------------------| 8 | | v3.11.0 | d60457a6 | PEP 654 `except*` groups | 9 | | v3.11.0 | e8e737bc | PEP 646 variadic generics | 10 | | v3.10.0 | 145bf269 | PEP 622/634 `match` statements | 11 | | v3.9.0 | 0daba822 | parenthesized context manager change | 12 | | v3.8.0 | b5e3ea28 + a965db37 | PEP 570 positional-only params | 13 | | v3.8.0 | 8f59ee01 | PEP 572 `:=` | 14 | | v3.7.0 | - | - | `annotations` (on in 4.0) | 15 | | v3.7.0 | ac317700 | PEP 492 `async`/`await` become keywords| 16 | | v3.6.0 | a721abac | PEP 515 underscores in numeric literals | 17 | | v3.6.0 | 52c4e7cc | PEP 530 `async for` comprehensions | 18 | | v3.6.0 | f8cb8a16 (2016) | PEP 526 `foo: int` annotations | 19 | | v3.6.0\* | 235a6f09 | PEP 498 f-strings | 20 | | v3.6.0\* | df395991 | `def f(a,)` trailing commas | 21 | | v3.5.0 | - | - | `generator_stop` (on in 3.7) | 22 | | v3.5.0 | 14acf5f4 | `f(*a if b else c)` fix | 23 | | v3.5.0 | de12b79c | `f(**a if b else c)` fix | 24 | | v3.5.0 | 7544508f | PEP 492 `async def`, `for`, `with` | 25 | | v3.5.0 | 025e9ebd | PEP 448 `{**x}` ?? | 26 | | v3.5.0 | d51374ed (2014) | PEP 465 `a @ b` and `a @= b` matrix multiply | 27 | | v3.3.0\* | 1f7ce62b | PEP 380 `yield from` | 28 | | v3.3.0\* | 6ecf77b3 | PEP 414 `u`-prefix strings are back | 29 | | 3.1\* | 1c50d117 (2010)| refactor argslist, a no-op? | 30 | | 3.1\* | 4905e80c | Fix ambiguity in unpacking | 31 | | 3.1\* | 0c31562a | `with` can have multiple | 32 | | 3.1\* | e3944a5e | PEP 401 `<>` operator | `barry_as_FLUFL` | 33 | | v3.0\* | 2d735bc0 | Allow keywords after `*args` | 34 | | v3.0\* | 828f04ac | 3-arg `raise` becomes `raise ... from` | 35 | | v3.0\* | 992d4a3e | PEP 274 `{k: v for ... }` dict comprehensions | 36 | | v3.0\* | e7ba4956 | True, False, None are now keywords | 37 | | v3.0\* | d59da4b4 | class decorators | 38 | | v3.0\* | 1bc535dc | No tuple unpacking in args | 39 | | v3.0\* | 0368b726 | extended iterable unpacking | 40 | | v3.0\* | 650f0d06 | lambda/for ambiguity removal | 41 | | v3.0\* | e66c8c7c | bugfix `from ... import` (with 3 `.`) | 42 | | v3.0\* | dde00289 (2007) | `...` is a token | 43 | | v3.0\* | 52cc1d83 | Metaclass changeover | 44 | | v3.0\* | 81e9502d | `nonlocal x` | 45 | | v3.0\* | 452bf519 | `print` statement removal | 46 | | v3.0\* | b940e113 + 16be03e4 | `except ... as x` | 47 | | v3.0\* | c150536b | PEP 3107 py3 type hints | 48 | | v3.0\* | 4f72a786 (2006) | PEP 3102 kwonly args | 49 | | v3.0\* | 52318d62 | `...` is a token | 50 | | v3.0\* | 7cae87ca | `exec` statement removal | 51 | | v3.0\* | 86e58e23 | set literals | 52 | | v3.0\* | cf588f64 | backticks removal | 53 | | v3.0\* | b053cd8f | `<>` operator removal | 54 | | v3.0\* | 477c8d5e | `from ..` for parent relative imports | 55 | | v3.0\* | 49fd7fa4 (2006) | change to if-conditions I don't understand | 56 | | v2.7\* | 45aecf45 | `as` and `with` become keywords | 57 | | v2.6 | - | - | `print_function` (on in 3) | 58 | | v2.6 | - | - | `unicode_literals` (on in 3) | 59 | | v2.5 | 8ae1295c | `as` and `with` are optionally keywords | 60 | | v2.5 | f7f438ba | PEP 328 relative/absolute imports | `absolute_import` (on in 3) | 61 | | v2.5 | c2e20744 | `with` statements | `with_statement` (on in 2.6) | 62 | | v2.5 | dca3b9c7 + d074beb6 | lambda/for ambiguity | 63 | | v2.5 | f599f424 | PEP 341 try/except and try/finally unification | 64 | | v2.5 | 37c0844b (2005) | require parens on kwarg generators | 65 | | v2.5 | 0d6615fd | PEP 342 adds yield expr | 66 | | v2.5 | 409d8f2e | `class F()` ok now | 67 | | v2.4 | 1a4ddaec | multi-line import tuple | 68 | | v2.4 | 0ccff074 | Require newlines between decorators | 69 | | v2.4 | c2a5a636 | PEP 318 (decorators) | 70 | | v2.4 | 354433a5 | PEP 289 (generator expressions) | 71 | | v2.3 | 00f1e3f5 | PEP 263 (source encoding) | 72 | | v2.3 | 2d3b9864 | change to backtick that ends with comma | 73 | | v2.3 | 84ee323c (2002) | disambiguate power-of-power | 74 | | v2.2 | 1c917072 (2001) | ban a list comprehension trailing comma | 75 | | v2.2 | 4668b000 | `//` for floordiv | `division` (on in 3) | 76 | | v2.2 | 5ca576ed | `yield` statement | `generators` (on in 2.3) | 77 | | v2.1 | - | - | `nested_scopes` (on in 2.2) | 78 | | v2.0 | 434d0828 | `**=` and friends | 79 | | v2.0 | 46dfa5f4 | `[1 if x]` is not a list comprehension | 80 | | v2.0 | 0360663e (2000) | `print >>file, x` and trailing comma | 81 | | v2.0 | 803d6e54 | list comprehensions | 82 | | v2.0 | 7690151c | "extended call syntax" `f(*args, **kwargs)` | 83 | | v2.0 | d295f120 | no-args raise | 84 | | v2.0 | 03a7466b + 556440d2 | assert statement | 85 | | v2.0 | 0dfcf753 | access statement removal | 86 | | v2.0 | 14f44516 | slice syntax changes | 87 | | v2.0 | 0bfd6c33 | add power operator | 88 | | v2.0 | a996b910 | 3-arg raise, and more | 89 | | v2.0 | 4a1da268 | `import a.b` | 90 | | v2.0 | 248a50c1 | implicit concatenation, try/except/else, default values in args | 91 | | v2.0 | 57531fea + 590baa4a + 12d12c5f | lambdas | 92 | | v2.0 | db3165e6 | add exec statement | 93 | | v2.0 | b3f7258f + 25831652 | access statement addition, something about class header | 94 | | v2.0 | 02334d2b | varargs uses `*` | 95 | | v2.0 | af82141b | looks like try/except/finally becomes try/except and try/finally | 96 | | v2.0 | e785fbcf | allow newline at end of eval | 97 | | v2.0 | 610cdc52 | varargs uses `*` or `+` | 98 | | v2.0 | 526e9096 | varargs uses `+` | 99 | | v2.0 | 09cea474 + 6cf1273f | evidently `=` used to work for comparison | 100 | | v2.0 | 68fc3497 | global statement, "new" class syntax with bases in parens | 101 | | v2.0 | 9eb4f535 + a76fb5b6 | lots of operators | 102 | | v2.0 | 56f78377 | semicolons, continue, dict literals, and more | 103 | | v2.0 | 4dae2167 | remove dir statement? | 104 | | v2.0 | 85a5fbbd | initial commit in current location | 105 | 106 | \* These commits are present in old tags according to git, but the released 107 | binaries do not appear to recognize them. 108 | 109 | # Code samples 110 | 111 | These are found in the `examples/` directory listed by the version they are 112 | "first" added. Some are backported. This could be cleaned up, and entries 113 | removed from this README if someone is interested and has time. 114 | 115 | If you get python 2.3 through 3.8 installed on your `PATH`, you can check an 116 | installed LibCST against the changes that result in syntax errors. 117 | 118 | ``` 119 | $ python metarun.py 120 | 23 24 25 26 27 30 31 32 33 34 35 36 37 38 121 | examples/py24-0027-generator-expr.py X o o o o o o o o o o o o o 122 | examples/py26-0025-print-function1.py O O o O O . . . . . . . . . 123 | examples/py26-0025-print-function2.py X X . X X o o o o o o o o o 124 | examples/py26-0025-print-function3.py X X . o o o o o o o o o o o 125 | ... 126 | ``` 127 | -------------------------------------------------------------------------------- /examples/py20-0029-list-comprehensions.py: -------------------------------------------------------------------------------- 1 | [1 for i in range(3)] 2 | -------------------------------------------------------------------------------- /examples/py24-0027-generator-expr.py: -------------------------------------------------------------------------------- 1 | print(sum(1 for i in range(10))) 2 | -------------------------------------------------------------------------------- /examples/py24-0027-generator-expr2.py: -------------------------------------------------------------------------------- 1 | (1 for i in range(10)) 2 | -------------------------------------------------------------------------------- /examples/py26-0025-print-function1.py: -------------------------------------------------------------------------------- 1 | import sys 2 | print "foo" 3 | print >>sys.stderr, "foo", 4 | -------------------------------------------------------------------------------- /examples/py26-0025-print-function2.py: -------------------------------------------------------------------------------- 1 | import sys 2 | print("foo", file=sys.stderr) 3 | -------------------------------------------------------------------------------- /examples/py26-0025-print-function3.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import sys 3 | print("foo", file=sys.stderr) 4 | -------------------------------------------------------------------------------- /examples/py26-0025-print-function4.py: -------------------------------------------------------------------------------- 1 | print = 2 2 | -------------------------------------------------------------------------------- /examples/py26-0025-print-function5.py: -------------------------------------------------------------------------------- 1 | print >>sys.stderr, a 2 | -------------------------------------------------------------------------------- /examples/py26-0025-print-function6.py: -------------------------------------------------------------------------------- 1 | print a, 2 | -------------------------------------------------------------------------------- /examples/py27-0024-as-keyword.py: -------------------------------------------------------------------------------- 1 | as = 1 2 | -------------------------------------------------------------------------------- /examples/py27-0026-set-literals.py: -------------------------------------------------------------------------------- 1 | {1,2,3} 2 | -------------------------------------------------------------------------------- /examples/py27-0026-set-literals2.py: -------------------------------------------------------------------------------- 1 | {i for i in range(3)} 2 | 3 | -------------------------------------------------------------------------------- /examples/py30-0004-unpacking-args1.py: -------------------------------------------------------------------------------- 1 | def f((x, y)): pass 2 | -------------------------------------------------------------------------------- /examples/py30-0013-kwargs-after-star.py: -------------------------------------------------------------------------------- 1 | f(*args, x=1) 2 | -------------------------------------------------------------------------------- /examples/py30-0014-raise-from1.py: -------------------------------------------------------------------------------- 1 | raise a from b 2 | -------------------------------------------------------------------------------- /examples/py30-0014-raise-from2.py: -------------------------------------------------------------------------------- 1 | raise a, None, None 2 | -------------------------------------------------------------------------------- /examples/py30-0015-dict-comprehensions.py: -------------------------------------------------------------------------------- 1 | {i: i for i in range(5)} 2 | -------------------------------------------------------------------------------- /examples/py30-0017-nonlocal.py: -------------------------------------------------------------------------------- 1 | def f(): 2 | nonlocal x 3 | -------------------------------------------------------------------------------- /examples/py30-0018-except-as.py: -------------------------------------------------------------------------------- 1 | try: 2 | pass 3 | except Exception as e: 4 | pass 5 | -------------------------------------------------------------------------------- /examples/py30-0018-except-comma.py: -------------------------------------------------------------------------------- 1 | try: 2 | pass 3 | except Exception, e: 4 | pass 5 | -------------------------------------------------------------------------------- /examples/py30-0018-except-comma2.py: -------------------------------------------------------------------------------- 1 | try: 2 | pass 3 | except Exception, (a, b): 4 | pass 5 | -------------------------------------------------------------------------------- /examples/py30-0019-kwonly-args.py: -------------------------------------------------------------------------------- 1 | def compare(a, b, *, key=None): pass 2 | -------------------------------------------------------------------------------- /examples/py30-0020-exec.py: -------------------------------------------------------------------------------- 1 | exec "1+1" 2 | -------------------------------------------------------------------------------- /examples/py30-0020-exec2.py: -------------------------------------------------------------------------------- 1 | exec "1+1" in globals(), locals() 2 | -------------------------------------------------------------------------------- /examples/py30-0021-set-literals.py: -------------------------------------------------------------------------------- 1 | {1, 2, 3} 2 | -------------------------------------------------------------------------------- /examples/py30-0022-backticks.py: -------------------------------------------------------------------------------- 1 | x=`1+1` 2 | -------------------------------------------------------------------------------- /examples/py30-0022-backticks2.py: -------------------------------------------------------------------------------- 1 | `` 2 | -------------------------------------------------------------------------------- /examples/py30-0022-backticks3.py: -------------------------------------------------------------------------------- 1 | `[1 for i in range(5)]` 2 | -------------------------------------------------------------------------------- /examples/py30-0022-backticks4.py: -------------------------------------------------------------------------------- 1 | `1 for i in range(4)` 2 | -------------------------------------------------------------------------------- /examples/py30-0022-backticks5.py: -------------------------------------------------------------------------------- 1 | `(1 for i in range(4))` 2 | -------------------------------------------------------------------------------- /examples/py30-0022-backticks6.py: -------------------------------------------------------------------------------- 1 | `1,2,3` 2 | -------------------------------------------------------------------------------- /examples/py30-0022-backticks7.py: -------------------------------------------------------------------------------- 1 | `{1,2,3}` 2 | -------------------------------------------------------------------------------- /examples/py30-0022-backticks8.py: -------------------------------------------------------------------------------- 1 | `1, 2, 3` 2 | -------------------------------------------------------------------------------- /examples/py30-0022-backticks9.py: -------------------------------------------------------------------------------- 1 | `1, 2, 3,` 2 | -------------------------------------------------------------------------------- /examples/py30-0023-relative-imports1.py: -------------------------------------------------------------------------------- 1 | from .. import foo 2 | -------------------------------------------------------------------------------- /examples/py30-0023-relative-imports2.py: -------------------------------------------------------------------------------- 1 | from ... import x 2 | -------------------------------------------------------------------------------- /examples/py30-0027-type-hints1.py: -------------------------------------------------------------------------------- 1 | def f(x: str): pass 2 | -------------------------------------------------------------------------------- /examples/py30-0027-type-hints2.py: -------------------------------------------------------------------------------- 1 | def f() -> str: pass 2 | -------------------------------------------------------------------------------- /examples/py30-0027-type-hints3.py: -------------------------------------------------------------------------------- 1 | def f() -> "str": pass 2 | -------------------------------------------------------------------------------- /examples/py30-0027-type-hints4.py: -------------------------------------------------------------------------------- 1 | def f(x, *y): pass 2 | -------------------------------------------------------------------------------- /examples/py30-0030-int-literals1.py: -------------------------------------------------------------------------------- 1 | 2L 2 | -------------------------------------------------------------------------------- /examples/py30-0030-int-literals2.py: -------------------------------------------------------------------------------- 1 | 0177 2 | -------------------------------------------------------------------------------- /examples/py30-0030-int-literals3.py: -------------------------------------------------------------------------------- 1 | 0177L 2 | -------------------------------------------------------------------------------- /examples/py30-0030-int-literals4.py: -------------------------------------------------------------------------------- 1 | 0o177 2 | -------------------------------------------------------------------------------- /examples/py30-0030-int-literals5.py: -------------------------------------------------------------------------------- 1 | 1_2 2 | -------------------------------------------------------------------------------- /examples/py31-0002-with1.py: -------------------------------------------------------------------------------- 1 | with a: pass 2 | -------------------------------------------------------------------------------- /examples/py31-0002-with2.py: -------------------------------------------------------------------------------- 1 | with a as a, b as b: pass 2 | -------------------------------------------------------------------------------- /examples/py31-0002-with3.py: -------------------------------------------------------------------------------- 1 | with a, b: pass 2 | -------------------------------------------------------------------------------- /examples/py31-0002-with4.py: -------------------------------------------------------------------------------- 1 | from __future__ import with_statement 2 | 3 | with a: 4 | pass 5 | -------------------------------------------------------------------------------- /examples/py311-0035-variadic-generics.py: -------------------------------------------------------------------------------- 1 | from typing import TypeVar, TypeVarTuple 2 | 3 | DType = TypeVar('DType') 4 | Shape = TypeVarTuple('Shape') 5 | 6 | class Array(Generic[DType, *Shape]): 7 | 8 | def __abs__(self) -> Array[DType, *Shape]: ... 9 | 10 | def __add__(self, other: Array[DType, *Shape]) -> Array[DType, *Shape]: ... 11 | -------------------------------------------------------------------------------- /examples/py311-0036-except-star.py: -------------------------------------------------------------------------------- 1 | try: 2 | ... 3 | except* g: 4 | pass 5 | -------------------------------------------------------------------------------- /examples/py312-0037-protocol-generic.py: -------------------------------------------------------------------------------- 1 | class GenProto[T](Protocol): 2 | def meth(self) -> T: 3 | ... 4 | -------------------------------------------------------------------------------- /examples/py33-0011-yield-from.py: -------------------------------------------------------------------------------- 1 | def f(x): 2 | yield from x 3 | -------------------------------------------------------------------------------- /examples/py33-0012-uprefix1.py: -------------------------------------------------------------------------------- 1 | u"foo" 2 | -------------------------------------------------------------------------------- /examples/py35-0008-else-in-call1.py: -------------------------------------------------------------------------------- 1 | f(*a if b else c) 2 | -------------------------------------------------------------------------------- /examples/py35-0008-else-in-call2.py: -------------------------------------------------------------------------------- 1 | f(**a if b else c) 2 | -------------------------------------------------------------------------------- /examples/py35-0009-async-def.py: -------------------------------------------------------------------------------- 1 | async def foo(): pass 2 | -------------------------------------------------------------------------------- /examples/py35-0010-dict-literals.py: -------------------------------------------------------------------------------- 1 | {**x} 2 | -------------------------------------------------------------------------------- /examples/py36-0005-types1.py: -------------------------------------------------------------------------------- 1 | x: int 2 | -------------------------------------------------------------------------------- /examples/py36-0006-fstrings.py: -------------------------------------------------------------------------------- 1 | f"{1+1}" 2 | -------------------------------------------------------------------------------- /examples/py36-0007-trailing-comma-def.py: -------------------------------------------------------------------------------- 1 | def f(*, a = 3,): pass 2 | -------------------------------------------------------------------------------- /examples/py36-0028-variable-annotations-pep526.py: -------------------------------------------------------------------------------- 1 | x: int 2 | -------------------------------------------------------------------------------- /examples/py36-0031-fstring-yield.py: -------------------------------------------------------------------------------- 1 | f"{yield 1}" 2 | -------------------------------------------------------------------------------- /examples/py36-0032-fstring-genexpr.py: -------------------------------------------------------------------------------- 1 | f"{a for a in b}" 2 | -------------------------------------------------------------------------------- /examples/py36-0033-fstring-star.py: -------------------------------------------------------------------------------- 1 | f"{*x,}" 2 | -------------------------------------------------------------------------------- /examples/py38-0003-walrus1.py: -------------------------------------------------------------------------------- 1 | if a := 1: pass 2 | -------------------------------------------------------------------------------- /examples/py38-0003-walrus2.py: -------------------------------------------------------------------------------- 1 | if (a := 1): pass 2 | -------------------------------------------------------------------------------- /examples/py38-0003-walrus3.py: -------------------------------------------------------------------------------- 1 | y := f(x) # from pep 572, invalid 2 | -------------------------------------------------------------------------------- /examples/py38-0003-walrus4.py: -------------------------------------------------------------------------------- 1 | (y := f(x)) # from pep 572, technically valid 2 | -------------------------------------------------------------------------------- /examples/py38-0034-posonly.py: -------------------------------------------------------------------------------- 1 | def pow(x, y, z=None, /): 2 | pass 3 | -------------------------------------------------------------------------------- /libcst_parse.py: -------------------------------------------------------------------------------- 1 | # Usage: libcst_parse.py 2.7 somefile.py 2 | 3 | import sys 4 | import libcst as cst 5 | 6 | 7 | def main(version, filename): 8 | with open(filename) as fo: 9 | data = fo.read() 10 | mod = cst.parse_module(data, cst.PartialParserConfig(python_version=version)) 11 | print(mod) 12 | 13 | 14 | if __name__ == "__main__": 15 | main(*sys.argv[1:]) 16 | -------------------------------------------------------------------------------- /metarun.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import subprocess 3 | 4 | import libcst as cst 5 | 6 | VERSIONS = [ 7 | # "python2.3", 8 | # "python2.4", 9 | # "python2.5", 10 | # "python2.6", 11 | # "python2.7", 12 | # "python3.0", 13 | # "python3.1", 14 | # "python3.2", 15 | # "python3.3", 16 | # "python3.4", 17 | # "python3.5", 18 | "python3.6", 19 | "python3.7", 20 | "python3.8", 21 | "python3.9", 22 | "python3.10", 23 | "python3.11", 24 | "python3.12", 25 | "python3.13", 26 | ] 27 | 28 | LIBCST_VERSIONS = [ 29 | # "2.3", 30 | # "2.4", 31 | # "2.6", 32 | # "2.7", 33 | # "3.0", 34 | # "3.1", 35 | # "3.3", 36 | # "3.5", 37 | "3.6", 38 | "3.7", 39 | "3.8", 40 | # "3.9", 41 | # "3.10", 42 | # "3.11", 43 | # "3.12", 44 | # "3.13", 45 | ] 46 | 47 | 48 | def main(filters=()): 49 | # dict[filename][version] 50 | results = {} 51 | for v in VERSIONS: 52 | # TODO as there get to be many more of these, should run them in 53 | # parallel, or at least streaming. 54 | output = subprocess.check_output([v, "run.py"], encoding="utf-8") 55 | for line in output.splitlines(): 56 | filename, result = line.split() 57 | results.setdefault(filename, {})[v] = result == "YES" 58 | 59 | # Now print a pretty table. TODO should write json or something consumable 60 | # as well. 61 | max_filename = max(len(f) for f in results) 62 | 63 | # TODO mark versions that will be libcst-checked too 64 | buf = [" " * max_filename] 65 | for v in VERSIONS: 66 | short_version = v[-3:] 67 | if short_version in LIBCST_VERSIONS: 68 | buf.append(f" \x1b[32m{short_version.replace('.', '')}\x1b[0m") 69 | else: 70 | buf.append(f" {short_version.replace('.', '')}") 71 | print("".join(buf)) 72 | 73 | for f in sorted(results): 74 | if filters and not any(filt in f for filt in filters): 75 | continue 76 | sys.stdout.write(f.ljust(max_filename + 1)) 77 | for v in VERSIONS: 78 | libcst_result = None 79 | if v[-3:] in LIBCST_VERSIONS: 80 | try: 81 | with open(f) as fo: 82 | data = fo.read() 83 | cst.parse_module( 84 | data, cst.PartialParserConfig(python_version=v[-3:]) 85 | ) 86 | libcst_result = True 87 | except cst.ParserSyntaxError: 88 | libcst_result = False 89 | 90 | if libcst_result != results[f][v]: 91 | sys.stdout.write( 92 | "\x1b[31m" 93 | f"{'o' if results[f][v] else '.'}{'o' if libcst_result else '.'} " 94 | "\x1b[0m" 95 | ) 96 | else: 97 | sys.stdout.write( 98 | f"{'o' if results[f][v] else '.'}{'o' if libcst_result else '.'} " 99 | ) 100 | else: 101 | sys.stdout.write( 102 | f"{'o' if results[f][v] else '.'} " 103 | ) 104 | sys.stdout.write("\n") 105 | 106 | print("Legend:") 107 | print(" green header means will test with libcst") 108 | print() 109 | print(" first result is python, second [optional] result is libcst") 110 | print(" o parses") 111 | print(" . does not parse") 112 | 113 | 114 | if __name__ == "__main__": 115 | main(sys.argv[1:]) 116 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | try: 2 | import ast 3 | except ImportError: 4 | ast = None 5 | import glob 6 | import sys 7 | 8 | try: 9 | sorted 10 | except NameError: 11 | 12 | def sorted(t): 13 | t2 = list(t) 14 | t2.sort() 15 | return t2 16 | 17 | 18 | def main(): 19 | for f in sorted(glob.glob("examples/*.py")): 20 | fo = open(f) 21 | data = fo.read() 22 | fo.close() 23 | 24 | try: 25 | if ast: 26 | ast.parse(data) 27 | else: 28 | # Note that __future__ imports here leak into this compile, so 29 | # we can't use any. 30 | compile(data, "", "exec") 31 | sys.stdout.write(f + " YES\n") 32 | except SyntaxError: 33 | sys.stdout.write(f + " NO\n") 34 | 35 | 36 | if __name__ == "__main__": 37 | main() 38 | --------------------------------------------------------------------------------