Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

gh-121141: add support forcopy.replace to AST nodes#121162

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
JelleZijlstra merged 25 commits intopython:mainfrompicnixz:ast-copy-replace
Jul 4, 2024
Merged
Changes from1 commit
Commits
Show all changes
25 commits
Select commitHold shift + click to select a range
2c1e3c4
fix `ast.compare` to handle missing fields at runtime
picnixzJun 29, 2024
28c2c6e
add support for `copy.replace` on AST nodes
picnixzJun 29, 2024
262608a
add tests
picnixzJun 29, 2024
70b06f5
blurb
picnixzJun 29, 2024
6d45361
update What's New
picnixzJun 29, 2024
8bdc824
update comments
picnixzJun 29, 2024
12b3c07
simplify implementation
picnixzJun 30, 2024
dff189d
update tests
picnixzJun 30, 2024
692ab55
fixup checks
picnixzJul 1, 2024
c22ba93
update tests
picnixzJul 1, 2024
93b97da
simplify implementation
picnixzJul 1, 2024
1085650
update template
picnixzJul 1, 2024
a73d181
regenerate objects
picnixzJul 1, 2024
829d091
ensure that the node attributes are copied
picnixzJul 1, 2024
08385f6
update tests
picnixzJul 1, 2024
0fe8951
update tests
picnixzJul 2, 2024
00c1e36
add strict check for keyword arguments
picnixzJul 2, 2024
59ea13d
update tests
picnixzJul 2, 2024
3041f7d
add strict check for keyword arguments
picnixzJul 2, 2024
40c3749
simplify implementation
picnixzJul 2, 2024
7afcc05
add comments
picnixzJul 2, 2024
51a1068
fixup! whitespace
picnixzJul 2, 2024
5f4888e
Merge branch 'main' into ast-copy-replace
picnixzJul 2, 2024
c04882a
update import comments
picnixzJul 2, 2024
bddcb97
address Jelle's comment
picnixzJul 3, 2024
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
PrevPrevious commit
NextNext commit
add tests
  • Loading branch information
@picnixz
picnixz committedJun 29, 2024
commit262608aff5c9852586ce43f7203e261a0bbc97fd
129 changes: 129 additions & 0 deletionsLib/test/test_ast.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -1130,6 +1130,23 @@ def test_none_checks(self) -> None:
class CopyTests(unittest.TestCase):
"""Test copying and pickling AST nodes."""

@staticmethod
def iter_ast_classes():
"""Iterate over the subclasses of ast.AST recursively.

This excludes the special class ast.Index since its constructor
returns an integer.
"""
def do(cls):
if cls is ast.Index:
return

yield cls
for sub in cls.__subclasses__():
yield from do(sub)

yield from do(ast.AST)

def test_pickling(self):
import pickle

Expand DownExpand Up@@ -1199,6 +1216,118 @@ def test_copy_with_parents(self):
)):
self.assertEqual(to_tuple(child.parent), to_tuple(node))

def test_replace_interface(self):
for klass in self.iter_ast_classes():
with self.subTest(klass=klass):
self.assertTrue(hasattr(klass, '__replace__'))

fields = set(klass._fields)
with self.subTest(klass=klass, fields=fields):
# check shallow copy
node = klass(**dict.fromkeys(fields))
# positional not allowed
self.assertRaises(TypeError, copy.replace, node, 1)
self.assertRaises(TypeError, node.__replace__, 1)

def test_replace_native(self):
for klass in self.iter_ast_classes():
fields = set(klass._fields)
with self.subTest(klass=klass, fields=fields):
# use of object() to ensure that '==' and 'is'
# behave similarly in ast.compare(node, repl)
old_value, new_value = object(), object()

# check shallow copy
node = klass(**dict.fromkeys(fields, old_value))
repl = copy.replace(node)
self.assertTrue(ast.compare(node, repl, compare_attributes=True))

for field in fields:
node = klass(**dict.fromkeys(fields, old_value))
# only change a single field
repl = copy.replace(node, **{field: new_value})
for f in fields:
self.assertIs(getattr(node, f), old_value)
if f != field:
self.assertIs(getattr(repl, f), old_value)
else:
self.assertIs(getattr(repl, f), new_value)

self.assertFalse(ast.compare(node, repl, compare_attributes=True))

def test_replace_accept_known_native_fields(self):
nid, ctx = object(), object()

node = ast.Name(id=nid, ctx=ctx)
self.assertIs(node.id, nid)
self.assertIs(node.ctx, ctx)

new_nid = object()
repl = copy.replace(node, id=new_nid)
# assert independence
self.assertIs(node.id, nid)
self.assertIs(node.ctx, ctx)
# assert changes
self.assertIs(repl.id, new_nid)
self.assertIs(repl.ctx, node.ctx) # no changes

def test_replace_accept_known_native_attributes(self):
node = ast.parse('x').body[0].value
self.assertEqual(node.id, 'x')
self.assertEqual(node.lineno, 1)

# constructor allows any type so replace() should do the same
lineno = object()
repl = copy.replace(node, lineno=lineno)
# assert independence
self.assertEqual(node.lineno, 1)
# check changes
self.assertEqual(repl.id, node.id)
self.assertEqual(repl.ctx, node.ctx)
self.assertEqual(repl.lineno, lineno)

_, _, state = node.__reduce__()
self.assertEqual(state['id'], 'x')
self.assertEqual(state['ctx'], node.ctx)
self.assertEqual(state['lineno'], 1)

_, _, state = repl.__reduce__()
self.assertEqual(state['id'], 'x')
self.assertEqual(state['ctx'], node.ctx)
self.assertEqual(state['lineno'], lineno)

def test_replace_accept_known_custom_fields(self):
nid, ctx = object(), object()

node = ast.Name(id=nid, ctx=ctx)
node.extra = old_extra = object() # add the 'extra' field
self.assertIs(node.extra, old_extra)
# assert shallow copy of extra fields as well
repl = copy.replace(node)
self.assertIs(node.extra, old_extra)
self.assertIs(repl.extra, old_extra)

new_extra = object()
repl = copy.replace(node, extra=new_extra)
self.assertIs(node.id, nid)
self.assertIs(node.ctx, ctx)
self.assertIs(node.extra, old_extra)

self.assertIs(repl.id, nid)
self.assertIs(repl.ctx, ctx)
self.assertIs(repl.extra, new_extra)

def test_replace_reject_unknown_custom_fields(self):
nid, ctx = object(), object()
node = ast.Name(id=nid, ctx=ctx)
with self.assertRaisesRegex(TypeError, "got an unexpected field name: 'extra'"):
# cannot replace a non-existing field
copy.replace(node, extra=1)
# check that we did not have a side-effect
self.assertIs(node.id, nid)
self.assertIs(node.ctx, ctx)
self.assertRaises(AttributeError, getattr, node, 'extra')


class ASTHelpers_Test(unittest.TestCase):
maxDiff = None
Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp