From fcb0a6071b1263b455ac4115e94ba5a9672afab5 Mon Sep 17 00:00:00 2001 From: Allan Feldman <> Date: Sat, 28 Apr 2018 14:24:08 -0700 Subject: [PATCH] bpo-33380: Update module attribute on namedtuple methods. This commit updates the __module__ attribute on namedtuple methods: * __new__ * _make.__func__ * _replace * __repr__ * _asdict * __getnewargs__ The __module__ attribute on namedtuple methods should now match the __module__ attribute on the generated namedtuple class. --- Lib/collections/__init__.py | 25 +++++++++++++------------ Lib/test/test_collections.py | 3 +++ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index 3109054e20..e06669ee8f 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -431,11 +431,21 @@ def namedtuple(typename, field_names, *, rename=False, defaults=None, module=Non 'Return self as a plain tuple. Used by copy and pickle.' return tuple(self) - # Modify function metadata to help with introspection and debugging + # Extract the module name. Bypass this step in environments where + # sys._getframe is not defined (Jython for example) or sys._getframe is not + # defined for arguments greater than 0 (IronPython), or where the user has + # specified a particular module. + if module is None: + try: + module = _sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + module = f'namedtuple_{typename}' + # Modify function metadata to help with introspection and debugging for method in (__new__, _make.__func__, _replace, __repr__, _asdict, __getnewargs__): method.__qualname__ = f'{typename}.{method.__name__}' + method.__module__ = module # Build-up the class namespace dictionary # and use type() to build the result class @@ -464,17 +474,8 @@ def namedtuple(typename, field_names, *, rename=False, defaults=None, module=Non result = type(typename, (tuple,), class_namespace) # For pickling to work, the __module__ variable needs to be set to the frame - # where the named tuple is created. Bypass this step in environments where - # sys._getframe is not defined (Jython for example) or sys._getframe is not - # defined for arguments greater than 0 (IronPython), or where the user has - # specified a particular module. - if module is None: - try: - module = _sys._getframe(1).f_globals.get('__name__', '__main__') - except (AttributeError, ValueError): - pass - if module is not None: - result.__module__ = module + # where the named tuple is created. + result.__module__ = module return result diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index 2099d236d0..86840942bc 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -209,6 +209,9 @@ class TestNamedTuple(unittest.TestCase): self.assertEqual(Point.__name__, 'Point') self.assertEqual(Point.__slots__, ()) self.assertEqual(Point.__module__, __name__) + self.assertEqual(Point._make.__module__, __name__) + self.assertEqual(Point._asdict.__module__, __name__) + self.assertEqual(Point._replace.__module__, __name__) self.assertEqual(Point.__getitem__, tuple.__getitem__) self.assertEqual(Point._fields, ('x', 'y')) -- 2.17.0