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.