Skip to content

bpo-39999: Improve compatibility of the ast module. #19056

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions Doc/whatsnew/3.9.rst
Original file line number Diff line number Diff line change
Expand Up @@ -590,12 +590,19 @@ Deprecated

(Contributed by Victor Stinner in :issue:`39353`.)

* :mod:`ast` classes ``Index`` and ``ExtSlice`` are considered deprecated
* :mod:`ast` classes ``slice``, ``Index`` and ``ExtSlice`` are considered deprecated
and will be removed in future Python versions. ``value`` itself should be
used instead of ``Index(value)``. ``Tuple(slices, Load())`` should be
used instead of ``ExtSlice(slices)``.
(Contributed by Serhiy Storchaka in :issue:`32892`.)

* :mod:`ast` classes ``Suite``, ``Param``, ``AugLoad`` and ``AugStore``
are considered deprecated and will be removed in future Python versions.
They were not generated by the parser and not accepted by the code
generator in Python 3.
(Contributed by Batuhan Taskaya in :issue:`39639` and :issue:`39969`
and Serhiy Storchaka in :issue:`39988`.)

* The :c:func:`PyEval_InitThreads` and :c:func:`PyEval_ThreadsInitialized`
functions are now deprecated and will be removed in Python 3.11. Calling
:c:func:`PyEval_InitThreads` now does nothing. The :term:`GIL` is initialized
Expand Down Expand Up @@ -701,11 +708,6 @@ Removed
defining ``COUNT_ALLOCS`` macro.
(Contributed by Victor Stinner in :issue:`39489`.)

* The ``ast.Suite``, ``ast.Param``, ``ast.AugLoad`` and ``ast.AugStore``
node classes have been removed due to no longer being needed.
(Contributed by Batuhan Taskaya in :issue:`39639` and :issue:`39969`
and Serhiy Storchaka in :issue:`39988`.)


Porting to Python 3.9
=====================
Expand Down
26 changes: 24 additions & 2 deletions Lib/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,7 @@ def generic_visit(self, node):
# It will be removed in future.

def _getter(self):
"""Deprecated. Use value instead."""
return self.value

def _setter(self, value):
Expand All @@ -499,6 +500,9 @@ def _setter(self, value):

class _ABC(type):

def __init__(cls, *args):
cls.__doc__ = """Deprecated AST node class. Use ast.Constant instead"""

def __instancecheck__(cls, inst):
if not isinstance(inst, Constant):
return False
Expand Down Expand Up @@ -564,22 +568,40 @@ def __new__(cls, *args, **kwargs):
type(...): 'Ellipsis',
}

class Index(AST):
class slice(AST):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@serhiy-storchaka what do you think about implementing custom instance and subclass checks for slice? slice is a sum type, so there should be checks like this issubclass(thing, ast.slice) or isinstance(thing, ast.slice)

something like this should work

    def __subclasscheck__(self, subclass):
        if issubclass(subclass, Slice):
            return True
        else:
            return super().__subclasscheck__(subclass)

    def __instancecheck__(self, instance):
        if isinstance(instance, Slice):
            return True
        else:
            return super().__instancecheck__(instance)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is Slice?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before the simplification, it was part of slice kind. Now it is an expression by it is own but if we want to increase compatibility, wouldn't it better to it behave like a subclass of ast.slice?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not think it would be useful because Index() can return arbitrary node type and it is impossible to distinguish ExtSlice from Tuple.

"""Deprecated AST node class."""

class Index(slice):
"""Deprecated AST node class. Use the index value directly instead."""
def __new__(cls, value, **kwargs):
return value

class ExtSlice(AST):
class ExtSlice(slice):
"""Deprecated AST node class. Use ast.Tuple instead."""
def __new__(cls, dims=(), **kwargs):
return Tuple(list(dims), Load(), **kwargs)

def _dims_getter(self):
"""Deprecated. Use elts instead."""
return self.elts

def _dims_setter(self, value):
self.elts = value

Tuple.dims = property(_dims_getter, _dims_setter)

class Suite(mod):
"""Deprecated AST node class. Unused in Python 3."""

class AugLoad(expr_context):
"""Deprecated AST node class. Unused in Python 3."""

class AugStore(expr_context):
"""Deprecated AST node class. Unused in Python 3."""

class Param(expr_context):
"""Deprecated AST node class. Unused in Python 3."""


# Large float and imaginary literals get turned into infinities in the AST.
# We unparse those infinities to INFSTR.
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ def test_AST_objects(self):
x.vararg

with self.assertRaises(TypeError):
# "_ast.AST constructor takes 0 positional arguments"
# "ast.AST constructor takes 0 positional arguments"
ast.AST(2)

def test_AST_garbage_collection(self):
Expand Down Expand Up @@ -573,7 +573,7 @@ def test_invalid_sum(self):
m = ast.Module([ast.Expr(ast.expr(**pos), **pos)], [])
with self.assertRaises(TypeError) as cm:
compile(m, "<test>", "exec")
self.assertIn("but got <_ast.expr", str(cm.exception))
self.assertIn("but got <ast.expr", str(cm.exception))

def test_invalid_identifier(self):
m = ast.Module([ast.Expr(ast.Name(42, ast.Load()))], [])
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Deprecated ``ast.Suite`` node class because it's no longer used. Patch by Batuhan Taskaya.
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
Remove ``ast.Param`` node class because it's no longer used. Patch by
Deprecated ``ast.Param`` node class because it's no longer used. Patch by
Batuhan Taskaya.
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
Removed ``ast.AugLoad`` and ``ast.AugStore`` node classes because they are
Deprecated ``ast.AugLoad`` and ``ast.AugStore`` node classes because they are
no longer used.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
``__module__`` of the AST node classes is now set to "ast" instead of
"_ast". Added docstrings for dummy AST node classes and deprecated
attributes.
6 changes: 3 additions & 3 deletions Parser/asdl_c.py
Original file line number Diff line number Diff line change
Expand Up @@ -774,7 +774,7 @@ def visitModule(self, mod):
};

static PyType_Spec AST_type_spec = {
"_ast.AST",
"ast.AST",
sizeof(AST_object),
0,
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
Expand All @@ -800,7 +800,7 @@ def visitModule(self, mod):
type, base,
astmodulestate_global->_fields, fnames,
astmodulestate_global->__module__,
astmodulestate_global->_ast,
astmodulestate_global->ast,
astmodulestate_global->__doc__, doc);
Py_DECREF(fnames);
return result;
Expand Down Expand Up @@ -1302,7 +1302,7 @@ def generate_module_def(f, mod):
visitor_list.add(visitor)

state_strings = {
"_ast",
"ast",
"_fields",
"__doc__",
"__dict__",
Expand Down
12 changes: 6 additions & 6 deletions Python/Python-ast.c

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.