Brett Cannon added the comment: OK, let's take a step back here and look at what exactly we are trying to simplify (which is now the updated example in PEP 399)::
from test.support import import_fresh_module import unittest c_heapq = import_fresh_module('heapq', fresh=['_heapq']) py_heapq = import_fresh_module('heapq', blocked=['_heapq']) class ExampleTest: def test_example(self): self.assertTrue(hasattr(self.module, 'heapify')) class PyExampleTest(ExampleTest, unittest.TestCase): module = py_heapq @unittest.skipUnless(c_heapq, 'requires the C _heapq module') class CExampleTest(ExampleTest, unittest.TestCase): module = c_heapq if __name__ == '__main__': unittest.main() Ignoring the unittest.main() boilerplate, we have two import_fresh_module() calls and the creation of two subclasses of ExampleTest, both of which inherit from unittest.TestCase and one of which is skipped if the acceleration code is lacking. So there is some boilerplate. The question is whether a solution be made that isn't too magical to minimize this code. In my head I see this becoming something more like:: from test.support import PEP399Tests pep399_tests = PEP399Tests('heapq', '_heapq') class ExampleTest: def test_example(self): self.assertTrue(hasattr(self.heapq, 'heapify')) PyExampleTest, CExampleTest = pep399_tests.create_test_cases(ExampleTest) if __name__ == '__main__': unittest.main() This would cut out the import_fresh_module() calls (which don't need to be injected globally as you should be accessing the code through the test class' attribute storing the module and if you don't care what version you get you just do a standard imoprt), remembering to subclass unittest.TestCase, and to skip the accelerated version of the tests if it isn't needed. Basically it goes from 7 lines to 2 with not repetitious lines. You could make this a decorator, but honestly it's the same number of lines without the magic of mucking with a module's globals by getting at them through sys.modules (the decorator really only saves you from typing the variable names to hold the new test classes and the test class argument to begin with). And the implementation should be relatively straight-forward (thanks to Eric having done most of the thinking on what needs to happen):: class PEP399Tests: # Using keyword-only arguments you could go as far as allowing for # customizing the attribute name to set, class name prefixes, etc. def __init__(module_name, *accelerated_names): self.module_name = module_name self.py_module = import_fresh_module(module_name, fresh=accelerated_names) self.accelerated_module = import_fresh_module(module_name, block=accelerated_names) def create_test_cases(self, test_class): class PyTestCase(test_class, unittest.TestCase): pass PyTestCase.__name__ = 'Py' + test_class.__name__ setattr(PyTestCase, self.module_name, self.py_module) if self.accelerated_module is None: AcceleratedTestCase = None else: class AcceleratedTestCase(test_class, unittest.TestCase): pass AcceleratedTestCase.__name__ = 'Accelerated' + test_class.__name__ setattr(AcceleratedTestCase, self.module_name, self.accelerated_module) return PyTestCase, AcceleratedTestCase Does this approach seem more reasonable to people in terms of cutting down boilerplate without being too magical? ---------- _______________________________________ Python tracker <rep...@bugs.python.org> <http://bugs.python.org/issue17037> _______________________________________ _______________________________________________ Python-bugs-list mailing list Unsubscribe: http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com