From bdcf7cc40d17e307882291566e3ba577b02760e4 Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Tue, 16 Jan 2018 09:54:14 -0500 Subject: [PATCH] lazy load modules --- pipenv/cli.py | 10 +++++ pipenv/vendor/lazyload/__init__.py | 61 ++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 pipenv/vendor/lazyload/__init__.py diff --git a/pipenv/cli.py b/pipenv/cli.py index 81bf29ec..dc3294ef 100644 --- a/pipenv/cli.py +++ b/pipenv/cli.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + import contextlib import codecs import os @@ -10,6 +11,15 @@ import tempfile from glob import glob import json as simplejson +import lazyload +for module in [ + 'urllib3', 'background', 'dotenv', 'delegator', 'pexpect', + 'requests', 'pip', 'pipfile', 'pipdeptree', 'requirements', + 'semver', 'flake8', 'pipreqs', 'blindspin', 'click_didyoumean', + '.project', '.utils' +]: + lazyload.make_lazy(module) + import urllib3 import background import click diff --git a/pipenv/vendor/lazyload/__init__.py b/pipenv/vendor/lazyload/__init__.py new file mode 100644 index 00000000..aeb0753e --- /dev/null +++ b/pipenv/vendor/lazyload/__init__.py @@ -0,0 +1,61 @@ +import sys +from types import ModuleType + + +class _LazyModuleMarker(object): + """ + A marker to indicate a LazyModule type. + Allows us to check module's with `isinstance(mod, _LazyModuleMarker)` + to know if the module is lazy. + """ + pass + + +class NonLocal(object): + """ + Simulates nonlocal keyword in Python 2 + """ + __slots__ = ['value'] + + def __init__(self, value): + self.value = value + + +def make_lazy(module_path): + """ + Mark that this module should not be imported until an + attribute is needed off of it. + """ + sys_modules = sys.modules # cache in the locals + + # store our 'instance' data in the closure. + module = NonLocal(None) + + class LazyModule(_LazyModuleMarker): + """ + A standin for a module to prevent it from being imported + """ + def __mro__(self): + """ + Override the __mro__ to fool `isinstance`. + """ + # We don't use direct subclassing because `ModuleType` has an + # incompatible metaclass base with object (they are both in c) + # and we are overridding __getattribute__. + # By putting a __mro__ method here, we can pass `isinstance` + # checks without ever invoking our __getattribute__ function. + return (LazyModule, ModuleType) + + def __getattribute__(self, attr): + """ + Override __getattribute__ to hide the implementation details. + """ + if module.value is None: + del sys_modules[module_path] + module.value = __import__(module_path) + + sys_modules[module_path] = __import__(module_path) + + return getattr(module.value, attr) + + sys_modules[module_path] = LazyModule()