Merge pull request #1917 from immerrr/make-costly-imports-lazy

Make costly imports lazy to optimize startup time
This commit is contained in:
Dan Ryan
2018-04-16 23:14:02 -04:00
committed by GitHub
5 changed files with 252 additions and 216 deletions
+210 -182
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -17,8 +17,6 @@ import dotenv
import delegator
from .vendor import pexpect
import pipfile
import pipdeptree
from .vendor.pipreqs import pipreqs
from blindspin import spinner
from requests.packages import urllib3
@@ -234,6 +232,7 @@ def ensure_environment():
def import_from_code(path='.'):
from pipreqs import pipreqs
rs = []
try:
for r in pipreqs.get_all_imports(path):
@@ -2341,6 +2340,7 @@ def do_check(three=None, python=False, system=False, unused=False, args=None):
def do_graph(bare=False, json=False, reverse=False):
import pipdeptree
try:
python_path = which('python')
except AttributeError:
+2 -5
View File
@@ -4,12 +4,10 @@ import json
import os
import re
import sys
import shlex
import base64
import hashlib
import contoml
import delegator
import pipfile
import pipfile.api
import toml
@@ -19,11 +17,9 @@ try:
except ImportError:
import pathlib2 as pathlib
from .vendor.pip9 import ConfigOptionParser
from .cmdparse import Script
from .utils import (
mkdir_p,
convert_deps_from_pip,
pep423_name,
proper_case,
find_requirements,
@@ -35,7 +31,6 @@ from .utils import (
is_valid_url,
normalize_drive,
python_version,
escape_grouped_arguments,
)
from .environments import (
PIPENV_MAX_DEPTH,
@@ -555,6 +550,7 @@ class Project(object):
def create_pipfile(self, python=None):
"""Creates the Pipfile, filled with juicy defaults."""
from .vendor.pip9 import ConfigOptionParser
config_parser = ConfigOptionParser(name=self.name)
install = dict(config_parser.get_config_section('install'))
indexes = install.get('extra-index-url', '').lstrip('\n').split('\n')
@@ -667,6 +663,7 @@ class Project(object):
self.write_toml(p)
def add_package_to_pipfile(self, package_name, dev=False):
from .utils import convert_deps_from_pip
# Read and append Pipfile.
p = self.parsed_pipfile
# Don't re-capitalize file URLs or VCSs.
+36 -25
View File
@@ -7,14 +7,12 @@ import tempfile
import sys
import shutil
import logging
import click
import crayons
import delegator
import parse
import requests
import six
import stat
import warnings
from click import echo as click_echo
try:
from weakref import finalize
@@ -45,16 +43,6 @@ except ImportError:
pass
from distutils.spawn import find_executable
from contextlib import contextmanager
from .patched.piptools.resolver import Resolver
from .patched.piptools.repositories.pypi import PyPIRepository
from .patched.piptools.scripts.compile import get_pip_command
from .patched.piptools import logging as piptools_logging
from .patched.piptools.exceptions import NoCandidateFound
from .vendor.pip9.download import is_archive_file
from .vendor.pip9.exceptions import DistributionNotFound
from .vendor.pip9.index import Link
from .vendor.pip9._vendor.requests.exceptions import HTTPError, ConnectionError
from .pep508checker import lookup
from .environments import PIPENV_MAX_ROUNDS, PIPENV_CACHE_DIR
@@ -68,11 +56,22 @@ specifiers = [k for k in lookup.keys()]
# List of version control systems we support.
VCS_LIST = ('git', 'svn', 'hg', 'bzr')
SCHEME_LIST = ('http://', 'https://', 'ftp://', 'ftps://', 'file://')
requests = requests.Session()
requests_session = None
def _get_requests_session():
"""Load requests lazily."""
global requests_session
if requests_session is not None:
return requests_session
import requests
requests_session = requests.Session()
return requests_session
def get_requirement(dep):
from .vendor.pip9.req.req_install import _strip_extras, Wheel
from .vendor.pip9.index import Link
from .vendor import requirements
"""Pre-clean requirement strings passed to the requirements parser.
@@ -214,6 +213,7 @@ def parse_python_version(output):
def python_version(path_to_python):
import delegator
if not path_to_python:
return None
try:
@@ -298,6 +298,13 @@ def actually_resolve_reps(
):
from pip9 import basecommand, req
from pip9._vendor import requests as pip_requests
from pip9.exceptions import DistributionNotFound
from pip9._vendor.requests.exceptions import HTTPError
from pipenv.patched.piptools.resolver import Resolver
from pipenv.patched.piptools.repositories.pypi import PyPIRepository
from pipenv.patched.piptools.scripts.compile import get_pip_command
from pipenv.patched.piptools import logging as piptools_logging
from pipenv.patched.piptools.exceptions import NoCandidateFound
class PipCommand(basecommand.Command):
"""Needed for pip-tools."""
@@ -362,7 +369,7 @@ def actually_resolve_reps(
try:
resolved_tree.update(resolver.resolve(max_rounds=PIPENV_MAX_ROUNDS))
except (NoCandidateFound, DistributionNotFound, HTTPError) as e:
click.echo(
click_echo(
'{0}: Your dependencies could not be resolved. You likely have a mismatch in your sub-dependencies.\n '
'You can use {1} to bypass this mechanism, then run {2} to inspect the situation.'
''.format(
@@ -372,9 +379,9 @@ def actually_resolve_reps(
),
err=True,
)
click.echo(crayons.blue(str(e)), err=True)
click_echo(crayons.blue(str(e)), err=True)
if 'no version found at all' in str(e):
click.echo(
click_echo(
crayons.blue(
'Please check your version specifier and version number. See PEP440 for more information.'
)
@@ -387,6 +394,7 @@ def actually_resolve_reps(
def venv_resolve_deps(
deps, which, project, pre=False, verbose=False, clear=False, allow_global=False
):
import delegator
from . import resolver
import json
@@ -406,13 +414,13 @@ def venv_resolve_deps(
assert c.return_code == 0
except AssertionError:
if verbose:
click.echo(c.out, err=True)
click.echo(c.err, err=True)
click_echo(c.out, err=True)
click_echo(c.err, err=True)
else:
click.echo(c.err[int(len(c.err) / 2) - 1:], err=True)
click_echo(c.err[int(len(c.err) / 2) - 1:], err=True)
sys.exit(c.return_code)
if verbose:
click.echo(c.out.split('RESULTS:')[0], err=True)
click_echo(c.out.split('RESULTS:')[0], err=True)
try:
return json.loads(c.out.split('RESULTS:')[1].strip())
@@ -434,6 +442,8 @@ def resolve_deps(
"""Given a list of dependencies, return a resolved list of dependencies,
using pip-tools -- and their hashes, using the warehouse API / pip9.
"""
from pip9._vendor.requests.exceptions import ConnectionError
index_lookup = {}
markers_lookup = {}
python_path = which('python', allow_global=allow_global)
@@ -494,7 +504,7 @@ def resolve_deps(
for source in sources):
try:
# Grab the hashes from the new warehouse API.
r = requests.get(
r = _get_requests_session().get(
'https://pypi.org/pypi/{0}/json'.format(name),
timeout=10,
)
@@ -511,7 +521,7 @@ def resolve_deps(
]
except (ValueError, KeyError, ConnectionError):
if verbose:
click.echo(
click_echo(
'{0}: Error generating hash for {1}'.format(
crayons.red('Warning', bold=True), name
)
@@ -809,6 +819,7 @@ def is_installable_file(path):
"""Determine if a path can potentially be installed"""
from .vendor.pip9.utils import is_installable_dir
from .vendor.pip9.utils.packaging import specifiers
from .vendor.pip9.download import is_archive_file
if hasattr(path, 'keys') and any(
key for key in path.keys() if key in ['file', 'path']
@@ -878,7 +889,7 @@ def pep423_name(name):
def proper_case(package_name):
"""Properly case project name from pypi.org."""
# Hit the simple API.
r = requests.get(
r = _get_requests_session().get(
'https://pypi.org/pypi/{0}/json'.format(package_name),
timeout=0.3,
stream=True,
@@ -1127,7 +1138,7 @@ def is_valid_url(url):
def download_file(url, filename):
"""Downloads file from url to a path with filename"""
r = requests.get(url, stream=True)
r = _get_requests_session().get(url, stream=True)
if not r.ok:
raise IOError('Unable to download file')
+2 -2
View File
@@ -81,10 +81,10 @@ def test_editable_vcs_install(PipenvInstance, pip_src_dir, pypi):
@pytest.mark.tablib
@pytest.mark.needs_internet
@flaky
def test_install_editable_git_tag(PipenvInstance, pip_src_dir):
def test_install_editable_git_tag(PipenvInstance, pip_src_dir, pypi):
# This uses the real PyPI since we need Internet to access the Git
# dependency anyway.
with PipenvInstance() as p:
with PipenvInstance(pypi=pypi) as p:
c = p.pipenv('install -e git+https://github.com/benjaminp/six.git@1.11.0#egg=six')
assert c.return_code == 0
assert 'six' in p.pipfile['packages']