This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author brett.cannon
Recipients Arfrever, brett.cannon, eli.bendersky, eric.snow, ezio.melotti, pitrou
Date 2013-01-27.19:09:33
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1359313773.56.0.425694552304.issue17037@psf.upfronthosting.co.za>
In-reply-to
Content
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?
History
Date User Action Args
2013-01-27 19:09:33brett.cannonsetrecipients: + brett.cannon, pitrou, ezio.melotti, Arfrever, eli.bendersky, eric.snow
2013-01-27 19:09:33brett.cannonsetmessageid: <1359313773.56.0.425694552304.issue17037@psf.upfronthosting.co.za>
2013-01-27 19:09:33brett.cannonlinkissue17037 messages
2013-01-27 19:09:33brett.cannoncreate