Skip to content

Commit 376137f

Browse files
gh-90953: Emit deprecation warnings for ast features deprecated in Python 3.8 (#104199)
`ast.Num`, `ast.Str`, `ast.Bytes`, `ast.Ellipsis` and `ast.NameConstant` now all emit deprecation warnings on import, access, instantation or `isinstance()` checks. Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent 263abd3 commit 376137f

File tree

4 files changed

+472
-135
lines changed

4 files changed

+472
-135
lines changed

Doc/whatsnew/3.12.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,19 @@ Pending Removal in Python 3.14
844844
use :func:`importlib.util.find_spec` instead.
845845
(Contributed by Nikita Sobolev in :gh:`97850`.)
846846

847+
* The following :mod:`ast` features have been deprecated in documentation since
848+
Python 3.8, now cause a :exc:`DeprecationWarning` to be emitted at runtime
849+
when they are accessed or used, and will be removed in Python 3.14:
850+
851+
* :class:`!ast.Num`
852+
* :class:`!ast.Str`
853+
* :class:`!ast.Bytes`
854+
* :class:`!ast.NameConstant`
855+
* :class:`!ast.Ellipsis`
856+
857+
Use :class:`ast.Constant` instead.
858+
(Contributed by Serhiy Storchaka in :gh:`90953`.)
859+
847860
Pending Removal in Future Versions
848861
----------------------------------
849862

Lib/ast.py

Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -294,9 +294,7 @@ def get_docstring(node, clean=True):
294294
if not(node.body and isinstance(node.body[0], Expr)):
295295
return None
296296
node = node.body[0].value
297-
if isinstance(node, Str):
298-
text = node.s
299-
elif isinstance(node, Constant) and isinstance(node.value, str):
297+
if isinstance(node, Constant) and isinstance(node.value, str):
300298
text = node.value
301299
else:
302300
return None
@@ -499,27 +497,66 @@ def generic_visit(self, node):
499497
return node
500498

501499

500+
_DEPRECATED_VALUE_ALIAS_MESSAGE = (
501+
"{name} is deprecated and will be removed in Python {remove}; use value instead"
502+
)
503+
_DEPRECATED_CLASS_MESSAGE = (
504+
"{name} is deprecated and will be removed in Python {remove}; "
505+
"use ast.Constant instead"
506+
)
507+
508+
502509
# If the ast module is loaded more than once, only add deprecated methods once
503510
if not hasattr(Constant, 'n'):
504511
# The following code is for backward compatibility.
505512
# It will be removed in future.
506513

507-
def _getter(self):
514+
def _n_getter(self):
515+
"""Deprecated. Use value instead."""
516+
import warnings
517+
warnings._deprecated(
518+
"Attribute n", message=_DEPRECATED_VALUE_ALIAS_MESSAGE, remove=(3, 14)
519+
)
520+
return self.value
521+
522+
def _n_setter(self, value):
523+
import warnings
524+
warnings._deprecated(
525+
"Attribute n", message=_DEPRECATED_VALUE_ALIAS_MESSAGE, remove=(3, 14)
526+
)
527+
self.value = value
528+
529+
def _s_getter(self):
508530
"""Deprecated. Use value instead."""
531+
import warnings
532+
warnings._deprecated(
533+
"Attribute s", message=_DEPRECATED_VALUE_ALIAS_MESSAGE, remove=(3, 14)
534+
)
509535
return self.value
510536

511-
def _setter(self, value):
537+
def _s_setter(self, value):
538+
import warnings
539+
warnings._deprecated(
540+
"Attribute s", message=_DEPRECATED_VALUE_ALIAS_MESSAGE, remove=(3, 14)
541+
)
512542
self.value = value
513543

514-
Constant.n = property(_getter, _setter)
515-
Constant.s = property(_getter, _setter)
544+
Constant.n = property(_n_getter, _n_setter)
545+
Constant.s = property(_s_getter, _s_setter)
516546

517547
class _ABC(type):
518548

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

522552
def __instancecheck__(cls, inst):
553+
if cls in _const_types:
554+
import warnings
555+
warnings._deprecated(
556+
f"ast.{cls.__qualname__}",
557+
message=_DEPRECATED_CLASS_MESSAGE,
558+
remove=(3, 14)
559+
)
523560
if not isinstance(inst, Constant):
524561
return False
525562
if cls in _const_types:
@@ -543,6 +580,10 @@ def _new(cls, *args, **kwargs):
543580
if pos < len(args):
544581
raise TypeError(f"{cls.__name__} got multiple values for argument {key!r}")
545582
if cls in _const_types:
583+
import warnings
584+
warnings._deprecated(
585+
f"ast.{cls.__qualname__}", message=_DEPRECATED_CLASS_MESSAGE, remove=(3, 14)
586+
)
546587
return Constant(*args, **kwargs)
547588
return Constant.__new__(cls, *args, **kwargs)
548589

@@ -565,10 +606,19 @@ class Ellipsis(Constant, metaclass=_ABC):
565606
_fields = ()
566607

567608
def __new__(cls, *args, **kwargs):
568-
if cls is Ellipsis:
609+
if cls is _ast_Ellipsis:
610+
import warnings
611+
warnings._deprecated(
612+
"ast.Ellipsis", message=_DEPRECATED_CLASS_MESSAGE, remove=(3, 14)
613+
)
569614
return Constant(..., *args, **kwargs)
570615
return Constant.__new__(cls, *args, **kwargs)
571616

617+
# Keep another reference to Ellipsis in the global namespace
618+
# so it can be referenced in Ellipsis.__new__
619+
# (The original "Ellipsis" name is removed from the global namespace later on)
620+
_ast_Ellipsis = Ellipsis
621+
572622
_const_types = {
573623
Num: (int, float, complex),
574624
Str: (str,),
@@ -1699,6 +1749,22 @@ def unparse(ast_obj):
16991749
return unparser.visit(ast_obj)
17001750

17011751

1752+
_deprecated_globals = {
1753+
name: globals().pop(name)
1754+
for name in ('Num', 'Str', 'Bytes', 'NameConstant', 'Ellipsis')
1755+
}
1756+
1757+
def __getattr__(name):
1758+
if name in _deprecated_globals:
1759+
globals()[name] = value = _deprecated_globals[name]
1760+
import warnings
1761+
warnings._deprecated(
1762+
f"ast.{name}", message=_DEPRECATED_CLASS_MESSAGE, remove=(3, 14)
1763+
)
1764+
return value
1765+
raise AttributeError(f"module 'ast' has no attribute '{name}'")
1766+
1767+
17021768
def main():
17031769
import argparse
17041770

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy