mirror of
https://github.com/kennethreitz/pipenv.git
synced 2026-06-05 22:50:18 +00:00
Merge branch 'master' into mention-pipenv-ignore-virtualenvs
This commit is contained in:
@@ -30,3 +30,5 @@ test_project
|
||||
/t.py
|
||||
|
||||
/deb_dist/
|
||||
|
||||
.tox/
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
- Resolve editable packages on the local filesystem.
|
||||
- Ensure lock hash does not change based on injected env vars.
|
||||
- Fix bug in detecting .venv at project root when in subdirectories.
|
||||
- Parse quoting in [scripts] section correctly + clearer run errors.
|
||||
- Fix bug resolving & locking markers correctly
|
||||
11.9.0:
|
||||
- Vastly improve markers capabilities.
|
||||
- Support for environment variables in Pipfiles.
|
||||
|
||||
+1
-1
@@ -322,7 +322,7 @@ To prevent pipenv from loading the ``.env`` file, set the ``PIPENV_DONT_LOAD_ENV
|
||||
☤ Support for Environment Variables
|
||||
-----------------------------------
|
||||
|
||||
``pipenv`` supports the usage of environment variables in values. For example:
|
||||
``pipenv`` supports the usage of environment variables in values. For example::
|
||||
|
||||
[[source]]
|
||||
url = "https://${PYPI_USERNAME}:${PYPI_PASSWORD}@my_private_repo.example.com/simple"
|
||||
|
||||
@@ -503,9 +503,6 @@ def lock(
|
||||
|
||||
# Ensure that virtualenv is available.
|
||||
core.ensure_project(three=three, python=python)
|
||||
# Load the --pre settings from the Pipfile.
|
||||
if not pre:
|
||||
pre = core.project.settings.get('pre')
|
||||
if requirements:
|
||||
core.do_init(dev=dev, requirements=requirements)
|
||||
core.do_lock(
|
||||
@@ -739,9 +736,6 @@ def update(
|
||||
crayons.white('.', bold=True),
|
||||
)
|
||||
)
|
||||
# Load the --pre settings from the Pipfile.
|
||||
if not pre:
|
||||
pre = core.project.settings.get('pre')
|
||||
core.do_lock(
|
||||
verbose=verbose, clear=clear, pre=pre, keep_outdated=keep_outdated
|
||||
)
|
||||
|
||||
+74
-38
@@ -4,6 +4,7 @@ import logging
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import shlex
|
||||
import signal
|
||||
import time
|
||||
import tempfile
|
||||
@@ -1007,7 +1008,11 @@ def do_lock(
|
||||
write=True,
|
||||
):
|
||||
"""Executes the freeze functionality."""
|
||||
from notpip._vendor.distlib.markers import Evaluator
|
||||
allowed_marker_keys = ['markers'] + [k for k in Evaluator.allowed_values.keys()]
|
||||
cached_lockfile = {}
|
||||
if not pre:
|
||||
pre = project.settings.get('allow_prereleases')
|
||||
if keep_outdated:
|
||||
if not project.lockfile_exists:
|
||||
click.echo(
|
||||
@@ -1064,8 +1069,11 @@ def do_lock(
|
||||
# Add index metadata to lockfile.
|
||||
if 'index' in dep:
|
||||
lockfile['develop'][dep['name']]['index'] = dep['index']
|
||||
# Add PEP 508 specifier metadata to lockfile.
|
||||
# Add PEP 508 specifier metadata to lockfile if dep isnt top level
|
||||
# or top level dep doesn't itself have markers
|
||||
if 'markers' in dep:
|
||||
if dep['name'] in dev_packages and not any(key in dev_packages[dep['name']] for key in allowed_marker_keys):
|
||||
continue
|
||||
lockfile['develop'][dep['name']]['markers'] = dep['markers']
|
||||
# Add refs for VCS installs.
|
||||
# TODO: be smarter about this.
|
||||
@@ -1120,8 +1128,11 @@ def do_lock(
|
||||
# Add index metadata to lockfile.
|
||||
if 'index' in dep:
|
||||
lockfile['default'][dep['name']]['index'] = dep['index']
|
||||
# Add PEP 508 specifier metadata to lockfile.
|
||||
# Add PEP 508 specifier metadata to lockfile if dep isn't top level
|
||||
# or top level dep has no specifiers itself
|
||||
if 'markers' in dep:
|
||||
if dep['name'] in project.packages and not any(key in project.packages[dep['name']] for key in allowed_marker_keys):
|
||||
continue
|
||||
lockfile['default'][dep['name']]['markers'] = dep['markers']
|
||||
# Add refs for VCS installs.
|
||||
# TODO: be smarter about this.
|
||||
@@ -1637,7 +1648,6 @@ def warn_in_virtualenv():
|
||||
|
||||
def ensure_lockfile(keep_outdated=False):
|
||||
"""Ensures that the lockfile is up–to–date."""
|
||||
pre = project.settings.get('allow_prereleases')
|
||||
if not keep_outdated:
|
||||
keep_outdated = project.settings.get('keep_outdated')
|
||||
# Write out the lockfile if it doesn't exist, but not if the Pipfile is being ignored
|
||||
@@ -1654,9 +1664,9 @@ def ensure_lockfile(keep_outdated=False):
|
||||
),
|
||||
err=True,
|
||||
)
|
||||
do_lock(pre=pre, keep_outdated=keep_outdated)
|
||||
do_lock(keep_outdated=keep_outdated)
|
||||
else:
|
||||
do_lock(pre=pre, keep_outdated=keep_outdated)
|
||||
do_lock(keep_outdated=keep_outdated)
|
||||
|
||||
|
||||
def do_py(system=False):
|
||||
@@ -2011,8 +2021,6 @@ def do_uninstall(
|
||||
system = True
|
||||
# Ensure that virtualenv is available.
|
||||
ensure_project(three=three, python=python)
|
||||
# Load the --pre settings from the Pipfile.
|
||||
pre = project.settings.get('allow_prereleases')
|
||||
package_names = (package_name,) + more_packages
|
||||
pipfile_remove = True
|
||||
# Un-install all dependencies, if --all was provided.
|
||||
@@ -2079,7 +2087,7 @@ def do_uninstall(
|
||||
project.remove_package_from_pipfile(package_name, dev=True)
|
||||
project.remove_package_from_pipfile(package_name, dev=False)
|
||||
if lock:
|
||||
do_lock(system=system, pre=pre, keep_outdated=keep_outdated)
|
||||
do_lock(system=system, keep_outdated=keep_outdated)
|
||||
|
||||
|
||||
def do_shell(three=None, python=False, fancy=False, shell_args=None):
|
||||
@@ -2177,7 +2185,7 @@ def inline_activate_virtualenv():
|
||||
activate_this = which('activate_this.py')
|
||||
with open(activate_this) as f:
|
||||
code = compile(f.read(), activate_this, 'exec')
|
||||
exec (code, dict(__file__=activate_this))
|
||||
exec(code, dict(__file__=activate_this))
|
||||
# Catch all errors, just in case.
|
||||
except Exception:
|
||||
click.echo(
|
||||
@@ -2187,34 +2195,52 @@ def inline_activate_virtualenv():
|
||||
)
|
||||
|
||||
|
||||
def do_run(command, args, three=None, python=False):
|
||||
# Ensure that virtualenv is available.
|
||||
ensure_project(three=three, python=python, validate=False)
|
||||
load_dot_env()
|
||||
def do_run_nt(command, args):
|
||||
"""Run command by appending space-joined args to it!"""
|
||||
import subprocess
|
||||
command = project.scripts.get(command, command)
|
||||
|
||||
# if you've passed something with crazy quoting...
|
||||
# ...just don't. (or put it in a script!)
|
||||
p = subprocess.Popen(
|
||||
command + ' '.join(args), shell=True, universal_newlines=True
|
||||
)
|
||||
p.communicate()
|
||||
sys.exit(p.returncode)
|
||||
|
||||
|
||||
def _get_command_posix(project, command, args):
|
||||
"""Fully bake command into executable and args, based upon project"""
|
||||
# Script was found…
|
||||
if command in project.scripts:
|
||||
command = ' '.join(project.scripts[command])
|
||||
# Separate out things that were passed in as a string.
|
||||
_c = list(command.split())
|
||||
command = _c.pop(0)
|
||||
if _c:
|
||||
args = list(args)
|
||||
for __c in reversed(_c):
|
||||
args.insert(0, __c)
|
||||
# Activate virtualenv under the current interpreter's environment
|
||||
inline_activate_virtualenv()
|
||||
# Windows!
|
||||
if os.name == 'nt':
|
||||
import subprocess
|
||||
command = project.scripts[command]
|
||||
parsed_command = shlex.split(command)
|
||||
executable = parsed_command[0]
|
||||
# prepend arguments
|
||||
args = list(parsed_command[1:]) + list(args)
|
||||
return executable, args
|
||||
|
||||
p = subprocess.Popen(
|
||||
[command] + list(args), shell=True, universal_newlines=True
|
||||
)
|
||||
p.communicate()
|
||||
sys.exit(p.returncode)
|
||||
else:
|
||||
command_path = system_which(command)
|
||||
if not command_path:
|
||||
|
||||
def do_run_posix(command, args):
|
||||
"""Attempt to run command either pulling from project or interpreting as executable.
|
||||
|
||||
Args are appended to the command in [scripts] section of project if found.
|
||||
"""
|
||||
executable, args = _get_command_posix(project, command, args)
|
||||
command_path = system_which(executable)
|
||||
if not command_path:
|
||||
if command in project.scripts:
|
||||
click.echo(
|
||||
'{0}: the command {1} (from {2}) could not be found within {3}.'
|
||||
''.format(
|
||||
crayons.red('Error', bold=True),
|
||||
crayons.red(executable),
|
||||
crayons.normal(command, bold=True),
|
||||
crayons.normal('PATH', bold=True),
|
||||
),
|
||||
err=True,
|
||||
)
|
||||
else:
|
||||
click.echo(
|
||||
'{0}: the command {1} could not be found within {2} or Pipfile\'s {3}.'
|
||||
''.format(
|
||||
@@ -2225,10 +2251,20 @@ def do_run(command, args, three=None, python=False):
|
||||
),
|
||||
err=True,
|
||||
)
|
||||
sys.exit(1)
|
||||
# Execute the command.
|
||||
os.execl(command_path, command_path, *args)
|
||||
pass
|
||||
sys.exit(1)
|
||||
os.execl(command_path, command_path, *args)
|
||||
|
||||
|
||||
def do_run(command, args, three=None, python=False):
|
||||
# Ensure that virtualenv is available.
|
||||
ensure_project(three=three, python=python, validate=False)
|
||||
load_dot_env()
|
||||
# Activate virtualenv under the current interpreter's environment
|
||||
inline_activate_virtualenv()
|
||||
if os.name == 'nt':
|
||||
do_run_nt(command, args)
|
||||
else:
|
||||
do_run_posix(command, args)
|
||||
|
||||
|
||||
def do_check(three=None, python=False, system=False, unused=False, args=None):
|
||||
|
||||
@@ -231,7 +231,7 @@ class TOMLFile:
|
||||
if has_anonymous_entry():
|
||||
return items
|
||||
else:
|
||||
return items + [('', self[''])]
|
||||
return list(items) + [('', self[''])]
|
||||
|
||||
@property
|
||||
def primitive(self):
|
||||
|
||||
@@ -653,7 +653,8 @@ class PackageFinder(object):
|
||||
if not ext:
|
||||
self._log_skipped_link(link, 'not a file')
|
||||
return
|
||||
if ext not in SUPPORTED_EXTENSIONS and not ignore_compatibility:
|
||||
# Always ignore unsupported extensions even when we ignore compatibility
|
||||
if ext not in SUPPORTED_EXTENSIONS:
|
||||
self._log_skipped_link(
|
||||
link, 'unsupported archive format: %s' % ext)
|
||||
return
|
||||
|
||||
@@ -9,6 +9,13 @@ import sys
|
||||
import os
|
||||
|
||||
|
||||
DEFAULT_SOURCE = {
|
||||
u'url': u'https://pypi.python.org/simple',
|
||||
u'verify_ssl': True,
|
||||
u'name': u'pypi',
|
||||
}
|
||||
|
||||
|
||||
def format_full_version(info):
|
||||
version = '{0.major}.{0.minor}.{0.micro}'.format(info)
|
||||
kind = info.releaselevel
|
||||
@@ -89,7 +96,7 @@ class PipfileParser(object):
|
||||
|
||||
# Load the default configuration.
|
||||
default_config = {
|
||||
u'source': [{u'url': u'https://pypi.python.org/simple', u'verify_ssl': True, 'name': "pypi"}],
|
||||
u'source': [DEFAULT_SOURCE],
|
||||
u'packages': {},
|
||||
u'requires': {},
|
||||
u'dev-packages': {}
|
||||
|
||||
@@ -13,7 +13,7 @@ from . import click
|
||||
from .cache import DependencyCache
|
||||
from .exceptions import UnsupportedConstraint
|
||||
from .logging import log
|
||||
from .utils import (format_requirement, format_specifier, full_groupby,
|
||||
from .utils import (format_requirement, format_specifier, full_groupby, dedup,
|
||||
is_pinned_requirement, key_from_ireq, key_from_req, UNSAFE_PACKAGES)
|
||||
|
||||
green = partial(click.style, fg='green')
|
||||
@@ -297,9 +297,9 @@ class Resolver(object):
|
||||
dependencies = self.repository.get_dependencies(ireq)
|
||||
import sys
|
||||
if sys.version_info[0] == 2:
|
||||
self.dependency_cache[ireq] = sorted(str(ireq.req) for ireq in dependencies)
|
||||
self.dependency_cache[ireq] = sorted(format_requirement(ireq) for ireq in dependencies)
|
||||
else:
|
||||
self.dependency_cache[ireq] = sorted('{0}; {1}'.format(str(ireq.req), str(ireq.markers)) if ireq.markers else str(ireq.req) for ireq in dependencies)
|
||||
self.dependency_cache[ireq] = sorted(format_requirement(ireq) for ireq in dependencies)
|
||||
|
||||
# Example: ['Werkzeug>=0.9', 'Jinja2>=2.4']
|
||||
dependency_strings = self.dependency_cache[ireq]
|
||||
@@ -310,28 +310,11 @@ class Resolver(object):
|
||||
for dependency_string in dependency_strings:
|
||||
|
||||
try:
|
||||
markers = None
|
||||
_dependency_string = dependency_string
|
||||
if ';' in dependency_string:
|
||||
# split off markers and remove any duplicates by comparing against deps
|
||||
dependencies, markers = dependency_string.rsplit(';', 1)
|
||||
dependency_string = ';'.join([dep for dep in dependencies.split(';') if dep.strip() != markers.strip()])
|
||||
individual_dependencies = [dep.strip() for dep in dependency_string.split(', ')]
|
||||
cleaned_deps = []
|
||||
for dep in individual_dependencies:
|
||||
tokens = [token.strip() for token in dep.split(';')]
|
||||
cleaned_tokens = []
|
||||
dep_markers = []
|
||||
if len(tokens) == 1:
|
||||
cleaned_deps.append(tokens[0])
|
||||
continue
|
||||
dep_markers = list(set(tokens[1:]))
|
||||
cleaned_tokens.append(tokens[0])
|
||||
if dep_markers:
|
||||
cleaned_tokens.extend(dep_markers)
|
||||
cleaned_deps.append('; '.join(cleaned_tokens))
|
||||
_dependency_string = ', '.join(set(cleaned_deps))
|
||||
if markers:
|
||||
_dependency_string += ';{0}'.format(markers)
|
||||
_dependencies = [dep.strip() for dep in dependency_string.split(';')]
|
||||
_dependency_string = '; '.join([dep for dep in dedup(_dependencies)])
|
||||
|
||||
yield InstallRequirement.from_line(_dependency_string, constraint=ireq.constraint)
|
||||
except InvalidMarker:
|
||||
|
||||
@@ -87,7 +87,7 @@ def format_requirement(ireq, marker=None):
|
||||
line = str(ireq.req).lower()
|
||||
|
||||
if marker:
|
||||
line = '{} ; {}'.format(line, marker)
|
||||
line = '{}; {}'.format(line, marker)
|
||||
|
||||
return line
|
||||
|
||||
|
||||
+37
-42
@@ -12,6 +12,7 @@ import hashlib
|
||||
import contoml
|
||||
import delegator
|
||||
import pipfile
|
||||
import pipfile.api
|
||||
import toml
|
||||
|
||||
from pip9 import ConfigOptionParser
|
||||
@@ -51,6 +52,26 @@ if PIPENV_PIPFILE:
|
||||
_pipfile_cache = {}
|
||||
|
||||
|
||||
if PIPENV_TEST_INDEX:
|
||||
DEFAULT_SOURCE = {
|
||||
u'url': PIPENV_TEST_INDEX,
|
||||
u'verify_ssl': True,
|
||||
u'name': u'custom',
|
||||
}
|
||||
else:
|
||||
DEFAULT_SOURCE = {
|
||||
u'url': u'https://pypi.python.org/simple',
|
||||
u'verify_ssl': True,
|
||||
u'name': u'pypi',
|
||||
}
|
||||
|
||||
pipfile.api.DEFAULT_SOURCE = DEFAULT_SOURCE
|
||||
|
||||
|
||||
class SourceNotFound(KeyError):
|
||||
pass
|
||||
|
||||
|
||||
class Project(object):
|
||||
"""docstring for Project"""
|
||||
|
||||
@@ -369,12 +390,7 @@ class Project(object):
|
||||
|
||||
@property
|
||||
def scripts(self):
|
||||
scripts = self.parsed_pipfile.get('scripts', {})
|
||||
posix = os.name == 'posix'
|
||||
_scripts = {}
|
||||
for (k, v) in scripts.items():
|
||||
_scripts[k] = shlex.split(str(v), posix=posix)
|
||||
return _scripts
|
||||
return dict(self.parsed_pipfile.get('scripts', {}))
|
||||
|
||||
def update_settings(self, d):
|
||||
settings = self.settings
|
||||
@@ -495,35 +511,20 @@ class Project(object):
|
||||
config_parser = ConfigOptionParser(name=self.name)
|
||||
install = dict(config_parser.get_config_section('install'))
|
||||
indexes = install.get('extra-index-url', '').lstrip('\n').split('\n')
|
||||
if PIPENV_TEST_INDEX:
|
||||
sources = [
|
||||
{
|
||||
u'url': PIPENV_TEST_INDEX,
|
||||
u'verify_ssl': True,
|
||||
u'name': u'custom',
|
||||
}
|
||||
]
|
||||
else:
|
||||
# Default source.
|
||||
pypi_source = {
|
||||
u'url': u'https://pypi.python.org/simple',
|
||||
u'verify_ssl': True,
|
||||
u'name': 'pypi',
|
||||
}
|
||||
sources = [pypi_source]
|
||||
for i, index in enumerate(indexes):
|
||||
if not index:
|
||||
continue
|
||||
sources = [DEFAULT_SOURCE]
|
||||
for i, index in enumerate(indexes):
|
||||
if not index:
|
||||
continue
|
||||
|
||||
source_name = 'pip_index_{}'.format(i)
|
||||
verify_ssl = index.startswith('https')
|
||||
sources.append(
|
||||
{
|
||||
u'url': index,
|
||||
u'verify_ssl': verify_ssl,
|
||||
u'name': source_name,
|
||||
}
|
||||
)
|
||||
source_name = 'pip_index_{}'.format(i)
|
||||
verify_ssl = index.startswith('https')
|
||||
sources.append(
|
||||
{
|
||||
u'url': index,
|
||||
u'verify_ssl': verify_ssl,
|
||||
u'name': source_name,
|
||||
}
|
||||
)
|
||||
data = {
|
||||
u'source': sources,
|
||||
# Default packages.
|
||||
@@ -570,15 +571,8 @@ class Project(object):
|
||||
|
||||
if 'source' in self.parsed_pipfile:
|
||||
return self.parsed_pipfile['source']
|
||||
|
||||
else:
|
||||
return [
|
||||
{
|
||||
u'url': u'https://pypi.python.org/simple',
|
||||
u'verify_ssl': True,
|
||||
'name': 'pypi',
|
||||
}
|
||||
]
|
||||
return [DEFAULT_SOURCE]
|
||||
|
||||
def get_source(self, name=None, url=None):
|
||||
for source in self.sources:
|
||||
@@ -589,6 +583,7 @@ class Project(object):
|
||||
elif url:
|
||||
if source.get('url') in url:
|
||||
return source
|
||||
raise SourceNotFound(name or url)
|
||||
|
||||
def destroy_lockfile(self):
|
||||
"""Deletes the lockfile."""
|
||||
|
||||
+1
-1
@@ -1207,7 +1207,7 @@ class TemporaryDirectory(object):
|
||||
in it are removed.
|
||||
"""
|
||||
|
||||
def __init__(self, suffix=None, prefix=None, dir=None):
|
||||
def __init__(self, suffix, prefix, dir=None):
|
||||
if 'RAM_DISK' in os.environ:
|
||||
import uuid
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -6,7 +6,6 @@ Home-page: https://github.com/kennethreitz/pytest-pypi
|
||||
Author: Kenneth Reitz
|
||||
Author-email: me@kennethreitz.org
|
||||
License: MIT
|
||||
Description-Content-Type: UNKNOWN
|
||||
Description: pytest-httpbin
|
||||
==============
|
||||
|
||||
|
||||
+145
-56
@@ -1,15 +1,17 @@
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import shutil
|
||||
import json
|
||||
import pytest
|
||||
import warnings
|
||||
from pipenv.core import activate_virtualenv
|
||||
from pipenv.core import activate_virtualenv, _get_command_posix
|
||||
from pipenv.utils import (
|
||||
temp_environ, get_windows_path, mkdir_p, normalize_drive, TemporaryDirectory
|
||||
)
|
||||
from pipenv.vendor import toml
|
||||
from pipenv.vendor import delegator
|
||||
from pipenv.vendor import requests
|
||||
from pipenv.patched import pipfile
|
||||
from pipenv.project import Project
|
||||
from pipenv.vendor.six import PY2
|
||||
@@ -28,6 +30,25 @@ os.environ['PIPENV_VENV_IN_PROJECT'] = '1'
|
||||
os.environ['PYPI_VENDOR_DIR'] = os.path.sep.join([os.path.dirname(__file__), 'pypi'])
|
||||
|
||||
|
||||
def check_internet():
|
||||
try:
|
||||
# Kenneth represents the Internet LGTM.
|
||||
resp = requests.get('http://httpbin.org/ip', timeout=1.0)
|
||||
resp.raise_for_status()
|
||||
except Exception:
|
||||
warnings.warn('Cannot connect to HTTPBin...', ResourceWarning)
|
||||
warnings.warn('Will skip tests requiring Internet', ResourceWarning)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
WE_HAVE_INTERNET = check_internet()
|
||||
|
||||
needs_internet = pytest.mark.skipif(not WE_HAVE_INTERNET, reason='requires internet')
|
||||
py3_only = pytest.mark.skipif(sys.version_info < (3, 0), reason="requires Python3")
|
||||
nix_only = pytest.mark.skipif(os.name != 'nt', reason="doesn't run on windows")
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def pip_src_dir(request):
|
||||
old_src_dir = os.environ.get('PIP_SRC', '')
|
||||
@@ -42,13 +63,16 @@ def pip_src_dir(request):
|
||||
return request
|
||||
|
||||
|
||||
class PipenvInstance():
|
||||
VERBOSE_COMMANDS = ('install', 'lock', 'uninstall')
|
||||
|
||||
|
||||
class PipenvInstance(object):
|
||||
"""An instance of a Pipenv Project..."""
|
||||
def __init__(self, pypi=None, pipfile=True, chdir=False):
|
||||
self.pypi = pypi
|
||||
self.original_umask = os.umask(0o007)
|
||||
self.original_dir = os.path.abspath(os.curdir)
|
||||
self._path = TemporaryDirectory(suffix='project', prefix='pipenv')
|
||||
self._path = TemporaryDirectory(suffix='-project', prefix='pipenv-')
|
||||
self.path = self._path.name
|
||||
# set file creation perms
|
||||
self.pipfile_path = None
|
||||
@@ -87,7 +111,11 @@ class PipenvInstance():
|
||||
if self.pipfile_path:
|
||||
os.environ['PIPENV_PIPFILE'] = self.pipfile_path
|
||||
|
||||
c = delegator.run('pipenv {0}'.format(cmd), block=block)
|
||||
with TemporaryDirectory(prefix='pipenv-', suffix='-cache') as tempdir:
|
||||
os.environ['PIPENV_CACHE_DIR'] = tempdir.name
|
||||
c = delegator.run('pipenv {0}'.format(cmd), block=block)
|
||||
if 'PIPENV_CACHE_DIR' in os.environ:
|
||||
del os.environ['PIPENV_CACHE_DIR']
|
||||
|
||||
if 'PIPENV_PIPFILE' in os.environ:
|
||||
del os.environ['PIPENV_PIPFILE']
|
||||
@@ -178,6 +206,7 @@ class TestPipenv:
|
||||
assert 'Warning: Using both --reverse and --json together is not supported.' in c.err
|
||||
|
||||
@pytest.mark.cli
|
||||
@needs_internet
|
||||
def test_pipenv_check(self, pypi):
|
||||
with PipenvInstance(pypi=pypi) as p:
|
||||
p.pipenv('install requests==1.0.0')
|
||||
@@ -350,7 +379,8 @@ tablib = "*"
|
||||
shutil.copy(source_path, os.path.join(p.path, file_name))
|
||||
os.mkdir(os.path.join(p.path, "tablib"))
|
||||
c = p.pipenv('install {}'.format(file_name))
|
||||
c = p.pipenv('uninstall --all --verbose')
|
||||
assert c.return_code == 0
|
||||
c = p.pipenv('uninstall --all')
|
||||
assert c.return_code == 0
|
||||
assert 'tablib' in c.out
|
||||
assert 'tablib' not in p.pipfile['packages']
|
||||
@@ -438,7 +468,8 @@ setup(
|
||||
|
||||
@pytest.mark.vcs
|
||||
@pytest.mark.install
|
||||
def test_basic_vcs_install(self, pypi):
|
||||
@needs_internet
|
||||
def test_basic_vcs_install(self, pip_src_dir, pypi):
|
||||
with PipenvInstance(pypi=pypi) as p:
|
||||
c = p.pipenv('install git+https://github.com/requests/requests.git#egg=requests')
|
||||
assert c.return_code == 0
|
||||
@@ -453,6 +484,7 @@ setup(
|
||||
@pytest.mark.e
|
||||
@pytest.mark.vcs
|
||||
@pytest.mark.install
|
||||
@needs_internet
|
||||
def test_editable_vcs_install(self, pip_src_dir, pypi):
|
||||
with PipenvInstance(pypi=pypi) as p:
|
||||
c = p.pipenv('install -e git+https://github.com/requests/requests.git#egg=requests')
|
||||
@@ -542,8 +574,9 @@ tpfd = "*"
|
||||
@pytest.mark.install
|
||||
@pytest.mark.resolver
|
||||
@pytest.mark.backup_resolver
|
||||
def test_backup_resolver(self, pypi):
|
||||
with PipenvInstance(pypi=pypi) as p:
|
||||
@needs_internet
|
||||
def test_backup_resolver(self):
|
||||
with PipenvInstance() as p:
|
||||
with open(p.pipfile_path, 'w') as f:
|
||||
contents = """
|
||||
[packages]
|
||||
@@ -557,24 +590,22 @@ tpfd = "*"
|
||||
|
||||
@pytest.mark.run
|
||||
@pytest.mark.markers
|
||||
@pytest.mark.install
|
||||
def test_package_environment_markers(self):
|
||||
def test_package_environment_markers(self, pypi):
|
||||
|
||||
with PipenvInstance() as p:
|
||||
with PipenvInstance(pypi=pypi) as p:
|
||||
with open(p.pipfile_path, 'w') as f:
|
||||
contents = """
|
||||
[packages]
|
||||
requests = {version = "*", markers="os_name=='splashwear'"}
|
||||
tablib = {version = "*", markers="os_name=='splashwear'"}
|
||||
""".strip()
|
||||
f.write(contents)
|
||||
|
||||
c = p.pipenv('install')
|
||||
assert c.return_code == 0
|
||||
|
||||
assert 'Ignoring' in c.out
|
||||
assert 'markers' in p.lockfile['default']['requests']
|
||||
assert 'markers' in p.lockfile['default']['tablib']
|
||||
|
||||
c = p.pipenv('run python -c "import requests;"')
|
||||
c = p.pipenv('run python -c "import tablib;"')
|
||||
assert c.return_code == 1
|
||||
|
||||
@pytest.mark.run
|
||||
@@ -601,10 +632,10 @@ requests = {version = "*", os_name = "== 'splashwear'"}
|
||||
|
||||
@pytest.mark.markers
|
||||
@pytest.mark.install
|
||||
def test_top_level_overrides_environment_markers(self):
|
||||
def test_top_level_overrides_environment_markers(self, pypi):
|
||||
"""Top-level environment markers should take precedence.
|
||||
"""
|
||||
with PipenvInstance() as p:
|
||||
with PipenvInstance(pypi=pypi) as p:
|
||||
with open(p.pipfile_path, 'w') as f:
|
||||
contents = """
|
||||
[packages]
|
||||
@@ -620,7 +651,7 @@ funcsigs = {version = "*", os_name = "== 'splashwear'"}
|
||||
|
||||
@pytest.mark.markers
|
||||
@pytest.mark.install
|
||||
def test_global_overrides_environment_markers(self):
|
||||
def test_global_overrides_environment_markers(self, pypi):
|
||||
"""Empty (unconditional) dependency should take precedence.
|
||||
|
||||
If a dependency is specified without environment markers, it should
|
||||
@@ -628,7 +659,7 @@ funcsigs = {version = "*", os_name = "== 'splashwear'"}
|
||||
APScheduler requires funcsigs only on Python 2, but since funcsigs is
|
||||
also specified as an unconditional dep, its markers should be empty.
|
||||
"""
|
||||
with PipenvInstance() as p:
|
||||
with PipenvInstance(pypi=pypi) as p:
|
||||
with open(p.pipfile_path, 'w') as f:
|
||||
contents = """
|
||||
[packages]
|
||||
@@ -645,8 +676,11 @@ funcsigs = "*"
|
||||
@pytest.mark.install
|
||||
@pytest.mark.vcs
|
||||
@pytest.mark.tablib
|
||||
def test_install_editable_git_tag(self, pip_src_dir, pypi):
|
||||
with PipenvInstance(pypi=pypi) as p:
|
||||
@needs_internet
|
||||
def test_install_editable_git_tag(self, pip_src_dir):
|
||||
# This uses the real PyPI since we need Internet to access the Git
|
||||
# dependency anyway.
|
||||
with PipenvInstance() as p:
|
||||
c = p.pipenv('install -e git+https://github.com/kennethreitz/tablib.git@v0.12.1#egg=tablib')
|
||||
assert c.return_code == 0
|
||||
assert 'tablib' in p.pipfile['packages']
|
||||
@@ -873,6 +907,21 @@ import records
|
||||
|
||||
assert command == '{0}/bin/activate'.format(venv)
|
||||
|
||||
@pytest.mark.lock
|
||||
def test_lock_handle_eggs(self, pypi):
|
||||
"""Ensure locking works with packages provoding egg formats.
|
||||
"""
|
||||
with PipenvInstance() as p:
|
||||
with open(p.pipfile_path, 'w') as f:
|
||||
f.write("""
|
||||
[packages]
|
||||
RandomWords = "*"
|
||||
""")
|
||||
c = p.pipenv('lock --verbose')
|
||||
assert c.return_code == 0
|
||||
assert 'randomwords' in p.lockfile['default']
|
||||
assert p.lockfile['default']['randomwords']['version'] == '==0.2.1'
|
||||
|
||||
@pytest.mark.lock
|
||||
@pytest.mark.requirements
|
||||
def test_lock_requirements_file(self, pypi):
|
||||
@@ -904,9 +953,11 @@ flask = "==0.12.2"
|
||||
|
||||
@pytest.mark.lock
|
||||
@pytest.mark.complex
|
||||
def test_complex_lock_with_vcs_deps(self, pip_src_dir, pypi):
|
||||
|
||||
with PipenvInstance(pypi=pypi) as p:
|
||||
@needs_internet
|
||||
def test_complex_lock_with_vcs_deps(self, pip_src_dir):
|
||||
# This uses the real PyPI since we need Internet to access the Git
|
||||
# dependency anyway.
|
||||
with PipenvInstance() as p:
|
||||
with open(p.pipfile_path, 'w') as f:
|
||||
contents = """
|
||||
[packages]
|
||||
@@ -936,14 +987,34 @@ requests = {git = "https://github.com/requests/requests", egg = "requests"}
|
||||
|
||||
@pytest.mark.lock
|
||||
@pytest.mark.requirements
|
||||
@pytest.mark.complex
|
||||
@pytest.mark.maya
|
||||
def test_complex_deps_lock_and_install_properly(self, pypi):
|
||||
def test_lock_with_prereleases(self, pypi):
|
||||
|
||||
with PipenvInstance(pypi=pypi) as p:
|
||||
with open(p.pipfile_path, 'w') as f:
|
||||
contents = """
|
||||
[packages]
|
||||
sqlalchemy = "==1.2.0b3"
|
||||
|
||||
[pipenv]
|
||||
allow_prereleases = true
|
||||
""".strip()
|
||||
f.write(contents)
|
||||
|
||||
c = p.pipenv('lock')
|
||||
assert c.return_code == 0
|
||||
assert p.lockfile['default']['sqlalchemy']['version'] == '==1.2.0b3'
|
||||
|
||||
@pytest.mark.lock
|
||||
@pytest.mark.requirements
|
||||
@pytest.mark.complex
|
||||
@pytest.mark.maya
|
||||
@needs_internet
|
||||
def test_complex_deps_lock_and_install_properly(self):
|
||||
# This uses the real PyPI because Maya has too many dependencies...
|
||||
with PipenvInstance() as p:
|
||||
with open(p.pipfile_path, 'w') as f:
|
||||
contents = """
|
||||
[packages]
|
||||
maya = "*"
|
||||
""".strip()
|
||||
f.write(contents)
|
||||
@@ -956,12 +1027,13 @@ maya = "*"
|
||||
|
||||
@pytest.mark.extras
|
||||
@pytest.mark.lock
|
||||
@pytest.mark.requirements
|
||||
@pytest.mark.complex
|
||||
def test_complex_lock_deep_extras(self, pypi):
|
||||
@needs_internet
|
||||
def test_complex_lock_deep_extras(self):
|
||||
# records[pandas] requires tablib[pandas] which requires pandas.
|
||||
# This uses the real PyPI; Pandas has too many requirements to mock.
|
||||
|
||||
with PipenvInstance(pypi=pypi) as p:
|
||||
with PipenvInstance() as p:
|
||||
with open(p.pipfile_path, 'w') as f:
|
||||
contents = """
|
||||
[packages]
|
||||
@@ -969,12 +1041,12 @@ records = {extras = ["pandas"], version = "==0.5.2"}
|
||||
""".strip()
|
||||
f.write(contents)
|
||||
|
||||
c = p.pipenv('install')
|
||||
assert c.return_code == 0
|
||||
c = p.pipenv('lock')
|
||||
assert c.return_code == 0
|
||||
assert 'tablib' in p.lockfile['default']
|
||||
assert 'pandas' in p.lockfile['default']
|
||||
c = p.pipenv('install')
|
||||
assert c.return_code == 0
|
||||
|
||||
@pytest.mark.lock
|
||||
@pytest.mark.deploy
|
||||
@@ -990,9 +1062,10 @@ flask = "==0.12.2"
|
||||
pytest = "==3.1.1"
|
||||
""".strip()
|
||||
f.write(contents)
|
||||
|
||||
p.pipenv('lock')
|
||||
|
||||
c = p.pipenv('install')
|
||||
assert c.return_code == 0
|
||||
c = p.pipenv('lock')
|
||||
assert c.return_code == 0
|
||||
with open(p.pipfile_path, 'w') as f:
|
||||
contents = """
|
||||
[packages]
|
||||
@@ -1006,6 +1079,7 @@ requests = "==2.14.0"
|
||||
@pytest.mark.install
|
||||
@pytest.mark.files
|
||||
@pytest.mark.urls
|
||||
@needs_internet
|
||||
def test_urls_work(self, pypi):
|
||||
|
||||
with PipenvInstance(pypi=pypi) as p:
|
||||
@@ -1058,6 +1132,7 @@ requests = "==2.14.0"
|
||||
shutil.copy(source_path, os.path.join(p.path, file_name))
|
||||
|
||||
c = p.pipenv('install {}'.format(file_name))
|
||||
assert c.return_code == 0
|
||||
key = [k for k in p.pipfile['packages'].keys()][0]
|
||||
dep = p.pipfile['packages'][key]
|
||||
|
||||
@@ -1072,6 +1147,7 @@ requests = "==2.14.0"
|
||||
@pytest.mark.install
|
||||
@pytest.mark.files
|
||||
@pytest.mark.urls
|
||||
@needs_internet
|
||||
def test_install_remote_requirements(self, pypi):
|
||||
with PipenvInstance(pypi=pypi) as p:
|
||||
# using a github hosted requirements.txt file
|
||||
@@ -1139,10 +1215,8 @@ requests = "==2.14.0"
|
||||
url = 'https://${PYPI_USERNAME}:${PYPI_PASSWORD}@pypi.python.org/simple'
|
||||
verify_ssl = true
|
||||
name = 'pypi'
|
||||
|
||||
[requires]
|
||||
python_version = '2.7'
|
||||
|
||||
[packages]
|
||||
flask = "==0.12.2"
|
||||
""")
|
||||
@@ -1164,14 +1238,16 @@ flask = "==0.12.2"
|
||||
assert Project().get_lockfile_hash() != Project().calculate_pipfile_hash()
|
||||
|
||||
@pytest.mark.run
|
||||
def test_scripts_basic(self):
|
||||
def test_scripts(self):
|
||||
with PipenvInstance(chdir=True) as p:
|
||||
with open(p.pipfile_path, 'w') as f:
|
||||
f.write("""
|
||||
f.write(r"""
|
||||
[scripts]
|
||||
printfoo = "python -c print('foo')"
|
||||
printfoo = "python -c \"print('foo')\""
|
||||
notfoundscript = "randomthingtotally"
|
||||
appendscript = "cmd arg1"
|
||||
multicommand = "bash -c \"cd docs && make html\""
|
||||
""")
|
||||
|
||||
c = p.pipenv('install')
|
||||
assert c.return_code == 0
|
||||
|
||||
@@ -1179,21 +1255,34 @@ printfoo = "python -c print('foo')"
|
||||
assert c.return_code == 0
|
||||
assert c.out == 'foo\n'
|
||||
assert c.err == ''
|
||||
if os.name != 'nt':
|
||||
c = p.pipenv('run notfoundscript')
|
||||
assert c.return_code == 1
|
||||
assert c.out == ''
|
||||
assert 'Error' in c.err
|
||||
assert 'randomthingtotally (from notfoundscript)' in c.err
|
||||
executable, argv = _get_command_posix(Project(), 'multicommand', [])
|
||||
assert executable == 'bash'
|
||||
assert argv == ['-c', 'cd docs && make html']
|
||||
executable, argv = _get_command_posix(Project(), 'appendscript', ['a', 'b'])
|
||||
assert executable == 'cmd'
|
||||
assert argv == ['arg1', 'a', 'b']
|
||||
|
||||
@pytest.mark.run
|
||||
@pytest.mark.skip(reason='This fails on Windows (not sure about POSIX).')
|
||||
def test_scripts_quoted(self):
|
||||
@pytest.mark.lock
|
||||
@pytest.mark.complex
|
||||
@py3_only
|
||||
def test_resolver_unique_markers(self, pypi):
|
||||
"""vcrpy has a dependency on `yarl` which comes with a marker
|
||||
of 'python version in "3.4, 3.5, 3.6" - this marker duplicates itself:
|
||||
|
||||
'yarl; python version in "3.4, 3.5, 3.6"; python version in "3.4, 3.5, 3.6"'
|
||||
|
||||
This verifies that we clean that successfully.
|
||||
"""
|
||||
with PipenvInstance(chdir=True) as p:
|
||||
with open(p.pipfile_path, 'w') as f:
|
||||
f.write("""
|
||||
[scripts]
|
||||
printfoo = "python -c print('foo')"
|
||||
""")
|
||||
|
||||
c = p.pipenv('install')
|
||||
c = p.pipenv('install vcrpy==1.11.0')
|
||||
assert c.return_code == 0
|
||||
|
||||
c = p.pipenv('run printfoo')
|
||||
assert c.return_code == 0
|
||||
assert c.out == 'foo\n'
|
||||
assert c.err == ''
|
||||
assert 'yarl' in p.lockfile['default']
|
||||
yarl = p.lockfile['default']['yarl']
|
||||
assert 'markers' in yarl
|
||||
assert yarl['markers'] == "python_version in '3.4, 3.5, 3.6'"
|
||||
|
||||
Reference in New Issue
Block a user