Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-gast for openSUSE:Factory checked in at 2024-08-01 22:03:14 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-gast (Old) and /work/SRC/openSUSE:Factory/.python-gast.new.7232 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-gast" Thu Aug 1 22:03:14 2024 rev:12 rq:1189024 version:0.6.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-gast/python-gast.changes 2024-07-02 18:14:42.423593071 +0200 +++ /work/SRC/openSUSE:Factory/.python-gast.new.7232/python-gast.changes 2024-08-01 22:03:15.213556705 +0200 @@ -1,0 +2,17 @@ +Mon Jul 22 09:49:36 UTC 2024 - John Paul Adrian Glaubitz <[email protected]> + +- Update to 0.6.0 + * Harmonize gast.dump behavior across versions, following py3.13 behavior +- from version 0.5.5 + * Provide a bug and security bug section in the README + * Upgrade to py3.13 support + * Slightly speedup gast node creation + * Document ExceptHandler.name change + * Optimize node constructors + * Update unparser for py 3.12 and setup minimal testing + * Reflect AST changes to README + * Support py 3.12 type parameters + * Update CI configuration for python 3.12 (and 2.7!) + * Support Type aliases from Python 3.12 + +------------------------------------------------------------------- Old: ---- gast-0.5.4.tar.gz New: ---- gast-0.6.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-gast.spec ++++++ --- /var/tmp/diff_new_pack.K4tSDV/_old 2024-08-01 22:03:15.909585415 +0200 +++ /var/tmp/diff_new_pack.K4tSDV/_new 2024-08-01 22:03:15.909585415 +0200 @@ -20,7 +20,7 @@ %define srcname gast Name: python-gast # only update when python-pythran is compatible -Version: 0.5.4 +Version: 0.6.0 Release: 0 Summary: Python AST that abstracts the underlying Python version License: BSD-3-Clause ++++++ gast-0.5.4.tar.gz -> gast-0.6.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gast-0.5.4/.github/workflows/core.yml new/gast-0.6.0/.github/workflows/core.yml --- old/gast-0.5.4/.github/workflows/core.yml 2023-04-29 21:35:43.000000000 +0200 +++ new/gast-0.6.0/.github/workflows/core.yml 2024-06-27 22:30:35.000000000 +0200 @@ -11,7 +11,7 @@ build: strategy: matrix: - python-version: ["2.7", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12-dev"] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13.0-beta.1"] include: - python-version: 3.6 os: ubuntu-20.04 @@ -26,6 +26,7 @@ - name: Install dependencies run: | python -m pip install --upgrade pip + pip install setuptools pip install pytest - name: Setup run: | @@ -33,3 +34,24 @@ - name: Testing sequential run: | pytest + + + build-27: + runs-on: ubuntu-20.04 + container: + image: python:2.7.18-buster + env: + py27: 2.7 + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pytest + - name: Setup + run: | + python setup.py install + - name: Testing sequential + run: | + pytest diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gast-0.5.4/README.rst new/gast-0.6.0/README.rst --- old/gast-0.5.4/README.rst 2023-04-29 21:35:43.000000000 +0200 +++ new/gast-0.6.0/README.rst 2024-06-27 22:30:35.000000000 +0200 @@ -64,6 +64,9 @@ notable exception of ``ast.arg`` being replaced by an ``ast.Name`` with an ``ast.Param`` context. +The ``name`` field of ``ExceptHandler`` is represented as an ``ast.Name`` with +an ``ast.Store`` context and not a ``str``. + For minor version before 3.9, please note that ``ExtSlice`` and ``Index`` are not used. @@ -138,20 +141,22 @@ stmt = FunctionDef(identifier name, arguments args, stmt* body, expr* decorator_list, expr? returns, - string? type_comment) + string? type_comment, type_param* type_params) | AsyncFunctionDef(identifier name, arguments args, stmt* body, expr* decorator_list, expr? returns, - string? type_comment) + string? type_comment, type_param* type_params) | ClassDef(identifier name, expr* bases, keyword* keywords, stmt* body, - expr* decorator_list) + expr* decorator_list, + type_param* type_params) | Return(expr? value) | Delete(expr* targets) | Assign(expr* targets, expr value, string? type_comment) + | TypeAlias(expr name, type_param* type_params, expr value) | AugAssign(expr target, operator op, expr value) -- 'simple' indicates that we annotate simple name without parens | AnnAssign(expr target, expr annotation, expr? value, int simple) @@ -278,4 +283,22 @@ attributes (int lineno, int col_offset, int end_lineno, int end_col_offset) type_ignore = TypeIgnore(int lineno, string tag) + + type_param = TypeVar(identifier name, expr? bound) + | ParamSpec(identifier name) + | TypeVarTuple(identifier name) + attributes (int lineno, int col_offset, int end_lineno, int end_col_offset) } + + +Reporting Bugs +-------------- + +Bugs can be reported through `GitHub issues <https://github.com/serge-sans-paille/gast/issues>`_. + +Reporting Security Issues +------------------------- + +If for some reason, you think your bug is security-related and should be subject +to responsible disclosure, don't hesitate to `contact the maintainer +<mailto:[email protected]>`_ directly. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gast-0.5.4/SECURITY.rst new/gast-0.6.0/SECURITY.rst --- old/gast-0.5.4/SECURITY.rst 1970-01-01 01:00:00.000000000 +0100 +++ new/gast-0.6.0/SECURITY.rst 2024-06-27 22:30:35.000000000 +0200 @@ -0,0 +1,7 @@ +Reporting Security Issues +------------------------- + +If for some reason, you think your bug is security-related and should be subject +to responsible disclosure, don't hesitate to `contact the maintainer +<mailto:[email protected]>`_ directly. + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gast-0.5.4/gast/__init__.py new/gast-0.6.0/gast/__init__.py --- old/gast-0.5.4/gast/__init__.py 2023-04-29 21:35:43.000000000 +0200 +++ new/gast-0.6.0/gast/__init__.py 2024-06-27 22:30:35.000000000 +0200 @@ -1,3 +1,3 @@ from .gast import * from .version import __version__ -from ast import NodeVisitor, NodeTransformer, iter_fields, dump +from ast import NodeVisitor, NodeTransformer, iter_fields diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gast-0.5.4/gast/ast2.py new/gast-0.6.0/gast/ast2.py --- old/gast-0.5.4/gast/ast2.py 2023-04-29 21:35:43.000000000 +0200 +++ new/gast-0.6.0/gast/ast2.py 2024-06-27 22:30:35.000000000 +0200 @@ -22,6 +22,7 @@ self._visit(node.decorator_list), None, # returns None, # type_comment + [], # type_params ) gast.copy_location(new_node, node) new_node.end_lineno = new_node.end_col_offset = None @@ -34,6 +35,7 @@ [], # keywords self._visit(node.body), self._visit(node.decorator_list), + [], # type_params ) gast.copy_location(new_node, node) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gast-0.5.4/gast/ast3.py new/gast-0.6.0/gast/ast3.py --- old/gast-0.5.4/gast/ast3.py 2023-04-29 21:35:43.000000000 +0200 +++ new/gast-0.6.0/gast/ast3.py 2024-06-27 22:30:35.000000000 +0200 @@ -82,6 +82,7 @@ self._visit(node.decorator_list), self._visit(node.returns), None, # type_comment + [], # type_params ) return gast.copy_location(new_node, node) @@ -93,6 +94,7 @@ self._visit(node.decorator_list), self._visit(node.returns), None, # type_comment + [], # type_params ) return gast.copy_location(new_node, node) @@ -223,6 +225,31 @@ ) return ast.copy_location(new_node, node) + if 8 <= sys.version_info.minor < 12: + def visit_FunctionDef(self, node): + new_node = gast.FunctionDef( + self._visit(node.name), + self._visit(node.args), + self._visit(node.body), + self._visit(node.decorator_list), + self._visit(node.returns), + self._visit(node.type_comment), + [], # type_params + ) + return gast.copy_location(new_node, node) + + def visit_AsyncFunctionDef(self, node): + new_node = gast.AsyncFunctionDef( + self._visit(node.name), + self._visit(node.args), + self._visit(node.body), + self._visit(node.decorator_list), + self._visit(node.returns), + self._visit(node.type_comment), + [], # type_params + ) + return gast.copy_location(new_node, node) + class GAstToAst3(GAstToAst): if sys.version_info.minor < 10: @@ -423,6 +450,41 @@ self._visit(node.keywords), ) return ast.copy_location(new_node, node) + if 5 <= sys.version_info.minor < 12: + def visit_ClassDef(self, node): + new_node = ast.ClassDef( + self._visit(node.name), + self._visit(node.bases), + self._visit(node.keywords), + self._visit(node.body), + self._visit(node.decorator_list), + ) + return ast.copy_location(new_node, node) + + if 8 <= sys.version_info.minor < 12: + def visit_FunctionDef(self, node): + new_node = ast.FunctionDef( + self._visit(node.name), + self._visit(node.args), + self._visit(node.body), + self._visit(node.decorator_list), + self._visit(node.returns), + self._visit(node.type_comment), + ) + return ast.copy_location(new_node, node) + + def visit_AsyncFunctionDef(self, node): + new_node = ast.AsyncFunctionDef( + self._visit(node.name), + self._visit(node.args), + self._visit(node.body), + self._visit(node.decorator_list), + self._visit(node.returns), + self._visit(node.type_comment), + ) + return ast.copy_location(new_node, node) + + def visit_arguments(self, node): extra_args = [self._make_arg(node.vararg), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gast-0.5.4/gast/gast.py new/gast-0.6.0/gast/gast.py --- old/gast-0.5.4/gast/gast.py 2023-04-29 21:35:43.000000000 +0200 +++ new/gast-0.6.0/gast/gast.py 2024-06-27 22:30:35.000000000 +0200 @@ -17,20 +17,29 @@ pass +try: + from ast import type_param +except ImportError: + class type_param(AST): + pass + + def _make_node(Name, Fields, Attributes, Bases): - NBFields = len(Fields) + # This constructor is used a lot during conversion from ast to gast, + # then as the primary way to build ast nodes. So we tried to optimized it + # for speed and not for readability. def create_node(self, *args, **kwargs): - if args: - if len(args) + len([k for k in kwargs if k in Fields]) != NBFields: - raise TypeError( - "{} constructor takes either 0 or {} mandatory arguments". - format(Name, NBFields)) - for argname, argval in zip(Fields, args): - setattr(self, argname, argval) - if kwargs: - for argname, argval in kwargs.items(): - setattr(self, argname, argval) + if len(args) > len(Fields): + raise TypeError( + "{} constructor takes at most {} positional arguments". + format(Name, len(Fields))) + + # it's faster to iterate rather than zipping or enumerate + for i in range(len(args)): + setattr(self, Fields[i], args[i]) + if kwargs: # cold branch + self.__dict__.update(kwargs) setattr(_sys.modules[__name__], Name, @@ -51,16 +60,16 @@ # stmt ('FunctionDef', (('name', 'args', 'body', 'decorator_list', 'returns', - 'type_comment'), + 'type_comment', 'type_params'), ('lineno', 'col_offset', 'end_lineno', 'end_col_offset',), (stmt,))), - ('AsyncFunctionDef', (('name', 'args', 'body', - 'decorator_list', 'returns', - 'type_comment'), + ('AsyncFunctionDef', (('name', 'args', 'body', 'decorator_list', 'returns', + 'type_comment', 'type_params',), ('lineno', 'col_offset', 'end_lineno', 'end_col_offset',), (stmt,))), - ('ClassDef', (('name', 'bases', 'keywords', 'body', 'decorator_list',), + ('ClassDef', (('name', 'bases', 'keywords', 'body', 'decorator_list', + 'type_params',), ('lineno', 'col_offset', 'end_lineno', 'end_col_offset',), (stmt,))), ('Return', (('value',), @@ -72,6 +81,9 @@ ('Assign', (('targets', 'value', 'type_comment'), ('lineno', 'col_offset', 'end_lineno', 'end_col_offset',), (stmt,))), + ('TypeAlias', (('name', 'type_params', 'value'), + ('lineno', 'col_offset', 'end_lineno', 'end_col_offset',), + (stmt,))), ('AugAssign', (('target', 'op', 'value',), ('lineno', 'col_offset', 'end_lineno', 'end_col_offset',), (stmt,))), @@ -340,8 +352,25 @@ # type_ignore ('type_ignore', ((), ('lineno', 'tag'), (TypeIgnore,))), + + # type_param + ('TypeVar', (('name', 'bound',), + ('lineno', 'col_offset', + 'end_lineno', 'end_col_offset'), + (type_param,))), + ('ParamSpec', (('name',), + ('lineno', 'col_offset', + 'end_lineno', 'end_col_offset'), + (type_param,))), + ('TypeVarTuple', (('name',), + ('lineno', 'col_offset', + 'end_lineno', 'end_col_offset'), + (type_param,))), ) + + + for name, descr in _nodes: _make_node(name, *descr) @@ -440,3 +469,95 @@ if 'end_lineno' in child._attributes: child.end_lineno = (getattr(child, 'end_lineno', 0) or 0) + n return node + +if _sys.version_info.major == 3 and _sys.version_info.minor >= 13: + dump = _ast.dump +else: + # Code import from Lib/ast.py + # + # minor changes: getattr(x, y, ...) is None => getattr(x, y, 42) is None + # + def dump( + node, annotate_fields=True, include_attributes=False, + # *, # removed for compatibility with python2 :-/ + indent=None, show_empty=False, + ): + """ + Return a formatted dump of the tree in node. This is mainly useful for + debugging purposes. If annotate_fields is true (by default), + the returned string will show the names and the values for fields. + If annotate_fields is false, the result string will be more compact by + omitting unambiguous field names. Attributes such as line + numbers and column offsets are not dumped by default. If this is wanted, + include_attributes can be set to true. If indent is a non-negative + integer or string, then the tree will be pretty-printed with that indent + level. None (the default) selects the single line representation. + If show_empty is False, then empty lists and fields that are None + will be omitted from the output for better readability. + """ + def _format(node, level=0): + if indent is not None: + level += 1 + prefix = '\n' + indent * level + sep = ',\n' + indent * level + else: + prefix = '' + sep = ', ' + if isinstance(node, AST): + cls = type(node) + args = [] + args_buffer = [] + allsimple = True + keywords = annotate_fields + for name in node._fields: + try: + value = getattr(node, name) + except AttributeError: + keywords = True + continue + if value is None and getattr(cls, name, 42) is None: + keywords = True + continue + if ( + not show_empty + and (value is None or value == []) + # Special cases: + # `Constant(value=None)` and `MatchSingleton(value=None)` + and not isinstance(node, (Constant, MatchSingleton)) + ): + args_buffer.append(repr(value)) + continue + elif not keywords: + args.extend(args_buffer) + args_buffer = [] + value, simple = _format(value, level) + allsimple = allsimple and simple + if keywords: + args.append('%s=%s' % (name, value)) + else: + args.append(value) + if include_attributes and node._attributes: + for name in node._attributes: + try: + value = getattr(node, name) + except AttributeError: + continue + if value is None and getattr(cls, name, 42) is None: + continue + value, simple = _format(value, level) + allsimple = allsimple and simple + args.append('%s=%s' % (name, value)) + if allsimple and len(args) <= 3: + return '%s(%s)' % (node.__class__.__name__, ', '.join(args)), not args + return '%s(%s%s)' % (node.__class__.__name__, prefix, sep.join(args)), False + elif isinstance(node, list): + if not node: + return '[]', True + return '[%s%s]' % (prefix, sep.join(_format(x, level)[0] for x in node)), False + return repr(node), True + + if not isinstance(node, AST): + raise TypeError('expected AST, got %r' % node.__class__.__name__) + if indent is not None and not isinstance(indent, str): + indent = ' ' * indent + return _format(node)[0] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gast-0.5.4/gast/unparser.py new/gast-0.6.0/gast/unparser.py --- old/gast-0.5.4/gast/unparser.py 2023-04-29 21:35:43.000000000 +0200 +++ new/gast-0.6.0/gast/unparser.py 2024-06-27 22:30:35.000000000 +0200 @@ -412,6 +412,8 @@ self.fill("@") self.traverse(deco) self.fill("class " + node.name) + if hasattr(node, "type_params"): + self._type_params_helper(node.type_params) with self.delimit_if("(", ")", condition = node.bases or node.keywords): comma = False for e in node.bases: @@ -443,6 +445,8 @@ self.traverse(deco) def_str = fill_suffix + " " + node.name self.fill(def_str) + if hasattr(node, "type_params"): + self._type_params_helper(node.type_params) with self.delimit("(", ")"): self.traverse(node.args) if node.returns: @@ -451,6 +455,30 @@ with self.block(extra=self.get_type_comment(node)): self._write_docstring_and_traverse_body(node) + def _type_params_helper(self, type_params): + if type_params is not None and len(type_params) > 0: + with self.delimit("[", "]"): + self.interleave(lambda: self.write(", "), self.traverse, type_params) + + def visit_TypeVar(self, node): + self.write(node.name) + if node.bound: + self.write(": ") + self.traverse(node.bound) + + def visit_TypeVarTuple(self, node): + self.write("*" + node.name) + + def visit_ParamSpec(self, node): + self.write("**" + node.name) + + def visit_TypeAlias(self, node): + self.fill("type ") + self.traverse(node.name) + self._type_params_helper(node.type_params) + self.write(" = ") + self.traverse(node.value) + def visit_For(self, node): self._for_helper("for ", node) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gast-0.5.4/gast/version.py new/gast-0.6.0/gast/version.py --- old/gast-0.5.4/gast/version.py 2023-04-29 21:35:43.000000000 +0200 +++ new/gast-0.6.0/gast/version.py 2024-06-27 22:30:35.000000000 +0200 @@ -1 +1 @@ -__version__ = '0.5.4' +__version__ = '0.6.0' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gast-0.5.4/tests/test_api.py new/gast-0.6.0/tests/test_api.py --- old/gast-0.5.4/tests/test_api.py 2023-04-29 21:35:43.000000000 +0200 +++ new/gast-0.6.0/tests/test_api.py 2024-06-27 22:30:35.000000000 +0200 @@ -2,6 +2,11 @@ import ast import gast +import sys + + +def dump(node): + return gast.dump(node, show_empty=True) class APITestCase(unittest.TestCase): @@ -33,7 +38,7 @@ def test_dump(self): code = 'lambda x: x' tree = gast.parse(code, mode='eval') - dump = gast.dump(tree) + zdump = dump(tree) norm = ("Expression(body=Lambda(args=arguments(args=[Name(" "id='x', ctx=Param(), " "annotation=None, type_comment=None)], posonlyargs=[], " @@ -41,16 +46,16 @@ "defaults=[]), body=Name(id='x', ctx=Load(), " "annotation=None, type_comment=None)" "))") - self.assertEqual(dump, norm) + self.assertEqual(zdump, norm) def test_walk(self): code = 'x + 1' tree = gast.parse(code, mode='eval') - dump = gast.dump(tree) + zdump = dump(tree) norm = ("Expression(body=BinOp(left=Name(id='x', ctx=Load(), " "annotation=None, type_comment=None), op=Add(), " "right=Constant(value=1, kind=None)))") - self.assertEqual(dump, norm) + self.assertEqual(zdump, norm) self.assertEqual(len(list(gast.walk(tree))), 6) def test_iter_fields(self): @@ -132,7 +137,8 @@ node1 = gast.Name('id', load, None, None) node2 = gast.Name('id', load, None, type_comment=None) with self.assertRaises(TypeError): - node1 = gast.Name('id') + node1 = gast.Name('id', 'ctx', 'annotation', 'type_comment', + 'random_field') for field in gast.Name._fields: self.assertEqual(getattr(node1, field), getattr(node2, field)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gast-0.5.4/tests/test_compat.py new/gast-0.6.0/tests/test_compat.py --- old/gast-0.5.4/tests/test_compat.py 2023-04-29 21:35:43.000000000 +0200 +++ new/gast-0.6.0/tests/test_compat.py 2024-06-27 22:30:35.000000000 +0200 @@ -4,8 +4,16 @@ import sys +def dump(node): + return gast.dump(node, show_empty=True) + + class CompatTestCase(unittest.TestCase): + def __init__(self, *args, **kwargs): + unittest.TestCase.__init__(self, *args, **kwargs) + self.maxDiff = None + if sys.version_info.major == 2: def test_FunctionDef(self): @@ -23,8 +31,9 @@ "Name(id='y', ctx=Load(), " "annotation=None, type_comment=None" ")], ctx=Load()))], decorator_list=" - "[], returns=None, type_comment=None)], type_ignores=[])") - self.assertEqual(gast.dump(tree), norm) + "[], returns=None, type_comment=None, type_params=[])], " + "type_ignores=[])") + self.assertEqual(dump(tree), norm) else: @@ -38,8 +47,9 @@ "), type_comment=None)], posonlyargs=" "[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=" "None, defaults=[]), body=[Pass()], decorator_list=[], " - "returns=None, type_comment=None)], type_ignores=[])") - self.assertEqual(gast.dump(tree), norm) + "returns=None, type_comment=None, type_params=[])], " + "type_ignores=[])") + self.assertEqual(dump(tree), norm) def test_KeywordOnlyArgument(self): code = 'def foo(*, x=1): pass' @@ -50,8 +60,9 @@ "(id='x', ctx=Param(), annotation=None, type_comment=None" ")], kw_defaults=[Constant(value=1, kind=None)], kwarg=" "None, defaults=[]), body=[Pass()], decorator_list=[], " - "returns=None, type_comment=None)], type_ignores=[])") - self.assertEqual(gast.dump(tree), norm) + "returns=None, type_comment=None, type_params=[])], " + "type_ignores=[])") + self.assertEqual(dump(tree), norm) if sys.version_info.minor >= 6: @@ -67,7 +78,7 @@ "ctx=Load(), annotation=None, type_comment=None), " "conversion=-1, format_spec=None)]))], " "type_ignores=[])") - self.assertEqual(gast.dump(tree), norm) + self.assertEqual(dump(tree), norm) def test_JoinedStr(self): code = 'e = 1; f"e = {e}"' @@ -82,7 +93,7 @@ "annotation=None, type_comment=None), " "conversion=-1, format_spec=None)]))], " "type_ignores=[])") - self.assertEqual(gast.dump(tree), norm) + self.assertEqual(dump(tree), norm) if sys.version_info.minor >= 8: @@ -94,9 +105,9 @@ "args=[], posonlyargs=[], vararg=None, kwonlyargs=[], " "kw_defaults=[], kwarg=None, defaults=[]), body=[" "Pass()], decorator_list=[], returns=None, " - "type_comment=None)], type_ignores=" + "type_comment=None, type_params=[])], type_ignores=" "[TypeIgnore(lineno=1, tag='[excuse]')])") - self.assertEqual(gast.dump(tree), norm) + self.assertEqual(dump(tree), norm) def test_PosonlyArgs(self): code = 'def foo(a, /, b): pass' @@ -108,9 +119,9 @@ "ctx=Param(), annotation=None, type_comment=None)], " "vararg=None, kwonlyargs=[], kw_defaults=[], " "kwarg=None, defaults=[]), body=[Pass()], " - "decorator_list=[], returns=None, type_comment=None)" - "], type_ignores=[])") - self.assertEqual(gast.dump(tree), norm) + "decorator_list=[], returns=None, type_comment=None, " + "type_params=[])], type_ignores=[])") + self.assertEqual(dump(tree), norm) def test_NamedExpr(self): code = '(x := 1) ' @@ -120,7 +131,7 @@ " ctx=Store(), annotation=None, type_comment=None), " "value=Constant(value=1, kind=None)))], type_ignores=" "[])") - self.assertEqual(gast.dump(tree), norm) + self.assertEqual(dump(tree), norm) if sys.version_info.minor >= 10: @@ -135,10 +146,9 @@ "[Expr(value=Constant(value=Ellipsis, kind=None))]" ")])], type_ignores=[])" ) - self.assertEqual(gast.dump(tree), norm) + self.assertEqual(dump(tree), norm) def test_MatchSingleton(self): - self.maxDiff = None code = 'match v:\n case None:...' tree = gast.parse(code) compile(gast.gast_to_ast(tree), '<test>', 'exec') @@ -147,7 +157,7 @@ "match_case(pattern=MatchSingleton(value=None), " "guard=None, body=[Expr(value=Constant(value=" "Ellipsis, kind=None))])])], type_ignores=[])") - self.assertEqual(gast.dump(tree), norm) + self.assertEqual(dump(tree), norm) def test_MatchSequence(self): code = 'match v:\n case a, b:...' @@ -160,7 +170,7 @@ "=None, name='b')]), guard=None, body=[Expr(value" "=Constant(value=Ellipsis, kind=None))])])], " "type_ignores=[])") - self.assertEqual(gast.dump(tree), norm) + self.assertEqual(dump(tree), norm) def test_MatchMapping(self): code = 'match v:\n case {1: a}:...' @@ -173,7 +183,7 @@ "=None, name='a')], rest=None), guard=None, body=" "[Expr(value=Constant(value=Ellipsis, kind=None))]" ")])], type_ignores=[])") - self.assertEqual(gast.dump(tree), norm) + self.assertEqual(dump(tree), norm) def test_MatchClass(self): code = 'match v:\n case Cls(attr=1):...' @@ -187,7 +197,7 @@ "=[MatchValue(value=Constant(value=1, kind=None))" "]), guard=None, body=[Expr(value=Constant(value=" "Ellipsis, kind=None))])])], type_ignores=[])") - self.assertEqual(gast.dump(tree), norm) + self.assertEqual(dump(tree), norm) def test_MatchStar(self): code = 'match v:\n case [1, *other]:...' @@ -200,7 +210,7 @@ "MatchStar(name='other')]), guard=None, body=" "[Expr(value=Constant(value=Ellipsis, kind=None)" ")])])], type_ignores=[])") - self.assertEqual(gast.dump(tree), norm) + self.assertEqual(dump(tree), norm) def test_MatchAs(self): code = 'match v:\n case 1, other:...' @@ -213,7 +223,7 @@ "MatchAs(pattern=None, name='other')]), guard=None" ", body=[Expr(value=Constant(value=Ellipsis, kind" "=None))])])], type_ignores=[])") - self.assertEqual(gast.dump(tree), norm) + self.assertEqual(dump(tree), norm) def test_MatchOr(self): code = 'match v:\n case 1 | 2:...' @@ -226,7 +236,7 @@ "value=Constant(value=2, kind=None))]), guard=" "None, body=[Expr(value=Constant(value=Ellipsis, " "kind=None))])])], type_ignores=[])") - self.assertEqual(gast.dump(tree), norm) + self.assertEqual(dump(tree), norm) if sys.version_info.minor >= 11: @@ -251,7 +261,7 @@ compile(gast.gast_to_ast(tree), '<test>', 'exec') norm = ("Module(body=[Expr(value=Constant(value=b'0012', " "kind=None))], type_ignores=[])") - self.assertEqual(gast.dump(tree), norm) + self.assertEqual(dump(tree), norm) # common @@ -263,7 +273,7 @@ "type=Name(id='e', ctx=Load(), annotation=None, " "type_comment=None), name=None, body=[Pass()])]" ", orelse=[Pass()], finalbody=[])], type_ignores=[])") - self.assertEqual(gast.dump(tree), norm) + self.assertEqual(dump(tree), norm) def test_TryExceptNamed(self): code = 'try:pass\nexcept e as f:pass\nelse:pass' @@ -274,7 +284,7 @@ "type_comment=None), name=Name(id='f', ctx=" "Store(), annotation=None, type_comment=None), body=[Pass()])]" ", orelse=[Pass()], finalbody=[])], type_ignores=[])") - self.assertEqual(gast.dump(tree), norm) + self.assertEqual(dump(tree), norm) def test_Raise(self): codes = ('raise Exception', @@ -315,10 +325,9 @@ for code, norm in zip(codes, norms): tree = gast.parse(code) compile(gast.gast_to_ast(tree), '<test>', 'exec') - self.assertEqual(gast.dump(tree), norm) + self.assertEqual(dump(tree), norm) def test_Call(self): - self.maxDiff = None code = 'foo(x, y=1, *args, **kwargs)' tree = gast.parse(code) compile(gast.gast_to_ast(tree), '<test>', 'exec') @@ -331,7 +340,7 @@ "arg='y', value=Constant(value=1, kind=None)), keyword(arg" "=None, value=Name(id='kwargs', ctx=Load(), annotation=None, " "type_comment=None))]))], type_ignores=[])") - self.assertEqual(gast.dump(tree), norm) + self.assertEqual(dump(tree), norm) def test_With(self): code = 'with open("any"): pass' @@ -342,7 +351,7 @@ "type_comment=None), args=[Constant(value='any', " "kind=None)], keywords=[]), optional_vars=None)], body=[" "Pass()], type_comment=None)], type_ignores=[])") - self.assertEqual(gast.dump(tree), norm) + self.assertEqual(dump(tree), norm) def test_TryFinally(self): code = 'try:pass\nfinally:pass' @@ -350,7 +359,7 @@ compile(gast.gast_to_ast(tree), '<test>', 'exec') norm = ("Module(body=[Try(body=[Pass()], handlers=[], orelse=[], " "finalbody=[Pass()])], type_ignores=[])") - self.assertEqual(gast.dump(tree), norm) + self.assertEqual(dump(tree), norm) def test_star_argument(self): code = 'def foo(*a): pass' @@ -361,8 +370,8 @@ "annotation=None, type_comment=None), " "kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), " "body=[Pass()], decorator_list=[], returns=None, " - "type_comment=None)], type_ignores=[])") - self.assertEqual(gast.dump(tree), norm) + "type_comment=None, type_params=[])], type_ignores=[])") + self.assertEqual(dump(tree), norm) def test_keyword_argument(self): code = 'def foo(**a): pass' @@ -372,9 +381,9 @@ "posonlyargs=[], vararg=None, kwonlyargs=[], kw_defaults=[], " "kwarg=Name(id='a', ctx=Param(), annotation=None, " "type_comment=None), defaults=[]), body=[Pass()], " - "decorator_list=[], returns=None, type_comment=None)], " - "type_ignores=[])") - self.assertEqual(gast.dump(tree), norm) + "decorator_list=[], returns=None, type_comment=None, " + "type_params=[])], type_ignores=[])") + self.assertEqual(dump(tree), norm) def test_Index(self): code = 'def foo(a): a[1]' @@ -386,12 +395,11 @@ ", kwarg=None, defaults=[]), body=[Expr(value=Subscript(value=" "Name(id='a', ctx=Load(), annotation=None, type_comment=None)" ", slice=Constant(value=1, kind=None), ctx=Load()" - "))], decorator_list=[], returns=None, type_comment=None)]" - ", type_ignores=[])") - self.assertEqual(gast.dump(tree), norm) + "))], decorator_list=[], returns=None, type_comment=None, " + "type_params=[])], type_ignores=[])") + self.assertEqual(dump(tree), norm) def test_ExtSlice(self): - self.maxDiff = None code = 'def foo(a): a[:,:]' tree = gast.parse(code) compile(gast.gast_to_ast(tree), '<test>', 'exec') @@ -403,11 +411,10 @@ ", slice=Tuple(elts=[Slice(lower=None, upper=None, step=" "None), Slice(lower=None, upper=None, step=None)], ctx=Load())" ", ctx=Load()))], decorator_list=[], returns=None, " - "type_comment=None)], type_ignores=[])") - self.assertEqual(gast.dump(tree), norm) + "type_comment=None, type_params=[])], type_ignores=[])") + self.assertEqual(dump(tree), norm) def test_ExtSlices(self): - self.maxDiff = None code = 'def foo(a): a[1,:]' tree = gast.parse(code) compile(gast.gast_to_ast(tree), '<test>', 'exec') @@ -419,11 +426,10 @@ ", slice=Tuple(elts=[Constant(value=1, kind=" "None), Slice(lower=None, upper=None, step=None)], ctx=Load())" ", ctx=Load()))], decorator_list=[], returns=None, " - "type_comment=None)], type_ignores=[])") - self.assertEqual(gast.dump(tree), norm) + "type_comment=None, type_params=[])], type_ignores=[])") + self.assertEqual(dump(tree), norm) def test_Ellipsis(self): - self.maxDiff = None code = 'def foo(a): a[...]' tree = gast.parse(code) compile(gast.gast_to_ast(tree), '<test>', 'exec') @@ -433,12 +439,11 @@ ", kwarg=None, defaults=[]), body=[Expr(value=Subscript(value=" "Name(id='a', ctx=Load(), annotation=None, type_comment=None)" ", slice=Constant(value=Ellipsis, kind=None), ctx=Load()))], " - "decorator_list=[], returns=None, type_comment=" - "None)], type_ignores=[])") - self.assertEqual(gast.dump(tree), norm) + "decorator_list=[], returns=None, type_comment=None, " + "type_params=[])], type_ignores=[])") + self.assertEqual(dump(tree), norm) def test_ExtSliceEllipsis(self): - self.maxDiff = None code = 'def foo(a): a[1, ...]' tree = gast.parse(code) compile(gast.gast_to_ast(tree), '<test>', 'exec') @@ -450,8 +455,8 @@ ", slice=Tuple(elts=[Constant(value=1, kind=None)" ", Constant(value=Ellipsis, kind=None)], ctx=Load()), ctx=" "Load()))], decorator_list=[], returns=None, type_comment=" - "None)], type_ignores=[])") - self.assertEqual(gast.dump(tree), norm) + "None, type_params=[])], type_ignores=[])") + self.assertEqual(dump(tree), norm) if __name__ == '__main__': diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gast-0.5.4/tests/test_py3_12.py new/gast-0.6.0/tests/test_py3_12.py --- old/gast-0.5.4/tests/test_py3_12.py 1970-01-01 01:00:00.000000000 +0100 +++ new/gast-0.6.0/tests/test_py3_12.py 2024-06-27 22:30:35.000000000 +0200 @@ -0,0 +1,71 @@ +import unittest + +import ast +import gast +import sys + +def dump(node): + return gast.dump(node, show_empty=True) + + +class Python3_12TestCase(unittest.TestCase): + + def __init__(self, *args, **kwargs): + unittest.TestCase.__init__(self, *args, **kwargs) + self.maxDiff = None + + def test_type_alias(self): + code = "type Point = tuple[float, float]" + tree = gast.parse(code) + compile(gast.gast_to_ast(tree), '<test>', 'exec') + norm = ("Module(body=[TypeAlias(name=Name(id='Point', ctx=Store()," + " annotation=None, type_comment=None), type_params=[], " + "value=Subscript(value=Name(id='tuple', ctx=Load(), " + "annotation=None, type_comment=None), slice=Tuple(elts=[" + "Name(id='float', ctx=Load(), annotation=None, " + "type_comment=None), Name(id='float', ctx=Load(), " + "annotation=None, type_comment=None)], ctx=Load()), " + "ctx=Load()))], type_ignores=[])") + self.assertEqual(dump(tree), norm) + + def test_generic_type_alias(self): + code = "type Point[T] = tuple[T, float]" + tree = gast.parse(code) + compile(gast.gast_to_ast(tree), '<test>', 'exec') + norm = ("Module(body=[TypeAlias(name=Name(id='Point', ctx=Store(), " + "annotation=None, type_comment=None), type_params=[TypeVar(" + "name='T', bound=None)], value=Subscript(value=Name(id='tuple'" + ", ctx=Load(), annotation=None, type_comment=None), " + "slice=Tuple(elts=[Name(id='T', ctx=Load(), annotation=None, " + "type_comment=None), Name(id='float', ctx=Load(), " + "annotation=None, type_comment=None)], ctx=Load()), ctx=Load()" + "))], type_ignores=[])") + self.assertEqual(dump(tree), norm) + + def test_generic_function(self): + code = "def foo[T]():..." + tree = gast.parse(code) + compile(gast.gast_to_ast(tree), '<test>', 'exec') + norm = ("Module(body=[FunctionDef(name='foo', args=arguments(args=[], " + "posonlyargs=[], vararg=None, kwonlyargs=[], kw_defaults=[], " + "kwarg=None, defaults=[]), body=[Expr(value=Constant(value=" + "Ellipsis, kind=None))], decorator_list=[], returns=None, " + "type_comment=None, type_params=[TypeVar(name='T', " + "bound=None)])], type_ignores=[])") + self.assertEqual(dump(tree), norm) + + def test_generic_class(self): + code = "class foo[T]:..." + tree = gast.parse(code) + compile(gast.gast_to_ast(tree), '<test>', 'exec') + norm = ("Module(body=[ClassDef(name='foo', bases=[], keywords=[], " + "body=[Expr(value=Constant(value=Ellipsis, kind=None))], " + "decorator_list=[], type_params=[TypeVar(name='T', bound=None)" + "])], type_ignores=[])") + self.assertEqual(dump(tree), norm) + +if sys.version_info < (3, 12): + del Python3_12TestCase + +if __name__ == '__main__': + unittest.main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gast-0.5.4/tests/test_unparser.py new/gast-0.6.0/tests/test_unparser.py --- old/gast-0.5.4/tests/test_unparser.py 1970-01-01 01:00:00.000000000 +0100 +++ new/gast-0.6.0/tests/test_unparser.py 2024-06-27 22:30:35.000000000 +0200 @@ -0,0 +1,37 @@ +import unittest + +import ast +import gast +import sys + + +class UnparserTestCase(unittest.TestCase): + + def __init__(self, *args, **kwargs): + unittest.TestCase.__init__(self, *args, **kwargs) + self.maxDiff = None + + def assertUnparse(self, code): + normalized_code = ast.unparse(ast.parse(code)) + tree = gast.parse(normalized_code) + compile(gast.gast_to_ast(tree), '<test>', 'exec') + unparsed = gast.unparse(tree) + self.assertEqual(normalized_code, unparsed) + + def test_FunctionDef(self): + self.assertUnparse('def foo(x, y): return x, y') + + def test_BinaryOp(self): + self.assertUnparse('1 + 3') + + if sys.version_info >= (3, 12): + + def test_TypeParameter(self): + self.assertUnparse('type x[T] = list[T]') + + +if sys.version_info < (3, 9): + del UnparserTestCase + +if __name__ == '__main__': + unittest.main()
