Merge branch 'master' into mention-pipenv-ignore-virtualenvs

This commit is contained in:
Dan Ryan
2018-03-30 19:10:56 -04:00
committed by GitHub
36 changed files with 280 additions and 172 deletions
+2
View File
@@ -30,3 +30,5 @@ test_project
/t.py
/deb_dist/
.tox/
+2
View File
@@ -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
View File
@@ -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"
-6
View File
@@ -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
View File
@@ -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 uptodate."""
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):
+1 -1
View File
@@ -231,7 +231,7 @@ class TOMLFile:
if has_anonymous_entry():
return items
else:
return items + [('', self[''])]
return list(items) + [('', self[''])]
@property
def primitive(self):
+2 -1
View File
@@ -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
+8 -1
View File
@@ -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': {}
+6 -23
View File
@@ -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:
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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.
@@ -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
View File
@@ -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'"