[Checkins] SVN: manuel/branches/pwilson-try-fake-modules/src/manuel/ Initial proof-of-concept attempt at fake module support.

Paul Wilson paulalexwilson at gmail.com
Fri Oct 23 06:10:22 EDT 2009


Log message for revision 105238:
  Initial proof-of-concept attempt at fake module support. 
  
  Added code and tests to allow modules to be defined within
  a doctest. These modules are created and imported into sys.modules
  and available to the rest of the doctest by some defined name.
  

Changed:
  A   manuel/branches/pwilson-try-fake-modules/src/manuel/fake_module.py
  A   manuel/branches/pwilson-try-fake-modules/src/manuel/fake_module.txt
  U   manuel/branches/pwilson-try-fake-modules/src/manuel/tests.py

-=-
Added: manuel/branches/pwilson-try-fake-modules/src/manuel/fake_module.py
===================================================================
--- manuel/branches/pwilson-try-fake-modules/src/manuel/fake_module.py	                        (rev 0)
+++ manuel/branches/pwilson-try-fake-modules/src/manuel/fake_module.py	2009-10-23 10:10:21 UTC (rev 105238)
@@ -0,0 +1,55 @@
+import manuel
+import re
+import sys
+import new
+import textwrap
+
+FAKE_MODULE_START = re.compile(
+        r'^\.\.\s*module-block::?\s*(?P<module_name>[a-zA-Z_]*)',
+        re.MULTILINE)
+FAKE_MODULE_END = re.compile(r'(\n\Z|\n(?=\S))')
+MODULE_NS = "manueltest.fake"
+
+# To store our fake module's lines
+class FakeModuleSnippet(object):
+    def __init__(self, code, module_name):
+        self.code = code
+        self.module_name = module_name
+
+def find_fakes(document):
+    for region in document.find_regions(FAKE_MODULE_START, FAKE_MODULE_END):
+        module_name = region.start_match.group('module_name')
+        # Sanitise indentation
+        source = textwrap.dedent('\n'.join(region.source.splitlines()[1:]))
+        source_location = '%s:%d' % (document.location, region.lineno)
+
+        code = compile(source, source_location, 'exec')
+        document.claim_region(region)
+        region.parsed = FakeModuleSnippet(code, module_name)
+
+def execute_into_module(region, document, doc_globs):
+    # build a suitable module
+    module_name = region.parsed.module_name
+    full_module_name = MODULE_NS + "." + module_name
+    module = new.module(full_module_name)
+    module_name_parts = full_module_name.split('.')
+    module.__file__ = '/' + '/'.join(module_name_parts)
+
+    exec region.parsed.code in module.__dict__
+
+    # Make the module visible and usable in the given name
+    doc_globs[module_name] =  module
+
+    # Make the module also available through normal import
+    if not MODULE_NS in sys.modules:
+        sys.modules['manueltest'] = new.module('manueltest')
+        sys.modules['manueltest.fake'] = new.module('manueltest.fake')
+        sys.modules['manueltest'].fake = sys.modules['manueltest.fake']
+
+    sys.modules[full_module_name] = module
+    setattr(sys.modules['manueltest.fake'], full_module_name.split('.')[-1],
+            module)
+
+class Manuel(manuel.Manuel):
+    def __init__(self):
+        manuel.Manuel.__init__(self, [find_modules], [execute_into_module])


Property changes on: manuel/branches/pwilson-try-fake-modules/src/manuel/fake_module.py
___________________________________________________________________
Added: svn:eol-style
   + native

Added: manuel/branches/pwilson-try-fake-modules/src/manuel/fake_module.txt
===================================================================
--- manuel/branches/pwilson-try-fake-modules/src/manuel/fake_module.txt	                        (rev 0)
+++ manuel/branches/pwilson-try-fake-modules/src/manuel/fake_module.txt	2009-10-23 10:10:21 UTC (rev 105238)
@@ -0,0 +1,113 @@
+manuel.fake_modules
+===================
+
+This document discusses the fake_module plugin for manuel that allows 
+doctests to define and then use modules. 
+
+   >>> import manuel
+   >>> import manuel.fake_module
+
+   >>> source = """\
+   ... This is a module doc-test:
+   ...
+   ... .. module-block:: fake_module
+   ...      import whatever
+   ...      class Something:
+   ...         pass
+   ...
+   ... As you can see, it does much!!
+   ... """
+   
+   >>> doc = manuel.Document(source)
+   >>> manuel.fake_module.find_fakes(doc)
+
+Let's extract the module name and its source:
+
+   >>> for region in doc:
+   ...    if region.parsed:
+   ...       print region.parsed.module_name
+   ...       print region.source
+   fake_module
+   .. module-block:: fake_module
+        import whatever
+        class Something:
+           pass
+   <BLANKLINE>
+   <BLANKLINE>
+
+The code can correctly parse multiple modules:
+
+   >>> source = """\
+   ... Our system has a few modules:
+   ...
+   ... .. module-block:: some_module
+   ...      class Foo:
+   ...         pass
+   ...
+   ... and:
+   ... 
+   ... .. module-block:: an_other_module
+   ...      class Bar:
+   ...          pass
+   ...
+   ... fin
+   ... """
+
+   >>> doc = manuel.Document(source)
+   >>> manuel.fake_module.find_fakes(doc)
+   
+Again, we extract the various parsed regions:
+
+   >>> for region in doc:
+   ...     if region.parsed:
+   ...         print region.parsed.module_name
+   ...         print region.source
+   some_module
+   .. module-block:: some_module
+        class Foo:
+           pass
+   <BLANKLINE>
+   <BLANKLINE>
+   an_other_module
+   .. module-block:: an_other_module
+        class Bar:
+            pass
+   <BLANKLINE>
+   <BLANKLINE>
+
+Let's execute these regions into a globs dictionary of our own:
+
+  >>> glob = {}
+  >>> for region in doc:
+  ...     if region.parsed:
+  ...         manuel.fake_module.execute_into_module(
+  ...                            region, doc, glob)
+
+Let's check that our glob contains the modules with their names:
+
+  >>> for module in glob.values(): 
+  ...     print module.__name__, ":", type(module)
+  manueltest.fake.some_module : <type 'module'>
+  manueltest.fake.an_other_module : <type 'module'>
+
+Let's also confirm that the modules contain their respective class
+definitions:
+
+  >>> for module in glob.values():
+  ...     print dir(module)
+  ['Foo', '__builtins__', '__doc__', '__file__', '__name__']
+  ['Bar', '__builtins__', '__doc__', '__file__', '__name__']
+
+We would also want to ensure that we can import the module correctly 
+too. The fake_module system also adds the modules under their own 
+namespace 'manueltest.fake', as you can see from the previous tests:
+
+  >>> import manueltest.fake.some_module
+  >>> import manueltest.fake.an_other_module
+  >>> manueltest.fake.some_module.Foo
+  <class manueltest.fake.some_module.Foo at ...>
+  >>> manueltest.fake.an_other_module.Bar
+  <class manueltest.fake.an_other_module.Bar at ...>
+
+Note: There is no checking done to ensure that the module definition
+hasn't overridden any previous modules defined in the doctest.


Property changes on: manuel/branches/pwilson-try-fake-modules/src/manuel/fake_module.txt
___________________________________________________________________
Added: svn:eol-style
   + native

Modified: manuel/branches/pwilson-try-fake-modules/src/manuel/tests.py
===================================================================
--- manuel/branches/pwilson-try-fake-modules/src/manuel/tests.py	2009-10-23 09:57:34 UTC (rev 105237)
+++ manuel/branches/pwilson-try-fake-modules/src/manuel/tests.py	2009-10-23 10:10:21 UTC (rev 105238)
@@ -32,7 +32,7 @@
         ])
 
     tests = ['../index.txt', 'table-example.txt', 'README.txt', 'bugs.txt',
-        'capture.txt']
+        'capture.txt', 'fake_module.txt']
 
     m = manuel.ignore.Manuel()
     m += manuel.doctest.Manuel(optionflags=optionflags, checker=checker)



More information about the checkins mailing list