From f6d3e8eaef83c82416c2ca9d639ed59beba1f877 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Wed, 23 Mar 2011 20:33:30 -0700 Subject: [PATCH] Add tests for _source to importable and exec'able. Move __name__ back out of the template; the responsibility for setting __name__ lies with the caller (which knows something about the new namespace), not with the class definition (which doesn't know about the namespace it is being built in). --- Lib/collections/__init__.py | 7 +++---- Lib/test/test_collections.py | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index 5f11f6a4f4f..122dfb6be30 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -234,8 +234,6 @@ class OrderedDict(dict): ################################################################################ _class_template = '''\ -__name__ = 'namedtuple_{typename}' - from builtins import property as _property, tuple as _tuple from operator import itemgetter as _itemgetter from collections import OrderedDict @@ -353,8 +351,9 @@ def namedtuple(typename, field_names, verbose=False, rename=False): for index, name in enumerate(field_names)) ) - # Execute the class definition string in a temporary namespace - namespace = {} + # Execute the template string in a temporary namespace and + # support tracing utilities by setting a value for frame.f_globals['__name__'] + namespace = dict(__name__='namedtuple_%s' % typename) try: exec(class_definition, namespace) except SyntaxError as e: diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index cdc7db9a7f8..4ef27ce2c50 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -1,6 +1,7 @@ """Unit tests for collections.py.""" import unittest, doctest, operator +from test.support import TESTFN, forget, unlink import inspect from test import support from collections import namedtuple, Counter, OrderedDict, _count_elements @@ -327,6 +328,39 @@ class TestNamedTuple(unittest.TestCase): pass self.assertEqual(repr(B(1)), 'B(x=1)') + def test_source(self): + # verify that _source can be run through exec() + tmp = namedtuple('Color', 'red green blue') + self.assertNotIn('Color', globals()) + exec(tmp._source, globals()) + self.assertIn('Color', globals()) + c = Color(10, 20, 30) + self.assertEqual((c.red, c.green, c.blue), (10, 20, 30)) + self.assertEqual(Color._fields, ('red', 'green', 'blue')) + + def test_source_importable(self): + tmp = namedtuple('Color', 'hue sat val') + + compiled = None + source = TESTFN + '.py' + with open(source, 'w') as f: + print(tmp._source, file=f) + + if TESTFN in sys.modules: + del sys.modules[TESTFN] + try: + mod = __import__(TESTFN) + compiled = mod.__file__ + Color = mod.Color + c = Color(10, 20, 30) + self.assertEqual((c.hue, c.sat, c.val), (10, 20, 30)) + self.assertEqual(Color._fields, ('hue', 'sat', 'val')) + finally: + forget(TESTFN) + if compiled: + unlink(compiled) + unlink(source) + ################################################################################ ### Abstract Base Classes