[Checkins] SVN: manuelpi.fakemodule/trunk/src/manuelpi/fakemodule/fakemodule. Changes to support martian doctests.

Paul Wilson paulalexwilson at gmail.com
Tue Dec 15 20:01:04 EST 2009


Log message for revision 106603:
  Changes to support martian doctests. 
  
  Principally:
   - Force doctest globs into __builtins__ so they
     are available as a fallback after module scoped
     stuff
   - Remove the aggregation of globs forced into each
     module. This was playing havoc with martian's 
     directive storage facility and causing all 'gets'
     from BoundDirectives to return their defaults.
  
  

Changed:
  U   manuelpi.fakemodule/trunk/src/manuelpi/fakemodule/fakemodule.py
  U   manuelpi.fakemodule/trunk/src/manuelpi/fakemodule/fakemodule.txt

-=-
Modified: manuelpi.fakemodule/trunk/src/manuelpi/fakemodule/fakemodule.py
===================================================================
--- manuelpi.fakemodule/trunk/src/manuelpi/fakemodule/fakemodule.py	2009-12-16 00:55:36 UTC (rev 106602)
+++ manuelpi.fakemodule/trunk/src/manuelpi/fakemodule/fakemodule.py	2009-12-16 01:01:04 UTC (rev 106603)
@@ -8,7 +8,7 @@
         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"
+MODULE_NAMESPACE = "manueltest.fake"
 
 # To store our fake module's lines
 class FakeModuleSnippet(object):
@@ -36,26 +36,73 @@
 
     # build a suitable module
     module_name = region.parsed.module_name
-    full_module_name = MODULE_NS + "." + module_name
+    full_module_name = MODULE_NAMESPACE + "." + module_name
     module = new.module(full_module_name)
     module_name_parts = full_module_name.split('.')
     module.__file__ = '/' + '/'.join(module_name_parts)
 
     # Make the module also available through normal import
-    if not MODULE_NS in sys.modules:
+    if not MODULE_NAMESPACE 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']
 
+    # We want to be able to resolve items that are in the surrounding
+    # doctest globs. To acheive this, we force all doctest globals into
+    # builtins such that they are resolved after module-scoped objects.
+    import __builtin__
+    __builtin__.__dict__.update(doc_globs)
+
     exec region.parsed.code in module.__dict__
-    # XXX Do I want del module['__builtin__']??
 
-    # Make the module visible and usable in the given name
+    # When we exec code within the dictionary namespace as
+    # above, the __module__ attributes of the objects created are set
+    # to __builtin__. We know better than the interpreter in this case
+    # and are able to set the defined namespace accordingly. We 
+    # iterate through and change the relevant attributes:
+    for name in dir(module):
+        if name.startswith('__'):
+            continue
+        obj = getattr(module, name)
+        try:
+            obj = obj.im_func
+        except: 
+            pass
+        __module__ = None
+        try:
+            __module__ = obj.__dict__.get('__module__')
+        except AttributeError:
+            try:
+                __module__ = obj.__module__
+            except AttributeError:
+                pass
+        if __module__ in (None, '__builtin__'):
+            try:
+                obj.__module__ = full_module_name
+            except AttributeError:
+                pass
+        setattr(module, name, obj)
+
+    # Provide correct globals for functions
+    for name in dir(module):
+        if name.startswith('__'):
+            continue
+        obj = getattr(module, name)
+        try:
+            code = obj.func_code
+            new_func = new.function(code, module.__dict__, name)
+            new_func.__module__ = module.__name__
+            setattr(module, name, new_func)
+        except AttributeError:
+            pass
+
+    # Make the module visible and usable in the rest of the doctest
     doc_globs[module_name] =  module
 
     sys.modules[full_module_name] = module
-    setattr(sys.modules['manueltest.fake'], full_module_name.split('.')[-1],
-            module)
+    setattr(sys.modules['manueltest.fake'], 
+                        full_module_name.split('.')[-1],
+                        module)
 
 class Manuel(manuel.Manuel):
     def __init__(self):

Modified: manuelpi.fakemodule/trunk/src/manuelpi/fakemodule/fakemodule.txt
===================================================================
--- manuelpi.fakemodule/trunk/src/manuelpi/fakemodule/fakemodule.txt	2009-12-16 00:55:36 UTC (rev 106602)
+++ manuelpi.fakemodule/trunk/src/manuelpi/fakemodule/fakemodule.txt	2009-12-16 01:01:04 UTC (rev 106603)
@@ -16,6 +16,8 @@
    .. module-block:: test_module
 
          import pprint
+
+         a = manuel
          
          class Something:
             pass
@@ -42,6 +44,8 @@
    .. module-block:: test_module
         import pprint
    <BLANKLINE>
+        a = manuel
+   <BLANKLINE>
         class Something:
            pass
    <BLANKLINE>
@@ -164,6 +168,50 @@
   >>> print glob['cls_n_func'].faz_func.__module__
   manueltest.fake.cls_n_func
 
+Test Continuity
+===============
+
+We also would like to define objects in a doctest and have the
+module use them too. For example, we might want to define a class
+and have a module use it directly::
+
+    >>> class Parent:
+    ...     pass
+
+Let's now create a module that creates instances of this object::
+
+.. module-block:: enclosing_ns_available
+
+    dad = Parent()
+
+Now lets use `dad`::
+    
+    >>> print enclosing_ns_available.dad
+    <....Parent instance at ...>
+    >>> print enclosing_ns_available.dad.__module__
+    manueltest.fake.enclosing_ns_available
+
+However, we would also like to have module globals equally available
+to local scopes. Consider the case that a class relies upon some 
+module level utility function::
+
+.. module-block:: module_globals
+
+    foobar = True
+
+    def utility_func():
+        return foobar
+
+    class DelegatingClass(object):
+        def __init__(self):
+            self.v = utility_func()
+
+The class should be able to use the utlilty function properly:
+
+    >>> dc = module_globals.DelegatingClass()
+    >>> dc.v
+    True
+
 Invalid Modules
 ===============
 



More information about the checkins mailing list