1

I have this module, wat.py

import re
import types
import sys

hello = "Hello World"

class MyModule(types.ModuleType):
    def get_re(self):
        return re
    def get_hello(self):
        return hello


sys.modules[__name__] = MyModule('wat')

And I run this code:

>>> import wat
>>> wat.get_hello()
None
>>> wat.get_re()
None

Why does this not work?

Eric
  • 95,302
  • 53
  • 242
  • 374
  • If I were you, I would start with a more general python tutorial... (http://docs.python.org/tutorial/modules.html#packages) – gecco Oct 08 '12 at 08:49
  • @gecco: I'm not new to python. I'm trying to make a module callable, but the imports are going awry. – Eric Oct 08 '12 at 08:50
  • ok, sorry, I got something wrong... – gecco Oct 08 '12 at 08:52
  • http://stackoverflow.com/questions/1060796/callable-modules – root Oct 08 '12 at 08:55
  • @root: I was looking at that to begin with, but found it didn't play well with imports – Eric Oct 08 '12 at 08:57
  • 1
    If you set a breakpoint with `import pdb; pdb.set_trace()` in the `get_hello()` method and inspect the locals and globals, you'll notice that `hello` is `None` (it's defined though). I'm not exactly sure why this is happening, but that seems to be the root cause of your problem. – Lukas Graf Oct 08 '12 at 08:58

3 Answers3

1

This code makes it work:

import types
import sys

def whyDoesThisWorkIDontEven():
    import re
    hello = "Hello World"

    class MyModule(types.ModuleType):
        def get_re(self):
            return re
        def get_hello(self):
            return hello

    return MyModule('wat')

sys.modules[__name__] = whyDoesThisWorkIDontEven()

But I have absolutely no idea why.

Eric
  • 95,302
  • 53
  • 242
  • 374
  • I think this makes it work because Python sets all the variables to `None` in a module when it's deleted as stated in the accepted answer to the linked question in my answer. Actually it would be more accurately to say it (only) sets all the top-level module attributes in its `__dict__` to `None` -- so what you've done here effectively protects things by moving them down a level to inside the function. This makes it work but doesn't answer the question. – martineau Oct 08 '12 at 22:31
1

Not sure what you mean by "doesn't play well with imports" as this seems to work also. Not sure if thats what you want but maybe it's useful...

    import sys
    import re
    class _Test(object):
      def __init__(self):
          self.re=re
      def testfunc(self):
          return self.re
      y = property(testfunc)
    sys.modules[__name__] = _Test()

import calltest

>>> calltest.y
<module 're' from 'C:\Python26\lib\re.pyc'>
>>> calltest.re
<module 're' from 'C:\Python26\lib\re.pyc'>
>>> calltest.testfunc()
<module 're' from 'C:\Python26\lib\re.pyc'>

EDIT:

If you simply try to return re you will get None. You have to import re after you do

sys.modules[__name__] = _Test()

like:

sys.modules[__name__] = _Test()
import re

then simply calling re woudld work.

root
  • 76,608
  • 25
  • 108
  • 120
  • Have an upvote for a working solution. But why is `re` in scope in `__init__`, but not in `testfunc`? – Eric Oct 08 '12 at 09:26
1

It doesn't work because you effectively deleted your module when you reassigned its entry in sys.modules. See my related question.

To make it work, change the last line of your module to:

_ref, sys.modules[__name__] = sys.modules[__name__], MyModule('wat')

and it will work.

BTW, you don't have to derive your class from types.ModuleType in order to put instances of it in sys.modules[]. Entries in sys.modules don't have to be module objects (according to Alex Martelli).

Community
  • 1
  • 1
martineau
  • 119,623
  • 25
  • 170
  • 301
  • _"you don't have to derive your class from `types.ModuleType`"_ - true, but I see no reason not to. – Eric Oct 09 '12 at 08:10
  • @Eric: Depends on what your class is or does, as in is it a type (or subclass) of a module, or is it something else altogether. – martineau Oct 09 '12 at 10:07