From 2638a76595e073c56f1992bddf85ae5eb49925fd Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Mon, 29 Jul 2019 02:57:14 -0700 Subject: [PATCH 1/7] Fix lazy package loads --- CHANGELOG | 6 ++++++ lazy_import/VERSION | 2 +- lazy_import/__init__.py | 17 +++++++++-------- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 84fac4e..3ed3f5a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,9 @@ +29/07/2019 v0.2.4 + - 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. ??/??/?? v0.2.3 - More informative error when inadvertently trying to use a lazy callable a baseclass (Issue #2). diff --git a/lazy_import/VERSION b/lazy_import/VERSION index 6b2eab2..2c9b9e7 100644 --- a/lazy_import/VERSION +++ b/lazy_import/VERSION @@ -1 +1 @@ -0.2.3-dev +0.2.4-dev diff --git a/lazy_import/__init__.py b/lazy_import/__init__.py index 4914ab1..8ebafc5 100644 --- a/lazy_import/__init__.py +++ b/lazy_import/__init__.py @@ -375,7 +375,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] @@ -533,7 +535,7 @@ def _load_module(module): cached_data = _clean_lazymodule(module) try: # Get Python to do the real import! - reload_module(module) + reload_module(module) except: # Loading failed. We reset our lazy state. logger.debug("Failed to load module {}. Resetting..." @@ -544,7 +546,7 @@ def _load_module(module): # 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{}: {}" @@ -679,17 +681,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) From 532091a3614772c6e3e6a8f8c48b3691bd233410 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Mon, 29 Jul 2019 03:01:26 -0700 Subject: [PATCH 2/7] Don't bump version --- lazy_import/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lazy_import/VERSION b/lazy_import/VERSION index 2c9b9e7..6b2eab2 100644 --- a/lazy_import/VERSION +++ b/lazy_import/VERSION @@ -1 +1 @@ -0.2.4-dev +0.2.3-dev From 235769e513ceff2c5ceca7f10f9c463e54f69e58 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Mon, 29 Jul 2019 03:02:25 -0700 Subject: [PATCH 3/7] fix changelog --- CHANGELOG | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 3ed3f5a..cb3ac1a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,10 +1,9 @@ -29/07/2019 v0.2.4 +??/??/?? 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. -??/??/?? v0.2.3 - 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. From 61d8c26fa5acaf414b4d1d67dfefc54600f87b90 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Mon, 29 Jul 2019 03:26:46 -0700 Subject: [PATCH 4/7] Attempt fix of version conflict --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 31a51aa..b2f1835 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ 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', version=version, From 578c72f34918ccc0412c629f70fd57f590f50569 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Tue, 30 Jul 2019 00:21:22 -0700 Subject: [PATCH 5/7] Set tag --- lazy_import/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lazy_import/VERSION b/lazy_import/VERSION index 6b2eab2..3988334 100644 --- a/lazy_import/VERSION +++ b/lazy_import/VERSION @@ -1 +1 @@ -0.2.3-dev +0.2.3-dev0 From 87f42ad45e4d7ee9236734dd8e006d570b287a4b Mon Sep 17 00:00:00 2001 From: matthewphsmith Date: Tue, 17 Sep 2019 14:49:32 -0700 Subject: [PATCH 6/7] Fix Errors on Relative Imports of Lazy Packages (#1) reload does not have the desired behavior. Since when the import is triggered it is actually the first import, it is preferable to use the importlib. --- lazy_import/VERSION | 2 +- lazy_import/__init__.py | 22 ++++++++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/lazy_import/VERSION b/lazy_import/VERSION index 3988334..4f5e9fb 100644 --- a/lazy_import/VERSION +++ b/lazy_import/VERSION @@ -1 +1 @@ -0.2.3-dev0 +0.2.4-dev0 diff --git a/lazy_import/__init__.py b/lazy_import/__init__.py index 8ebafc5..915776c 100644 --- a/lazy_import/__init__.py +++ b/lazy_import/__init__.py @@ -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 @@ -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__"))) @@ -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 @@ -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__"))) @@ -512,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, @@ -533,14 +540,18 @@ 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 @@ -637,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: @@ -670,8 +678,6 @@ def _reset_lazymodule(module, cls_attrs): """ modclass = type(module) - del modclass.__getattribute__ - del modclass.__setattr__ try: del modclass._LOADING except AttributeError: From 4587cea64e933e0dd40b91ea909ad729cf79b523 Mon Sep 17 00:00:00 2001 From: matthewphsmith Date: Tue, 17 Sep 2019 15:12:03 -0700 Subject: [PATCH 7/7] Update setup.py for release (#2) Names --- setup.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index b2f1835..89b7fe7 100644 --- a/setup.py +++ b/setup.py @@ -10,15 +10,15 @@ 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://github.com/mnmelo/lazy_import', - author='Manuel Nuno Melo', - author_email='manuel.nuno.melo@gmail.com', + url='https://github.com/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',