Merge branch 'master' into pew-associate-project-dir

This commit is contained in:
Tzu-ping Chung
2018-05-05 01:20:32 +08:00
committed by GitHub
20 changed files with 326 additions and 150 deletions
+1 -1
View File
@@ -2,4 +2,4 @@
# // ) ) / / // ) ) //___) ) // ) ) || / /
# //___/ / / / //___/ / // // / / || / /
# // / / // ((____ // / / ||/ /
__version__ = '11.10.1'
__version__ = '11.10.2.dev1'
+2 -3
View File
@@ -9,7 +9,7 @@ import io
import os
import six
import warnings
from tempfile import _bin_openflags, gettempdir, _mkstemp_inner, mkdtemp, _text_openflags
from tempfile import _bin_openflags, gettempdir, _mkstemp_inner, mkdtemp
from .utils import (logging, rmtree)
try:
@@ -258,13 +258,12 @@ def NamedTemporaryFile(
if os.name == "nt" and delete:
flags |= os.O_TEMPORARY
if six.PY2:
flags = _text_openflags if 'b' not in mode else flags
(fd, name) = _mkstemp_inner(dir, prefix, suffix, flags)
else:
(fd, name) = _mkstemp_inner(dir, prefix, suffix, flags, output_type)
try:
file = io.open(
fd, mode, buffering=buffering, newline=newline, encoding=encoding
fd, mode, buffering=buffering, newline=newline, encoding=encoding,
)
return _TemporaryFileWrapper(file, name, delete)
+18 -14
View File
@@ -25,7 +25,6 @@ import six
from .cmdparse import ScriptEmptyError
from .project import Project, SourceNotFound
from .utils import (
atomic_open_for_write,
convert_deps_from_pip,
convert_deps_to_pip,
is_required_version,
@@ -1168,13 +1167,7 @@ def do_lock(
default_package
]
if write:
# Write out the lockfile.
with atomic_open_for_write(project.lockfile_location) as f:
simplejson.dump(
lockfile, f, indent=4, separators=(',', ': '), sort_keys=True
)
# Write newline at end of document. GH Issue #319.
f.write('\n')
project.write_lockfile(lockfile)
click.echo(
'{0}'.format(
crayons.normal(
@@ -2494,19 +2487,30 @@ def do_sync(
unused=False,
sequential=False,
):
# The lock file needs to exist because sync won't write to it.
if not project.lockfile_exists:
click.echo(
'{0}: Pipfile.lock is missing! You need to run {1} first.'.format(
crayons.red('Error', bold=True),
crayons.red('$ pipenv lock', bold=True),
),
err=True,
)
sys.exit(1)
# Ensure that virtualenv is available.
ensure_project(three=three, python=python, validate=False)
# Install everything.
requirements_dir = TemporaryDirectory(
suffix='-requirements', prefix='pipenv-'
)
# Ensure that virtualenv is available.
ensure_project(three=three, python=python, validate=False)
concurrent = (not sequential)
ensure_lockfile()
# Install everything.
do_init(
dev=dev,
verbose=verbose,
concurrent=concurrent,
concurrent=(not sequential),
requirements_dir=requirements_dir,
ignore_pipfile=True, # Don't check if Pipfile and lock match.
)
requirements_dir.cleanup()
click.echo(crayons.green('All dependencies are now up-to-date!'))
@@ -1,4 +1,4 @@
Copyright 2016 Kenneth Reitz
Copyright 2017 Kenneth Reitz
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+21
View File
@@ -0,0 +1,21 @@
setuptools==39.1.0
appdirs==1.4.0
distlib==0.2.4
distro==1.2.0
html5lib==1.0b10
six==1.10.0
colorama==0.3.7
requests==2.18.4
chardet==3.0.4
idna==2.6
urllib3==1.22
certifi==2018.1.18
CacheControl==0.11.7
lockfile==0.12.2
ordereddict==1.1
progress==1.2
ipaddress==1.0.17
packaging==16.8
pyparsing==2.1.10
retrying==1.3.3
webencodings==0.5
+73 -27
View File
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
import codecs
import io
import json
import os
import re
@@ -10,15 +10,18 @@ import contoml
from first import first
import pipfile
import pipfile.api
import six
import toml
import json as simplejson
try:
import pathlib
from pathlib import Path
except ImportError:
import pathlib2 as pathlib
from pathlib2 import Path
from .cmdparse import Script
from .utils import (
atomic_open_for_write,
mkdir_p,
pep423_name,
proper_case,
@@ -46,7 +49,17 @@ from .environments import (
def _normalized(p):
if p is None:
return None
return normalize_drive(str(pathlib.Path(p).resolve()))
return normalize_drive(str(Path(p).resolve()))
DEFAULT_NEWLINES = u'\n'
def preferred_newlines(f):
if isinstance(f.newlines, six.text_type):
return f.newlines
return DEFAULT_NEWLINES
if PIPENV_PIPFILE:
@@ -90,6 +103,8 @@ class Project(object):
self._download_location = None
self._proper_names_location = None
self._pipfile_location = None
self._pipfile_newlines = DEFAULT_NEWLINES
self._lockfile_newlines = DEFAULT_NEWLINES
self._requirements_location = None
self._original_dir = os.path.abspath(os.curdir)
self.which = which
@@ -369,9 +384,7 @@ class Project(object):
"""Parse Pipfile into a TOMLFile and cache it
(call clear_pipfile_cache() afterwards if mutating)"""
# Open the pipfile, read it into memory.
with open(self.pipfile_location) as f:
contents = f.read()
contents = self.read_pipfile()
# use full contents to get around str/bytes 2/3 issues
cache_key = (self.pipfile_location, contents)
if cache_key not in _pipfile_cache:
@@ -379,10 +392,17 @@ class Project(object):
_pipfile_cache[cache_key] = parsed
return _pipfile_cache[cache_key]
def read_pipfile(self):
# Open the pipfile, read it into memory.
with io.open(self.pipfile_location) as f:
contents = f.read()
self._pipfile_newlines = preferred_newlines(f)
return contents
@property
def pased_pure_pipfile(self):
with open(self.pipfile_location) as f:
contents = f.read()
contents = self.read_pipfile()
return self._parse_pipfile(contents)
@@ -474,14 +494,7 @@ class Project(object):
@property
def lockfile_content(self):
with open(self.lockfile_location) as lock:
j = json.load(lock)
# Expand environment variables in Pipfile.lock at runtime.
for i, source in enumerate(j['_meta']['sources'][:]):
j['_meta']['sources'][i]['url'] = os.path.expandvars(j['_meta']['sources'][i]['url'])
return j
return self.load_lockfile()
@property
def editable_packages(self):
@@ -546,9 +559,8 @@ class Project(object):
if not self.pipfile_exists:
return True
with open(self.pipfile_location, 'r') as f:
if not f.read():
return True
if not len(self.read_pipfile()):
return True
return False
@@ -572,7 +584,7 @@ class Project(object):
u'name': source_name,
}
)
data = {
u'source': sources,
# Default packages.
@@ -610,12 +622,29 @@ class Project(object):
)
data[section][package].update(_data)
formatted_data = toml.dumps(data).rstrip()
if Path(path).absolute() == Path(self.pipfile_location).absolute():
newlines = self._pipfile_newlines
else:
newlines = DEFAULT_NEWLINES
formatted_data = cleanup_toml(formatted_data)
with open(path, 'w') as f:
with io.open(path, 'w', newline=newlines) as f:
f.write(formatted_data)
# pipfile is mutated!
self.clear_pipfile_cache()
def write_lockfile(self, content):
"""Write out the lockfile.
"""
newlines = self._lockfile_newlines
s = simplejson.dumps( # Send Unicode in to guarentee Unicode out.
content, indent=4, separators=(u',', u': '), sort_keys=True,
)
with atomic_open_for_write(self.lockfile_location, newline=newlines) as f:
f.write(s)
if not s.endswith(u'\n'):
f.write(u'\n') # Write newline at end of document. GH #319.
@property
def pipfile_sources(self):
if 'source' in self.parsed_pipfile:
@@ -632,7 +661,7 @@ class Project(object):
@property
def sources(self):
if self.lockfile_exists:
if self.lockfile_exists and hasattr(self.lockfile_content, 'keys'):
meta_ = self.lockfile_content['_meta']
sources_ = meta_.get('sources')
if sources_:
@@ -735,13 +764,30 @@ class Project(object):
if self.ensure_proper_casing():
self.write_toml(self.parsed_pipfile)
def load_lockfile(self, expand_env_vars=True):
with io.open(self.lockfile_location) as lock:
j = json.load(lock)
self._lockfile_newlines = preferred_newlines(lock)
# lockfile is just a string
if not j or not hasattr(j, 'keys'):
return j
if expand_env_vars:
# Expand environment variables in Pipfile.lock at runtime.
for i, source in enumerate(j['_meta']['sources'][:]):
j['_meta']['sources'][i]['url'] = os.path.expandvars(j['_meta']['sources'][i]['url'])
return j
def get_lockfile_hash(self):
if not os.path.exists(self.lockfile_location):
return
# Open the lockfile.
with codecs.open(self.lockfile_location, 'r') as f:
lockfile = json.load(f)
return lockfile['_meta'].get('hash', {}).get('sha256')
lockfile = self.load_lockfile(expand_env_vars=False)
if '_meta' in lockfile and hasattr(lockfile, 'keys'):
return lockfile['_meta'].get('hash', {}).get('sha256')
# Lockfile exists but has no hash at all
return ''
def calculate_pipfile_hash(self):
# Update the lockfile if it is out-of-date.
+12 -4
View File
@@ -657,6 +657,7 @@ def convert_deps_to_pip(deps, project=None, r=True, include_index=False):
for dep in deps.keys():
# Default (e.g. '>1.10').
extra = deps[dep] if isinstance(deps[dep], six.string_types) else ''
extras = ''
version = ''
index = ''
# Get rid of '*'.
@@ -675,7 +676,7 @@ def convert_deps_to_pip(deps, project=None, r=True, include_index=False):
)
# Support for extras (e.g. requests[socks])
if 'extras' in deps[dep]:
extra = '[{0}]'.format(','.join(deps[dep]['extras']))
extras = '[{0}]'.format(','.join(deps[dep]['extras']))
if 'version' in deps[dep]:
if not is_star(deps[dep]['version']):
version = deps[dep]['version']
@@ -709,9 +710,14 @@ def convert_deps_to_pip(deps, project=None, r=True, include_index=False):
# Support for version control
maybe_vcs = [vcs for vcs in VCS_LIST if vcs in deps[dep]]
vcs = maybe_vcs[0] if maybe_vcs else None
if not any(key in deps[dep] for key in ['path', 'vcs', 'file']):
extra += extras
# Support for files.
if 'file' in deps[dep]:
extra = '{1}{0}'.format(extra, deps[dep]['file']).strip()
dep_file = deps[dep]['file']
if is_valid_url(dep_file) and dep_file.startswith('http'):
dep_file += '#egg={0}'.format(dep)
extra = '{0}{1}'.format(dep_file, extras).strip()
# Flag the file as editable if it is a local relative path
if 'editable' in deps[dep]:
dep = '-e '
@@ -719,7 +725,7 @@ def convert_deps_to_pip(deps, project=None, r=True, include_index=False):
dep = ''
# Support for paths.
elif 'path' in deps[dep]:
extra = '{1}{0}'.format(extra, deps[dep]['path']).strip()
extra = '{1}{0}'.format(extras, deps[dep]['path']).strip()
# Flag the file as editable if it is a local relative path
if 'editable' in deps[dep]:
dep = '-e '
@@ -730,7 +736,7 @@ def convert_deps_to_pip(deps, project=None, r=True, include_index=False):
# Support for @refs.
if 'ref' in deps[dep]:
extra += '@{0}'.format(deps[dep]['ref'])
extra += '#egg={0}'.format(dep)
extra += '#egg={0}{1}'.format(dep, extras)
# Support for subdirectory
if 'subdirectory' in deps[dep]:
extra += '&subdirectory={0}'.format(deps[dep]['subdirectory'])
@@ -740,6 +746,7 @@ def convert_deps_to_pip(deps, project=None, r=True, include_index=False):
dep = '-e '
else:
dep = ''
s = '{0}{1}{2}{3}{4} {5}'.format(
dep, extra, version, specs, hash, index
).strip()
@@ -1305,6 +1312,7 @@ def atomic_open_for_write(target, binary=False, newline=None, encoding=None):
target with this new file.
"""
from ._compat import NamedTemporaryFile
mode = 'w+b' if binary else 'w'
f = NamedTemporaryFile(
dir=os.path.dirname(target),
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright 2016 Kenneth Reitz
Copyright 2017 Kenneth Reitz
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+21
View File
@@ -0,0 +1,21 @@
setuptools==39.1.0
appdirs==1.4.0
distlib==0.2.4
distro==1.2.0
html5lib==1.0b10
six==1.10.0
colorama==0.3.7
requests==2.18.4
chardet==3.0.4
idna==2.6
urllib3==1.22
certifi==2018.1.18
CacheControl==0.11.7
lockfile==0.12.2
ordereddict==1.1
progress==1.2
ipaddress==1.0.17
packaging==16.8
pyparsing==2.1.10
retrying==1.3.3
webencodings==0.5
+21
View File
@@ -0,0 +1,21 @@
setuptools==39.1.0
appdirs==1.4.0
distlib==0.2.4
distro==1.2.0
html5lib==1.0b10
six==1.10.0
colorama==0.3.7
requests==2.18.4
chardet==3.0.4
idna==2.6
urllib3==1.22
certifi==2018.1.18
CacheControl==0.11.7
lockfile==0.12.2
ordereddict==1.1
progress==1.2
ipaddress==1.0.17
packaging==16.8
pyparsing==2.1.10
retrying==1.3.3
webencodings==0.5
+36 -64
View File
@@ -17,13 +17,14 @@ import requests
TASK_NAME = 'update'
LIBRARY_OVERRIDES = {
LIBRARY_DIRNAMES = {
'requirements-parser': 'requirements',
'backports.shutil_get_terminal_size': 'backports/shutil_get_terminal_size',
'backports.weakref': 'backports/weakref',
'shutil_backports': 'backports/shutil_get_terminal_size',
'python-dotenv': 'dotenv',
'pip-tools': 'piptools'
'pip-tools': 'piptools',
'setuptools': 'pkg_resources',
}
# from time to time, remove the no longer needed ones
@@ -38,7 +39,10 @@ HARDCODED_LICENSE_URLS = {
'semver': 'https://raw.githubusercontent.com/k-bx/python-semver/master/LICENSE.txt',
'crayons': 'https://raw.githubusercontent.com/kennethreitz/crayons/master/LICENSE',
'pip-tools': 'https://raw.githubusercontent.com/jazzband/pip-tools/master/LICENSE',
'pew': 'https://raw.githubusercontent.com/berdario/pew/master/LICENSE'
'pew': 'https://raw.githubusercontent.com/berdario/pew/master/LICENSE',
'pytoml': 'https://github.com/avakar/pytoml/raw/master/LICENSE',
'webencodings': 'https://github.com/SimonSapin/python-webencodings/raw/'
'master/LICENSE',
}
FILE_WHITE_LIST = (
@@ -50,7 +54,8 @@ FILE_WHITE_LIST = (
'README.md',
'appdirs.py',
'safety.zip',
'cacert.pem'
'cacert.pem',
'vendor_pip.txt',
)
LIBRARY_RENAMES = {
@@ -212,14 +217,6 @@ cli(prog_name="safety")
else:
lib = yaml_build_dir / 'lib3' / 'yaml'
shutil.copytree(str(lib.absolute()), str(safety_dir / 'yaml{0}'.format(version_choices[0])))
# yaml_init = yaml_dir / '__init__.py'
# yaml_init.write_text("""
# import sys
# if sys.version_info[0] == 3:
# from .yaml3 import *
# else:
# from .yaml2 import *
# """.strip())
requests_dir = safety_dir / 'requests'
cacert = vendor_dir / 'requests' / 'cacert.pem'
if not cacert.exists():
@@ -247,8 +244,8 @@ cli(prog_name="safety")
def rename_if_needed(ctx, vendor_dir, item):
rename_dict = LIBRARY_RENAMES if vendor_dir.name != 'patched' else PATCHED_RENAMES
new_path = None
if item.name in rename_dict or item.name in LIBRARY_OVERRIDES:
new_name = rename_dict.get(item.name, LIBRARY_OVERRIDES.get(item.name))
if item.name in rename_dict or item.name in LIBRARY_DIRNAMES:
new_name = rename_dict.get(item.name, LIBRARY_DIRNAMES.get(item.name))
new_path = item.parent / new_name
log('Renaming %s => %s' % (item.name, new_path))
# handle existing directories
@@ -307,7 +304,6 @@ def vendor(ctx, vendor_dir, rewrite=True):
apply_patch(ctx, patch)
# Global import rewrites
# log("Rewriting all imports related to vendored libs")
log('Renaming specified libs...')
for item in vendor_dir.iterdir():
if item.is_dir():
@@ -401,16 +397,17 @@ def find_and_extract_license(vendor_dir, tar, members):
def license_fallback(vendor_dir, sdist_name):
"""Hardcoded license URLs. Check when updating if those are still needed"""
for libname, url in HARDCODED_LICENSE_URLS.items():
if libname in sdist_name:
_, _, name = url.rpartition('/')
dest = license_destination(vendor_dir, libname, name)
r = requests.get(url, allow_redirects=True)
log('Downloading {}'.format(url))
r.raise_for_status()
dest.write_bytes(r.content)
return
raise ValueError('No hardcoded URL for {} license'.format(sdist_name))
libname = libname_from_dir(sdist_name)
if libname not in HARDCODED_LICENSE_URLS:
raise ValueError('No hardcoded URL for {} license'.format(libname))
url = HARDCODED_LICENSE_URLS[libname]
_, _, name = url.rpartition('/')
dest = license_destination(vendor_dir, libname, name)
r = requests.get(url, allow_redirects=True)
log('Downloading {}'.format(url))
r.raise_for_status()
dest.write_bytes(r.content)
def libname_from_dir(dirname):
@@ -420,7 +417,7 @@ def libname_from_dir(dirname):
if part[0].isdigit():
break
parts.append(part)
return'-'.join(parts)
return '-'.join(parts)
def license_destination(vendor_dir, libname, filename):
@@ -432,16 +429,17 @@ def license_destination(vendor_dir, libname, filename):
if lowercase.is_dir():
return lowercase / filename
rename_dict = LIBRARY_RENAMES if vendor_dir.name != 'patched' else PATCHED_RENAMES
# Short circuit all logic if we are renaming the whole library
if libname in rename_dict:
return vendor_dir / rename_dict[libname] / filename
if libname in LIBRARY_OVERRIDES:
override = vendor_dir / LIBRARY_OVERRIDES[libname]
if libname in LIBRARY_DIRNAMES:
override = vendor_dir / LIBRARY_DIRNAMES[libname]
if not override.exists() and override.parent.exists():
# for flattened subdeps, specifically backports/weakref.py
target_dir = vendor_dir / override.parent
target_file = '{0}.{1}'.format(override.name, filename)
return target_dir / target_file
return vendor_dir / LIBRARY_OVERRIDES[libname] / filename
return (
vendor_dir / override.parent
) / '{0}.{1}'.format(override.name, filename)
return vendor_dir / LIBRARY_DIRNAMES[libname] / filename
# fallback to libname.LICENSE (used for nondirs)
return vendor_dir / '{}.{}'.format(libname, filename)
@@ -451,8 +449,6 @@ def extract_license_member(vendor_dir, tar, member, name):
dirname = list(mpath.parents)[-2].name # -1 is .
libname = libname_from_dir(dirname)
dest = license_destination(vendor_dir, libname, mpath.name)
# dest_relative = dest.relative_to(Path.cwd())
# log('Extracting {} into {}'.format(name, dest_relative))
log('Extracting {} into {}'.format(name, dest))
try:
fileobj = tar.extractfile(member)
@@ -461,36 +457,6 @@ def extract_license_member(vendor_dir, tar, member, name):
dest.write_bytes(tar.read(member))
@invoke.task
def update_stubs(ctx):
vendor_dir = _get_vendor_dir(ctx)
vendored_libs = detect_vendored_libs(vendor_dir)
print("[vendoring.update_stubs] Add mypy stubs")
extra_stubs_needed = {
# Some projects need stubs other than a simple <name>.pyi
"six": ["six.__init__", "six.moves"],
# Some projects should not have stubs coz they're single file modules
"appdirs": [],
}
for lib in vendored_libs:
if lib not in extra_stubs_needed:
(vendor_dir / (lib + ".pyi")).write_text("from %s import *" % lib)
continue
for selector in extra_stubs_needed[lib]:
fname = selector.replace(".", os.sep) + ".pyi"
if selector.endswith(".__init__"):
selector = selector[:-9]
f_path = vendor_dir / fname
if not f_path.parent.exists():
f_path.parent.mkdir()
f_path.write_text("from %s import *" % selector)
@invoke.task(name=TASK_NAME)
def main(ctx):
vendor_dir = _get_vendor_dir(ctx)
@@ -502,5 +468,11 @@ def main(ctx):
vendor(ctx, patched_dir, rewrite=False)
download_licenses(ctx, vendor_dir)
download_licenses(ctx, patched_dir, 'patched.txt')
for pip_dir in [vendor_dir / 'pip9', patched_dir / 'notpip']:
_vendor_dir = pip_dir / '_vendor'
vendor_src_file = vendor_dir / 'vendor_pip.txt'
vendor_file = _vendor_dir / 'vendor.txt'
vendor_file.write_bytes(vendor_src_file.read_bytes())
download_licenses(ctx, _vendor_dir)
# update_safety(ctx)
log('Revendoring complete')
@@ -55,3 +55,14 @@ index 48aaa35..bf1cba9 100644
url:
url of the resource pointed to (href of the link)
diff --git a/pipenv/patched/notpip/models/index.py b/pipenv/patched/notpip/models/index.py
index 25fd488d..db324287 100644
--- a/pipenv/patched/notpip/models/index.py
+++ b/pipenv/patched/notpip/models/index.py
@@ -13,4 +13,4 @@ class Index(object):
return urllib_parse.urljoin(self.url, path)
-PyPI = Index('https://pypi.python.org/')
+PyPI = Index('https://pypi.org/')
@@ -1,13 +0,0 @@
diff --git a/pipenv/patched/pipfile/api.py b/pipenv/patched/pipfile/api.py
index 18a1ea2..e8fa027 100644
--- a/pipenv/patched/pipfile/api.py
+++ b/pipenv/patched/pipfile/api.py
@@ -10,7 +10,7 @@ import os
DEFAULT_SOURCE = {
- u'url': u'https://pypi.python.org/simple',
+ u'url': u'https://pypi.org/simple',
u'verify_ssl': True,
u'name': u'pypi',
}
@@ -12,7 +12,7 @@ index 8a2a6a3..18a1ea2 100644
+DEFAULT_SOURCE = {
+ u'url': u'https://pypi.python.org/simple',
+ u'url': u'https://pypi.org/simple',
+ u'verify_ssl': True,
+ u'name': u'pypi',
+}
@@ -85,7 +85,8 @@ index d3b7fe7..e1f63d2 100644
+
+
class PyPIRepository(BaseRepository):
DEFAULT_INDEX_URL = 'https://pypi.python.org/simple'
- DEFAULT_INDEX_URL = 'https://pypi.python.org/simple'
+ DEFAULT_INDEX_URL = 'https://pypi.org/simple'
@@ -30,8 +69,9 @@ class PyPIRepository(BaseRepository):
config), but any other PyPI mirror can be used if index_urls is
@@ -10,19 +10,20 @@ index 59fd5748..48663aed 100644
+
from prettytoml.elements.common import ContainerElement
from prettytoml.elements import traversal
-class AbstractTable(ContainerElement, traversal.TraversalMixin):
+class AbstractTable(ContainerElement, traversal.TraversalMixin, Mapping):
"""
Common code for handling tables as key-value pairs with metadata elements sprinkled all over.
@@ -37,6 +42,9 @@ class AbstractTable(ContainerElement, traversal.TraversalMixin):
def __len__(self):
return len(tuple(self._enumerate_items()))
+ def __iter__(self):
+ return (key for key, _ in self.items())
+
def __contains__(self, item):
return item in self.keys()
+5 -1
View File
@@ -113,10 +113,14 @@ class _PipenvInstance(object):
@property
def lockfile(self):
p_path = os.sep.join([self.path, 'Pipfile.lock'])
p_path = self.lockfile_path
with open(p_path, 'r') as f:
return json.loads(f.read())
@property
def lockfile_path(self):
return os.sep.join([self.path, 'Pipfile.lock'])
@pytest.fixture()
def PipenvInstance():
+38
View File
@@ -1,4 +1,5 @@
# -*- coding=utf-8 -*-
import io
import pytest
import os
from pipenv.project import Project
@@ -73,3 +74,40 @@ six = {{version = "*", index = "pypi"}}
assert sorted(source.items()) == sorted(project.get_source(url=url).items())
assert sorted(source.items()) == sorted(project.find_source(name).items())
assert sorted(source.items()) == sorted(project.find_source(url).items())
@pytest.mark.install
@pytest.mark.project
@pytest.mark.parametrize('newlines', [u'\n', u'\r\n'])
def test_maintain_file_line_endings(PipenvInstance, pypi, newlines):
with PipenvInstance(pypi=pypi, chdir=True) as p:
# Initial pipfile + lockfile generation
c = p.pipenv('install pytz')
assert c.return_code == 0
# Rewrite each file with parameterized newlines
for fn in [p.pipfile_path, p.lockfile_path]:
with io.open(fn) as f:
contents = f.read()
written_newlines = f.newlines
assert written_newlines == u'\n', '{0!r} != {1!r} for {2}'.format(
written_newlines, u'\n', fn,
)
# message because of https://github.com/pytest-dev/pytest/issues/3443
with io.open(fn, 'w', newline=newlines) as f:
f.write(contents)
# Run pipenv install to programatically rewrite
c = p.pipenv('install chardet')
assert c.return_code == 0
# Make sure we kept the right newlines
for fn in [p.pipfile_path, p.lockfile_path]:
with io.open(fn) as f:
f.read() # Consumes the content to detect newlines.
actual_newlines = f.newlines
assert actual_newlines == newlines, '{0!r} != {1!r} for {2}'.format(
actual_newlines, newlines, fn,
)
# message because of https://github.com/pytest-dev/pytest/issues/3443
+42
View File
@@ -0,0 +1,42 @@
import pytest
@pytest.mark.sync
def test_sync_error_without_lockfile(PipenvInstance, pypi):
with PipenvInstance(pypi=pypi) as p:
with open(p.pipfile_path, 'w') as f:
f.write("""
[packages]
""".strip())
c = p.pipenv('sync')
assert c.return_code != 0
assert 'Pipfile.lock is missing!' in c.err
@pytest.mark.sync
@pytest.mark.lock
def test_sync_should_not_lock(PipenvInstance, pypi):
"""Sync should not touch the lock file, even if Pipfile is changed.
"""
with PipenvInstance(pypi=pypi) as p:
with open(p.pipfile_path, 'w') as f:
f.write("""
[packages]
""".strip())
# Perform initial lock.
c = p.pipenv('lock')
assert c.return_code == 0
lockfile_content = p.lockfile
assert lockfile_content
# Make sure sync does not trigger lockfile update.
with open(p.pipfile_path, 'w') as f:
f.write("""
[packages]
six = "*"
""".strip())
c = p.pipenv('sync')
assert c.return_code == 0
assert lockfile_content == p.lockfile
+15 -15
View File
@@ -44,7 +44,21 @@ DEP_PIP_PAIRS = [
}},
'-e svn+svn://svn.myproject.org/svn/MyProject#egg=MyProject',
),
(
# Extras in url
{'discord.py': {
'file': 'https://github.com/Rapptz/discord.py/archive/rewrite.zip',
'extras': ['voice']
}},
'https://github.com/Rapptz/discord.py/archive/rewrite.zip#egg=discord.py[voice]',
),
(
{'requests': {
'git': 'https://github.com/requests/requests.git',
'ref': 'master', 'extras': ['security'],
}},
'git+https://github.com/requests/requests.git@master#egg=requests[security]',
),
]
@@ -97,20 +111,6 @@ def test_convert_from_pip(expected, requirement):
assert pipenv.utils.convert_deps_from_pip(requirement) == expected
@pytest.mark.utils
@pytest.mark.parametrize('expected, requirement', [
( # XXX: This should work the other way around as well, but does not atm.
{'requests': {
'git': 'https://github.com/requests/requests.git',
'ref': 'master', 'extras': ['security'],
}},
'git+https://github.com/requests/requests.git@master#egg=requests[security]',
),
])
def test_convert_from_pip_vcs_with_extra(expected, requirement):
assert pipenv.utils.convert_deps_from_pip(requirement) == expected
@pytest.mark.utils
def test_convert_from_pip_fail_if_no_egg():
"""Parsing should fail without `#egg=`.