I think I need to explain the title, not really sure how accurately title this post. Here it is a quick code:
from my_module import foobar
Where foobar is not defined in my_module module, but the code will not raise ImportError. So, its a module whose attributes can be imported and which does not exist within it.
I did not get this idea on my own and I dont know who firstly got this interesting idea. I saw this in a project called pbs, a Python subprocess wrapper. (How abbreviated as pbs?) It is written by Andrew Moffat and licensed under the MIT License.
To be honest, I am not interested in what this project can do, I dont feel writing Python script for easy command-line access would be more productive or easier than scripting in shell script. But thats not the point of this post and that code has many to learn from even I dont use.
I found this project via GitHub Explore, the part of being able to import any commands caught my eyes and I was thinking: this only main code pbs.py must be hell long like traffic in <INSERT YOUR CITY>. When I clicked in, it had only around 450 lines and most of them were comments. So, I took a close look at it.
To strip it down, it could be just like (for Python 3.x and 2.x)
#############
# main script
#############
from my_module import attr1
print('Value of attr1: %s' % attr1)
print('')
from my_module import attr2
print('Value of attr2: %s' % attr2)
##############
# my_module.py
##############
import sys
attr1 = 123
class SelfWrapper(object):
def __init__(self, self_module):
self.self_module = self_module
def __getattr__(self, name):
print('%s is being imported:' % name)
if hasattr(self.self_module, name):
print(' found in self_module')
return getattr(self.self_module, name)
elif name.startswith('__'):
print(' starts with __ and not found, raising AttributeError')
raise AttributeError
print(' returns a generated string')
return 'automatically generated ' + name
sys.modules[__name__] = SelfWrapper(sys.modules[__name__])
Where attr1 is defined, but attr2 is not. The output is
__path__ is being imported: starts with __ and not found, raising AttributeError attr1 is being imported: found in self_module Value of attr1: 123 __path__ is being imported: starts with __ and not found, raising AttributeError attr2 is being imported: returns a generated string Value of attr2: automatically generated attr2
If you dont check for __xyz__, __path__ in this case, __getattr__ will be invoked twice for attr1 and attr2. I can not answer the behind scene of module import for this __path__ part, feel free to educate me in comments.
The project is great for this idea, however I would say its fancy but not really necessary. I see no much trouble of using like Cmd.curl, Cmd.wget, etc. But its awesome to use import like that. Cool, right?