Lazy Loading¶
The current implementation of kalasiris uses a Factory Pattern to build a function for each ISIS program that it finds on the system.
You might consider that inelegant, as it will build a function for
each of the 300+ ISIS programs, even if you only use one. The
kalasiris.py
and __init__.py
files can be modified to not build
this long list of functions, but to only lazy-load the right function
when it is asked for. The test implementation described below
describes that, but it isn’t particularly faster nor does it use
less resources (and it makes the code harder to read).
Perhaps this is because there is extra overhead involved to instantiate
a class, and leverage its exception mechanism when an attribute
call tries to call a function that doesn’t exist. Then it runs
the Factory to build it on demand. We also then need to use
sys.module
calls to bootstrap the namespace correctly up to the
top level.
Gedanken Implementation¶
Well, it isn’t really a ‘thought implementation,’ a test implementation was built and lightly tested.
Make a kalasirisModule(types.ModuleType)
class that encapsulates
all of the loose functions in kalasiris.py
. And delete the for
p in _get_isis_program_names()
loop.
The functions all need to have their signatures modified to take
self
, but you should probably @staticmethod param_fmt()
. Remove
the setattr()
call and have _build_isis_fn()
return the built
isis_fn()
. Optionally experiment with leaving it in, so that
once the factory is triggered to build a function, it stays in the
module. Otherwise every time an ISIS function is called , the
Factory would have to build it again.
Then build a function that looks like this:
def __getattr__(self, name):
print('Got to overridden __getattr__ !')
if name in self._get_isis_program_names():
# remember to rewrite _build_isis_fn() to return its isis_fn()
return(self._build_isis_fn(name))
else:
raise AttributeError
This is part of the the magic. The kalasirisModule
class’s
__getattr__()
gets called when something tries to access an
attribute that doesn’t exist on the class. This then runs the
Factory to make the ISIS function on the spot.
Finally after you close the class, add this line:
sys.modules[__name__] = kalaModule(__name__)
This pattern is a sanctioned hack of the import system. This
sys.modules[] assignment within kalasiris.py
basically replaces
the namespace of the file with the namespce of the kalasirisModule()
class, which enables it to receive the call to __getattr__()
if
it were loaded directly.
Of course, given our structure, we want to leverage __init__.py
to load kalasiris.py
and the other files to present a unified
whole when someone loads the whole package.
To do that, we need to use the sys.modules[]
hack to bootstrap
the namespace up another level.
So in in __init__.py, add this at the very top:
from sys import modules
And then this after the from ... import
statements:
modules[__name__] = kalasiris
setattr(modules[__name__], 'Histogram', Histogram)
setattr(modules[__name__], 'PathSet', PathSet)
Setting modules[__name__]
brings up the namespace up a level, but
then masks the imports of Histogram and PathSet from being
exposed, so you need to setattr them to add them back to the namespace
that is exposed when someone loads the package. Clearly, something
more elegant than a bunch of individual setattr()
calls, but this
gets the point across.