Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Lazy Package Loads #14

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
??/??/?? v0.2.3
- Ensure that when a package of a lazy-loaded module is loaded, the
lazy-loaded module remains an attribute of the newly loaded package.
- Ensure that when two lazy-loaded modules exist in the same lazy package,
the second (and all subsequent) lazy-modules are also added to the lazy
packages list of lazy sub-modules.
- More informative error when inadvertently trying to use a lazy callable a
baseclass (Issue #2).
- Extra level of debugging (trace debugging), toggled via lazy_import.logger.
Expand Down
2 changes: 1 addition & 1 deletion lazy_import/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.2.3-dev
0.2.4-dev0
37 changes: 22 additions & 15 deletions lazy_import/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def __exit__(self, exc_type, exc_value, exc_traceback):

import six
from six import raise_from
from six.moves import reload_module
from importlib import import_module
# It is sometime useful to have access to the version number of a library.
# This is usually done through the __version__ special attribute.
# To make sure the version number is consistent between setup.py and the
Expand Down Expand Up @@ -127,6 +127,9 @@ class LazyModule(ModuleType):
# peak.util.imports sets __slots__ to (), but it seems pointless because
# the base ModuleType doesn't itself set __slots__.
def __getattribute__(self, attr):
if hasattr(type(self), '_loaded_module'):
return getattr(type(self)._loaded_module, attr)

logger.debug("Getting attr {} of LazyModule instance of {}"
.format(attr, super(LazyModule, self)
.__getattribute__("__name__")))
Expand All @@ -142,6 +145,7 @@ def __getattribute__(self, attr):
"inspection.".format(super(LazyModule, self)
.__getattribute__("__name__"), attr))
raise AttributeError

if not attr in ('__name__','__class__','__spec__'):
# __name__ and __class__ yield their values from the LazyModule;
# __spec__ causes an AttributeError. Maybe in the future it will be
Expand Down Expand Up @@ -170,6 +174,8 @@ def __getattribute__(self, attr):
return super(LazyModule, self).__getattribute__(attr)

def __setattr__(self, attr, value):
if hasattr(type(self), '_loaded_module'):
return setattr(type(self)._loaded_module, attr, value)
logger.debug("Setting attr {} to value {}, in LazyModule instance "
"of {}".format(attr, value, super(LazyModule, self)
.__getattribute__("__name__")))
Expand Down Expand Up @@ -375,7 +381,9 @@ def __repr__(self):
if fullsubmodname:
submod = sys.modules[fullsubmodname]
ModuleType.__setattr__(mod, submodname, submod)
_LazyModule._lazy_import_submodules[submodname] = submod
if issubclass(type(mod), LazyModule):
type(mod)._lazy_import_submodules[submodname] = submod

fullsubmodname = modname
modname, _, submodname = modname.rpartition('.')
return sys.modules[fullmodname]
Expand Down Expand Up @@ -510,7 +518,8 @@ def _load_module(module):
if not issubclass(modclass, LazyModule):
raise TypeError("Passed module is not a LazyModule instance.")
with _ImportLockContext():
parent, _, modname = module.__name__.rpartition('.')
full_name = module.__name__
parent, _, modname = full_name.rpartition('.')
logger.debug("loading module {}".format(modname))
# We first identify whether this is a loadable LazyModule, then we
# strip as much of lazy_import behavior as possible (keeping it cached,
Expand All @@ -531,20 +540,24 @@ def _load_module(module):
# We've been loaded by the parent. Let's bail.
return
cached_data = _clean_lazymodule(module)
cached_module = sys.modules.pop(full_name)
try:
# Get Python to do the real import!
reload_module(module)
lazy_loaded_module = import_module(full_name)
setattr(modclass, '_loaded_module', lazy_loaded_module)
sys.modules[full_name] = module
except:
# Loading failed. We reset our lazy state.
logger.debug("Failed to load module {}. Resetting..."
.format(modname))
_reset_lazymodule(module, cached_data)
sys.modules[full_name] = cached_module
raise
else:
# Successful load
logger.debug("Successfully loaded module {}".format(modname))
delattr(modclass, '_LOADING')
_reset_lazy_submod_refs(module)
_reset_lazy_submod_refs(module, cached_data)

except (AttributeError, ImportError) as err:
logger.debug("Failed to load {}.\n{}: {}"
Expand Down Expand Up @@ -635,9 +648,6 @@ def _clean_lazymodule(module):
"""
modclass = type(module)
_clean_lazy_submod_refs(module)

modclass.__getattribute__ = ModuleType.__getattribute__
modclass.__setattr__ = ModuleType.__setattr__
cls_attrs = {}
for cls_attr in _CLS_ATTRS:
try:
Expand Down Expand Up @@ -668,8 +678,6 @@ def _reset_lazymodule(module, cls_attrs):

"""
modclass = type(module)
del modclass.__getattribute__
del modclass.__setattr__
try:
del modclass._LOADING
except AttributeError:
Expand All @@ -679,17 +687,16 @@ def _reset_lazymodule(module, cls_attrs):
setattr(modclass, cls_attr, cls_attrs[cls_attr])
except KeyError:
pass
_reset_lazy_submod_refs(module)
_reset_lazy_submod_refs(module, cls_attrs)


def _reset_lazy_submod_refs(module):
modclass = type(module)
def _reset_lazy_submod_refs(module, cls_attrs):
for deldict in _DELETION_DICT:
try:
resetnames = getattr(modclass, deldict)
resetnames = cls_attrs[deldict]
except AttributeError:
continue
for name, submod in resetnames.items():
for name, submod in resetnames.items():
super(LazyModule, module).__setattr__(name, submod)


Expand Down
12 changes: 6 additions & 6 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@
with open('lazy_import/VERSION') as infile:
version = infile.read().strip()

tests_require = ['pytest', 'pytest-xdist']
tests_require = ['pytest>=4.4.0', 'pytest-xdist']

setup(name='lazy_import',
setup(name='sloth_import',
version=version,
description='A module for lazy loading of Python modules',
long_description=readme,
url='https:/mnmelo/lazy_import',
author='Manuel Nuno Melo',
author_email='manuel.nuno.melo@gmail.com',
url='https:/matthewphsmith/lazy_import',
author='Manuel Nuno Melo, Matthew Smith',
author_email='matthew_smith@outlook.com',
license='GPL',
platforms = ["any"],
platforms=["any"],
classifiers=['Development Status :: 4 - Beta',
# Indicate who your project is intended for
'Intended Audience :: Developers',
Expand Down