mirror of
https://github.com/kennethreitz/pipenv.git
synced 2026-06-05 22:50:18 +00:00
Fix all tests and split out requirements refactor
- includes missing artifacts - fixes cmdprse script - Remove extraneous changes - Fix tests
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
[dev-packages]
|
||||
|
||||
pipenv = {path = ".", editable = true}
|
||||
"flake8" = ">=3.3.0,<4"
|
||||
pytest = "*"
|
||||
@@ -13,14 +12,12 @@ pytest-pypy = {path = "./tests/pytest-pypi", editable = true}
|
||||
pytest-tap = "*"
|
||||
stdeb = {version="*", markers="sys_platform == 'linux'"}
|
||||
white = {version="*", markers="python_version >= '3.6'"}
|
||||
flaky = "*"
|
||||
|
||||
[packages]
|
||||
|
||||
|
||||
[scripts]
|
||||
|
||||
tests = "bash ./run-tests.sh"
|
||||
|
||||
[pipenv]
|
||||
|
||||
allow_prereleases = true
|
||||
|
||||
Generated
+15
-114
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "eab749ae861993f9e4b71bea88782c806a02fa82e3f45c82c4550ee459d8b886"
|
||||
"sha256": "5941b36256503b9729e927f3249a366748da5c353b59c1e32ca7fd919b31a4d6"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {},
|
||||
@@ -29,13 +29,6 @@
|
||||
],
|
||||
"version": "==1.4"
|
||||
},
|
||||
"asn1crypto": {
|
||||
"hashes": [
|
||||
"sha256:2f1adbb7546ed199e3c90ef23ec95c5cf3585bac7d11fb7eb562a3fe89c64e87",
|
||||
"sha256:9d5c20441baf0cb60a4ac34cc447c6c189024b6b4c6cd7877034f4965c464e49"
|
||||
],
|
||||
"version": "==0.24.0"
|
||||
},
|
||||
"attrs": {
|
||||
"hashes": [
|
||||
"sha256:1c7960ccfd6a005cd9f7ba884e6316b5e430a3f1a6c37c5f87d8b43f83b54ec9",
|
||||
@@ -57,39 +50,6 @@
|
||||
],
|
||||
"version": "==2018.1.18"
|
||||
},
|
||||
"cffi": {
|
||||
"hashes": [
|
||||
"sha256:151b7eefd035c56b2b2e1eb9963c90c6302dc15fbd8c1c0a83a163ff2c7d7743",
|
||||
"sha256:1553d1e99f035ace1c0544050622b7bc963374a00c467edafac50ad7bd276aef",
|
||||
"sha256:1b0493c091a1898f1136e3f4f991a784437fac3673780ff9de3bcf46c80b6b50",
|
||||
"sha256:2ba8a45822b7aee805ab49abfe7eec16b90587f7f26df20c71dd89e45a97076f",
|
||||
"sha256:3c85641778460581c42924384f5e68076d724ceac0f267d66c757f7535069c93",
|
||||
"sha256:3eb6434197633b7748cea30bf0ba9f66727cdce45117a712b29a443943733257",
|
||||
"sha256:4c91af6e967c2015729d3e69c2e51d92f9898c330d6a851bf8f121236f3defd3",
|
||||
"sha256:770f3782b31f50b68627e22f91cb182c48c47c02eb405fd689472aa7b7aa16dc",
|
||||
"sha256:79f9b6f7c46ae1f8ded75f68cf8ad50e5729ed4d590c74840471fc2823457d04",
|
||||
"sha256:7a33145e04d44ce95bcd71e522b478d282ad0eafaf34fe1ec5bbd73e662f22b6",
|
||||
"sha256:857959354ae3a6fa3da6651b966d13b0a8bed6bbc87a0de7b38a549db1d2a359",
|
||||
"sha256:87f37fe5130574ff76c17cab61e7d2538a16f843bb7bca8ebbc4b12de3078596",
|
||||
"sha256:95d5251e4b5ca00061f9d9f3d6fe537247e145a8524ae9fd30a2f8fbce993b5b",
|
||||
"sha256:9d1d3e63a4afdc29bd76ce6aa9d58c771cd1599fbba8cf5057e7860b203710dd",
|
||||
"sha256:a36c5c154f9d42ec176e6e620cb0dd275744aa1d804786a71ac37dc3661a5e95",
|
||||
"sha256:ae5e35a2c189d397b91034642cb0eab0e346f776ec2eb44a49a459e6615d6e2e",
|
||||
"sha256:b0f7d4a3df8f06cf49f9f121bead236e328074de6449866515cea4907bbc63d6",
|
||||
"sha256:b75110fb114fa366b29a027d0c9be3709579602ae111ff61674d28c93606acca",
|
||||
"sha256:ba5e697569f84b13640c9e193170e89c13c6244c24400fc57e88724ef610cd31",
|
||||
"sha256:be2a9b390f77fd7676d80bc3cdc4f8edb940d8c198ed2d8c0be1319018c778e1",
|
||||
"sha256:d5d8555d9bfc3f02385c1c37e9f998e2011f0db4f90e250e5bc0c0a85a813085",
|
||||
"sha256:e55e22ac0a30023426564b1059b035973ec82186ddddbac867078435801c7801",
|
||||
"sha256:e90f17980e6ab0f3c2f3730e56d1fe9bcba1891eeea58966e89d352492cc74f4",
|
||||
"sha256:ecbb7b01409e9b782df5ded849c178a0aa7c906cf8c5a67368047daab282b184",
|
||||
"sha256:ed01918d545a38998bfa5902c7c00e0fee90e957ce036a4000a88e3fe2264917",
|
||||
"sha256:edabd457cd23a02965166026fd9bfd196f4324fe6032e866d0f3bd0301cd486f",
|
||||
"sha256:fdf1c1dc5bafc32bc5d08b054f94d659422b05aba244d6be4ddc1c72d9aa70fb"
|
||||
],
|
||||
"markers": "platform_python_implementation != 'pypy'",
|
||||
"version": "==1.11.5"
|
||||
},
|
||||
"chardet": {
|
||||
"hashes": [
|
||||
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
|
||||
@@ -113,35 +73,6 @@
|
||||
"markers": "sys_platform == 'win32'",
|
||||
"version": "==0.3.9"
|
||||
},
|
||||
"configparser": {
|
||||
"hashes": [
|
||||
"sha256:5308b47021bc2340965c371f0f058cc6971a04502638d4244225c49d80db273a"
|
||||
],
|
||||
"markers": "python_version < '3.2'",
|
||||
"version": "==3.5.0"
|
||||
},
|
||||
"cryptography": {
|
||||
"hashes": [
|
||||
"sha256:3f3b65d5a16e6b52fba63dc860b62ca9832f51f1a2ae5083c78b6840275f12dd",
|
||||
"sha256:551a3abfe0c8c6833df4192a63371aa2ff43afd8f570ed345d31f251d78e7e04",
|
||||
"sha256:5cb990056b7cadcca26813311187ad751ea644712022a3976443691168781b6f",
|
||||
"sha256:60bda7f12ecb828358be53095fc9c6edda7de8f1ef571f96c00b2363643fa3cd",
|
||||
"sha256:6fef51ec447fe9f8351894024e94736862900d3a9aa2961528e602eb65c92bdb",
|
||||
"sha256:77d0ad229d47a6e0272d00f6bf8ac06ce14715a9fd02c9a97f5a2869aab3ccb2",
|
||||
"sha256:808fe471b1a6b777f026f7dc7bd9a4959da4bfab64972f2bbe91e22527c1c037",
|
||||
"sha256:9b62fb4d18529c84b961efd9187fecbb48e89aa1a0f9f4161c61b7fc42a101bd",
|
||||
"sha256:9e5bed45ec6b4f828866ac6a6bedf08388ffcfa68abe9e94b34bb40977aba531",
|
||||
"sha256:9fc295bf69130a342e7a19a39d7bbeb15c0bcaabc7382ec33ef3b2b7d18d2f63",
|
||||
"sha256:abd070b5849ed64e6d349199bef955ee0ad99aefbad792f0c587f8effa681a5e",
|
||||
"sha256:ba6a774749b6e510cffc2fb98535f717e0e5fd91c7c99a61d223293df79ab351",
|
||||
"sha256:c332118647f084c983c6a3e1dba0f3bcb051f69d12baccac68db8d62d177eb8a",
|
||||
"sha256:d6f46e862ee36df81e6342c2177ba84e70f722d9dc9c6c394f9f1f434c4a5563",
|
||||
"sha256:db6013746f73bf8edd9c3d1d3f94db635b9422f503db3fc5ef105233d4c011ab",
|
||||
"sha256:f57008eaff597c69cf692c3518f6d4800f0309253bb138b526a37fe9ef0c7471",
|
||||
"sha256:f6c821ac253c19f2ad4c8691633ae1d1a17f120d5b01ea1d256d7b602bc59887"
|
||||
],
|
||||
"version": "==2.2.2"
|
||||
},
|
||||
"docutils": {
|
||||
"hashes": [
|
||||
"sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6",
|
||||
@@ -150,16 +81,6 @@
|
||||
],
|
||||
"version": "==0.14"
|
||||
},
|
||||
"enum34": {
|
||||
"hashes": [
|
||||
"sha256:2d81cbbe0e73112bdfe6ef8576f2238f2ba27dd0d55752a776c41d38b7da2850",
|
||||
"sha256:644837f692e5f550741432dd3f223bbb9852018674981b1664e5dc339387588a",
|
||||
"sha256:6bd0f6ad48ec2aa117d3d141940d484deccda84d4fcd884f5c3d93c23ecd8c79",
|
||||
"sha256:8ad8c4783bf61ded74527bffb48ed9b54166685e4230386a9ed9b1279e2df5b1"
|
||||
],
|
||||
"markers": "python_version < '3'",
|
||||
"version": "==1.1.6"
|
||||
},
|
||||
"execnet": {
|
||||
"hashes": [
|
||||
"sha256:a7a84d5fa07a089186a329528f127c9d73b9de57f1a1131b82bb5320ee651f6a",
|
||||
@@ -175,6 +96,14 @@
|
||||
"index": "pypi",
|
||||
"version": "==3.5.0"
|
||||
},
|
||||
"flaky": {
|
||||
"hashes": [
|
||||
"sha256:4ad7880aef8c35a34ddb394d4fa33047765bca1e3d67d182bf6eba9c8eabf3a2",
|
||||
"sha256:d0533f473a46b916e6db6e84e20b06d8a70656600a0c14e819b0760b63f70226"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.4.0"
|
||||
},
|
||||
"flask": {
|
||||
"hashes": [
|
||||
"sha256:0749df235e3ff61ac108f69ac178c9770caeaccad2509cb762ce1f65570a8856",
|
||||
@@ -182,14 +111,6 @@
|
||||
],
|
||||
"version": "==0.12.2"
|
||||
},
|
||||
"funcsigs": {
|
||||
"hashes": [
|
||||
"sha256:330cc27ccbf7f1e992e69fef78261dc7c6569012cf397db8d3de0234e6c937ca",
|
||||
"sha256:a7bb0f2cf3a3fd1ab2732cb49eba4252c2af4240442415b4abce3b87022a8f50"
|
||||
],
|
||||
"markers": "python_version < '3.0'",
|
||||
"version": "==1.0.2"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f",
|
||||
@@ -204,13 +125,6 @@
|
||||
],
|
||||
"version": "==1.0.0"
|
||||
},
|
||||
"ipaddress": {
|
||||
"hashes": [
|
||||
"sha256:200d8686011d470b5e4de207d803445deee427455cd0cb7c982b68cf82524f81"
|
||||
],
|
||||
"markers": "python_version < '3'",
|
||||
"version": "==1.0.19"
|
||||
},
|
||||
"itsdangerous": {
|
||||
"hashes": [
|
||||
"sha256:cbb3fcf8d3e33df861709ecaf89d9e6629cff0a217bc2848f1b41cd30d360519"
|
||||
@@ -263,10 +177,10 @@
|
||||
},
|
||||
"pbr": {
|
||||
"hashes": [
|
||||
"sha256:8b9a7c3704657cb0831b3ded0a6b61377947c8235b76649bb7de4bfe2e6cfa56",
|
||||
"sha256:cf66675e22ae91a4f20e4b8354f117d3e3d1de651513051d109cc39645fb3672"
|
||||
"sha256:56b7a8ba7d64bf6135a9dfefb85a80d95924b3fde5ed6343a1a1d464a040dae3",
|
||||
"sha256:de75cf1d510542c746beeff66b52241eb12c8f95f2ef846ee50ed5d72392caa4"
|
||||
],
|
||||
"version": "==4.0.0"
|
||||
"version": "==4.0.1"
|
||||
},
|
||||
"pipenv": {
|
||||
"editable": true,
|
||||
@@ -299,12 +213,6 @@
|
||||
],
|
||||
"version": "==2.3.1"
|
||||
},
|
||||
"pycparser": {
|
||||
"hashes": [
|
||||
"sha256:99a8ca03e29851d96616ad0404b4aad7d9ee16f25c9f9708a11faf2810f7b226"
|
||||
],
|
||||
"version": "==2.18"
|
||||
},
|
||||
"pyflakes": {
|
||||
"hashes": [
|
||||
"sha256:08bd6a50edf8cffa9fa09a463063c425ecaaf10d1eb0335a7e8b1401aef89e6f",
|
||||
@@ -319,13 +227,6 @@
|
||||
],
|
||||
"version": "==2.2.0"
|
||||
},
|
||||
"pyopenssl": {
|
||||
"hashes": [
|
||||
"sha256:07a2de1a54de07448732a81e38a55df7da109b2f47f599f8bb35b0cbec69d4bd",
|
||||
"sha256:2c10cfba46a52c0b0950118981d61e72c1e5b1aac451ca1bc77de1a679456773"
|
||||
],
|
||||
"version": "==17.5.0"
|
||||
},
|
||||
"pytest": {
|
||||
"hashes": [
|
||||
"sha256:6266f87ab64692112e5477eba395cfedda53b1933ccd29478e671e73b420c19c",
|
||||
@@ -436,10 +337,10 @@
|
||||
},
|
||||
"tqdm": {
|
||||
"hashes": [
|
||||
"sha256:782aa84b61a5246c4f9e5b938875009e0b759d9a5c9d16b12e4f8deefdff7892",
|
||||
"sha256:eaf9c32a2cf7b9623eb272134b934eb354ef6304299611aa71e3cf47cf83c22b"
|
||||
"sha256:4f2eb1d14804caf7095500fe11da0e481a47af912e7b57c93f886ac3c40a49dd",
|
||||
"sha256:91ac47ec2ba6bb92b7ba37706f4dea37019ddd784b22fd279a4b12d93327191d"
|
||||
],
|
||||
"version": "==4.19.9"
|
||||
"version": "==4.20.0"
|
||||
},
|
||||
"twine": {
|
||||
"hashes": [
|
||||
|
||||
+3
-1
@@ -35,7 +35,7 @@ class Script(object):
|
||||
def extend(self, extra_args):
|
||||
self._parts.extend(extra_args)
|
||||
|
||||
def cmdify(self):
|
||||
def cmdify(self, extra_args=None):
|
||||
"""Encode into a cmd-executable string.
|
||||
|
||||
This re-implements CreateProcess's quoting logic to turn a list of
|
||||
@@ -54,6 +54,8 @@ class Script(object):
|
||||
|
||||
See also: https://docs.python.org/3/library/subprocess.html#converting-argument-sequence
|
||||
"""
|
||||
if extra_args:
|
||||
self.extend(extra_args)
|
||||
return ' '.join(
|
||||
'"{0}"'.format(re.sub(r'(\\*)"', r'\1\1\\"', arg))
|
||||
for arg in self._parts
|
||||
|
||||
+4
-6
@@ -25,8 +25,7 @@ from blindspin import spinner
|
||||
from requests.packages import urllib3
|
||||
from requests.packages.urllib3.exceptions import InsecureRequestWarning
|
||||
|
||||
from.project import Project
|
||||
from .requirements import PipenvRequirement
|
||||
from .project import Project
|
||||
from .utils import (
|
||||
convert_deps_from_pip,
|
||||
convert_deps_to_pip,
|
||||
@@ -1400,9 +1399,9 @@ def pip_install(
|
||||
f.write(package_name)
|
||||
# Install dependencies when a package is a VCS dependency.
|
||||
try:
|
||||
req = PipenvRequirement.from_line(
|
||||
req = get_requirement(
|
||||
package_name.split('--hash')[0].split('--trusted-host')[0]
|
||||
).requirement.vcs
|
||||
).vcs
|
||||
except (pip9._vendor.pyparsing.ParseException, ValueError) as e:
|
||||
click.echo('{0}: {1}'.format(crayons.red('WARNING'), e), err=True)
|
||||
click.echo(
|
||||
@@ -2472,8 +2471,7 @@ def do_clean(
|
||||
)
|
||||
installed_package_names = []
|
||||
for installed in installed_packages:
|
||||
pipenvreq = PipenvRequirement.from_line(installed)
|
||||
r = pipenvreq.requirement
|
||||
r = get_requirement(installed)
|
||||
# Ignore editable installations.
|
||||
if not r.editable:
|
||||
installed_package_names.append(r.name.lower())
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+237
-15
@@ -65,7 +65,8 @@ requests = requests.Session()
|
||||
|
||||
|
||||
def get_requirement(dep):
|
||||
|
||||
from pip9.req.req_install import _strip_extras
|
||||
import requirements
|
||||
"""Pre-clean requirement strings passed to the requirements parser.
|
||||
|
||||
Ensures that we can accept both local and relative paths, file and VCS URIs,
|
||||
@@ -76,8 +77,79 @@ def get_requirement(dep):
|
||||
:param str dep: A requirement line
|
||||
:returns: :class:`requirements.Requirement` object
|
||||
"""
|
||||
from .requirements import PipenvRequirement
|
||||
return PipenvRequirement.from_line(dep).requirement
|
||||
path = None
|
||||
uri = None
|
||||
cleaned_uri = None
|
||||
editable = False
|
||||
dep_link = None
|
||||
# check for editable dep / vcs dep
|
||||
if dep.startswith('-e '):
|
||||
editable = True
|
||||
# Use the user supplied path as the written dependency
|
||||
dep = dep.split(' ', 1)[1]
|
||||
# Split out markers if they are present - similar to how pip does it
|
||||
# See pip9.req.req_install.InstallRequirement.from_line
|
||||
if not any(dep.startswith(uri_prefix) for uri_prefix in SCHEME_LIST):
|
||||
marker_sep = ';'
|
||||
else:
|
||||
marker_sep = '; '
|
||||
if marker_sep in dep:
|
||||
dep, markers = dep.split(marker_sep, 1)
|
||||
markers = markers.strip()
|
||||
if not markers:
|
||||
markers = None
|
||||
else:
|
||||
markers = None
|
||||
# Strip extras from the requirement so we can make a properly parseable req
|
||||
dep, extras = _strip_extras(dep)
|
||||
# Only operate on local, existing, non-URI formatted paths which are installable
|
||||
if is_installable_file(dep):
|
||||
dep_path = Path(dep)
|
||||
dep_link = Link(dep_path.absolute().as_uri())
|
||||
if dep_path.is_absolute() or dep_path.as_posix() == '.':
|
||||
path = dep_path.as_posix()
|
||||
else:
|
||||
path = get_converted_relative_path(dep)
|
||||
dep = dep_link.egg_fragment if dep_link.egg_fragment else dep_link.url_without_fragment
|
||||
elif is_vcs(dep):
|
||||
# Generate a Link object for parsing egg fragments
|
||||
dep_link = Link(dep)
|
||||
# Save the original path to store in the pipfile
|
||||
uri = dep_link.url
|
||||
# Construct the requirement using proper git+ssh:// replaced uris or names if available
|
||||
cleaned_uri = clean_git_uri(dep)
|
||||
dep = cleaned_uri
|
||||
if editable:
|
||||
dep = '-e {0}'.format(dep)
|
||||
req = [r for r in requirements.parse(dep)][0]
|
||||
# if all we built was the requirement name and still need everything else
|
||||
if req.name and not any([req.uri, req.path]):
|
||||
if dep_link:
|
||||
if dep_link.scheme.startswith('file') and path and not req.path:
|
||||
req.path = path
|
||||
req.local_file = True
|
||||
req.uri = None
|
||||
else:
|
||||
req.uri = dep_link.url_without_fragment
|
||||
# If the result is a local file with a URI and we have a local path, unset the URI
|
||||
# and set the path instead -- note that local files may have 'path' set by accident
|
||||
elif req.local_file and path and not req.vcs:
|
||||
req.path = path
|
||||
req.uri = None
|
||||
elif req.vcs and req.uri and cleaned_uri and cleaned_uri != uri:
|
||||
req.uri = strip_ssh_from_git_uri(req.uri)
|
||||
req.line = strip_ssh_from_git_uri(req.line)
|
||||
req.editable = editable
|
||||
if markers:
|
||||
req.markers = markers
|
||||
if extras:
|
||||
# Bizarrely this is also what pip does...
|
||||
req.extras = [
|
||||
r for r in requirements.parse('fakepkg{0}'.format(extras))
|
||||
][
|
||||
0
|
||||
].extras
|
||||
return req
|
||||
|
||||
|
||||
def cleanup_toml(tml):
|
||||
@@ -441,9 +513,81 @@ def multi_split(s, split):
|
||||
|
||||
def convert_deps_from_pip(dep):
|
||||
""""Converts a pip-formatted dependency to a Pipfile-formatted one."""
|
||||
from .requirements import PipenvRequirement
|
||||
req = PipenvRequirement.from_line(dep)
|
||||
return req.as_pipfile()
|
||||
dependency = {}
|
||||
req = get_requirement(dep)
|
||||
extras = {'extras': req.extras}
|
||||
# File installs.
|
||||
if (req.uri or req.path or is_installable_file(req.name)) and not req.vcs:
|
||||
# Assign a package name to the file, last 7 of it's sha256 hex digest.
|
||||
if not req.uri and not req.path:
|
||||
req.path = os.path.abspath(req.name)
|
||||
hashable_path = req.uri if req.uri else req.path
|
||||
req.name = hashlib.sha256(hashable_path.encode('utf-8')).hexdigest()
|
||||
req.name = req.name[len(req.name) - 7:]
|
||||
# {path: uri} TOML (spec 4 I guess...)
|
||||
if req.uri:
|
||||
dependency[req.name] = {'file': hashable_path}
|
||||
else:
|
||||
dependency[req.name] = {'path': hashable_path}
|
||||
if req.extras:
|
||||
dependency[req.name].update(extras)
|
||||
# Add --editable if applicable
|
||||
if req.editable:
|
||||
dependency[req.name].update({'editable': True})
|
||||
# VCS Installs.
|
||||
elif req.vcs:
|
||||
if req.name is None:
|
||||
raise ValueError(
|
||||
'pipenv requires an #egg fragment for version controlled '
|
||||
'dependencies. Please install remote dependency '
|
||||
'in the form {0}#egg=<package-name>.'.format(req.uri)
|
||||
)
|
||||
|
||||
# Crop off the git+, etc part.
|
||||
if req.uri.startswith('{0}+'.format(req.vcs)):
|
||||
req.uri = req.uri[len(req.vcs) + 1:]
|
||||
dependency.setdefault(req.name, {}).update({req.vcs: req.uri})
|
||||
# Add --editable, if it's there.
|
||||
if req.editable:
|
||||
dependency[req.name].update({'editable': True})
|
||||
# Add subdirectory, if it's there
|
||||
if req.subdirectory:
|
||||
dependency[req.name].update({'subdirectory': req.subdirectory})
|
||||
# Add the specifier, if it was provided.
|
||||
if req.revision:
|
||||
dependency[req.name].update({'ref': req.revision})
|
||||
# Extras: e.g. #egg=requests[security]
|
||||
if req.extras:
|
||||
dependency[req.name].update({'extras': req.extras})
|
||||
elif req.extras or req.specs or req.markers:
|
||||
specs = None
|
||||
keys = [k for k in ['specs', 'extras', 'markers'] if getattr(req, k, None)]
|
||||
|
||||
# Comparison operators: e.g. Django>1.10
|
||||
if req.specs:
|
||||
r = multi_split(dep, '!=<>~')
|
||||
specs = dep[len(r[0]):]
|
||||
|
||||
if len(keys) > 1:
|
||||
dependency[req.name] = {}
|
||||
for k in keys:
|
||||
entry_key = 'version' if k == 'specs' else k
|
||||
entry = {entry_key: getattr(req, k)} if k != 'extras' else extras
|
||||
dependency[req.name].update(entry)
|
||||
else:
|
||||
entry_key = 'version' if keys[0] == 'specs' else keys[0]
|
||||
entry = {entry_key: getattr(req, keys[0])} if entry_key != 'extras' else extras
|
||||
dependency[req.name] = entry
|
||||
|
||||
# Bare dependencies: e.g. requests
|
||||
else:
|
||||
dependency[dep] = '*'
|
||||
# Cleanup when there's multiple values, e.g. -e.
|
||||
if len(dependency) > 1:
|
||||
for key in dependency.copy():
|
||||
if not hasattr(dependency[key], 'keys'):
|
||||
del dependency[key]
|
||||
return dependency
|
||||
|
||||
|
||||
def is_star(val):
|
||||
@@ -456,16 +600,94 @@ def is_pinned(val):
|
||||
|
||||
def convert_deps_to_pip(deps, project=None, r=True, include_index=False):
|
||||
""""Converts a Pipfile-formatted dependency to a pip-formatted one."""
|
||||
from .requirements import PipenvRequirement
|
||||
dependencies = []
|
||||
for dep_name, dep in deps.items():
|
||||
indexes = project.sources if hasattr(project, 'sources') else None
|
||||
if hasattr(dep, 'keys') and dep.get('index'):
|
||||
indexes = project.get_source(dep['index'])
|
||||
new_dep = PipenvRequirement.from_pipfile(dep_name, indexes, dep)
|
||||
req = new_dep.as_requirement(project=project, include_index=include_index)
|
||||
req = req.strip()
|
||||
dependencies.append(req)
|
||||
for dep in deps.keys():
|
||||
# Default (e.g. '>1.10').
|
||||
extra = deps[dep] if isinstance(deps[dep], six.string_types) else ''
|
||||
version = ''
|
||||
index = ''
|
||||
# Get rid of '*'.
|
||||
if is_star(deps[dep]) or str(extra) == '{}':
|
||||
extra = ''
|
||||
hash = ''
|
||||
# Support for single hash (spec 1).
|
||||
if 'hash' in deps[dep]:
|
||||
hash = ' --hash={0}'.format(deps[dep]['hash'])
|
||||
# Support for multiple hashes (spec 2).
|
||||
if 'hashes' in deps[dep]:
|
||||
hash = '{0} '.format(
|
||||
''.join(
|
||||
[' --hash={0} '.format(h) for h in deps[dep]['hashes']]
|
||||
)
|
||||
)
|
||||
# Support for extras (e.g. requests[socks])
|
||||
if 'extras' in deps[dep]:
|
||||
extra = '[{0}]'.format(','.join(deps[dep]['extras']))
|
||||
if 'version' in deps[dep]:
|
||||
if not is_star(deps[dep]['version']):
|
||||
version = deps[dep]['version']
|
||||
# For lockfile format.
|
||||
if 'markers' in deps[dep]:
|
||||
specs = '; {0}'.format(deps[dep]['markers'])
|
||||
else:
|
||||
# For pipfile format.
|
||||
specs = []
|
||||
for specifier in specifiers:
|
||||
if specifier in deps[dep]:
|
||||
if not is_star(deps[dep][specifier]):
|
||||
specs.append(
|
||||
'{0} {1}'.format(specifier, deps[dep][specifier])
|
||||
)
|
||||
if specs:
|
||||
specs = '; {0}'.format(' and '.join(specs))
|
||||
else:
|
||||
specs = ''
|
||||
if include_index and not is_file(deps[dep]) and not is_vcs(deps[dep]):
|
||||
pip_src_args = []
|
||||
if 'index' in deps[dep]:
|
||||
pip_src_args = [project.get_source(deps[dep]['index'])]
|
||||
else:
|
||||
pip_src_args = project.sources
|
||||
pip_args = prepare_pip_source_args(pip_src_args)
|
||||
index = ' '.join(pip_args)
|
||||
# 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
|
||||
# Support for files.
|
||||
if 'file' in deps[dep]:
|
||||
extra = '{1}{0}'.format(extra, deps[dep]['file']).strip()
|
||||
# Flag the file as editable if it is a local relative path
|
||||
if 'editable' in deps[dep]:
|
||||
dep = '-e '
|
||||
else:
|
||||
dep = ''
|
||||
# Support for paths.
|
||||
elif 'path' in deps[dep]:
|
||||
extra = '{1}{0}'.format(extra, deps[dep]['path']).strip()
|
||||
# Flag the file as editable if it is a local relative path
|
||||
if 'editable' in deps[dep]:
|
||||
dep = '-e '
|
||||
else:
|
||||
dep = ''
|
||||
if vcs:
|
||||
extra = '{0}+{1}'.format(vcs, deps[dep][vcs])
|
||||
# Support for @refs.
|
||||
if 'ref' in deps[dep]:
|
||||
extra += '@{0}'.format(deps[dep]['ref'])
|
||||
extra += '#egg={0}'.format(dep)
|
||||
# Support for subdirectory
|
||||
if 'subdirectory' in deps[dep]:
|
||||
extra += '&subdirectory={0}'.format(deps[dep]['subdirectory'])
|
||||
# Support for editable.
|
||||
if 'editable' in deps[dep]:
|
||||
# Support for --egg.
|
||||
dep = '-e '
|
||||
else:
|
||||
dep = ''
|
||||
s = '{0}{1}{2}{3}{4} {5}'.format(
|
||||
dep, extra, version, specs, hash, index
|
||||
).strip()
|
||||
dependencies.append(s)
|
||||
if not r:
|
||||
return dependencies
|
||||
|
||||
|
||||
Vendored
-57
@@ -1,57 +0,0 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
from functools import partial
|
||||
|
||||
from . import converters, exceptions, filters, validators
|
||||
from ._config import get_run_validators, set_run_validators
|
||||
from ._funcs import asdict, assoc, astuple, evolve, has
|
||||
from ._make import (
|
||||
NOTHING, Attribute, Factory, attrib, attrs, fields, fields_dict,
|
||||
make_class, validate
|
||||
)
|
||||
|
||||
|
||||
__version__ = "18.1.0.dev0"
|
||||
|
||||
__title__ = "attrs"
|
||||
__description__ = "Classes Without Boilerplate"
|
||||
__uri__ = "http://www.attrs.org/"
|
||||
__doc__ = __description__ + " <" + __uri__ + ">"
|
||||
|
||||
__author__ = "Hynek Schlawack"
|
||||
__email__ = "hs@ox.cx"
|
||||
|
||||
__license__ = "MIT"
|
||||
__copyright__ = "Copyright (c) 2015 Hynek Schlawack"
|
||||
|
||||
|
||||
s = attributes = attrs
|
||||
ib = attr = attrib
|
||||
dataclass = partial(attrs, auto_attribs=True) # happy Easter ;)
|
||||
|
||||
__all__ = [
|
||||
"Attribute",
|
||||
"Factory",
|
||||
"NOTHING",
|
||||
"asdict",
|
||||
"assoc",
|
||||
"astuple",
|
||||
"attr",
|
||||
"attrib",
|
||||
"attributes",
|
||||
"attrs",
|
||||
"converters",
|
||||
"evolve",
|
||||
"exceptions",
|
||||
"fields",
|
||||
"fields_dict",
|
||||
"filters",
|
||||
"get_run_validators",
|
||||
"has",
|
||||
"ib",
|
||||
"make_class",
|
||||
"s",
|
||||
"set_run_validators",
|
||||
"validate",
|
||||
"validators",
|
||||
]
|
||||
Vendored
-146
@@ -1,146 +0,0 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
import platform
|
||||
import sys
|
||||
import types
|
||||
import warnings
|
||||
|
||||
|
||||
PY2 = sys.version_info[0] == 2
|
||||
PYPY = platform.python_implementation() == "PyPy"
|
||||
|
||||
|
||||
if PYPY or sys.version_info[:2] >= (3, 6):
|
||||
ordered_dict = dict
|
||||
else:
|
||||
from collections import OrderedDict
|
||||
ordered_dict = OrderedDict
|
||||
|
||||
|
||||
if PY2:
|
||||
from UserDict import IterableUserDict
|
||||
|
||||
# We 'bundle' isclass instead of using inspect as importing inspect is
|
||||
# fairly expensive (order of 10-15 ms for a modern machine in 2016)
|
||||
def isclass(klass):
|
||||
return isinstance(klass, (type, types.ClassType))
|
||||
|
||||
# TYPE is used in exceptions, repr(int) is different on Python 2 and 3.
|
||||
TYPE = "type"
|
||||
|
||||
def iteritems(d):
|
||||
return d.iteritems()
|
||||
|
||||
# Python 2 is bereft of a read-only dict proxy, so we make one!
|
||||
class ReadOnlyDict(IterableUserDict):
|
||||
"""
|
||||
Best-effort read-only dict wrapper.
|
||||
"""
|
||||
|
||||
def __setitem__(self, key, val):
|
||||
# We gently pretend we're a Python 3 mappingproxy.
|
||||
raise TypeError("'mappingproxy' object does not support item "
|
||||
"assignment")
|
||||
|
||||
def update(self, _):
|
||||
# We gently pretend we're a Python 3 mappingproxy.
|
||||
raise AttributeError("'mappingproxy' object has no attribute "
|
||||
"'update'")
|
||||
|
||||
def __delitem__(self, _):
|
||||
# We gently pretend we're a Python 3 mappingproxy.
|
||||
raise TypeError("'mappingproxy' object does not support item "
|
||||
"deletion")
|
||||
|
||||
def clear(self):
|
||||
# We gently pretend we're a Python 3 mappingproxy.
|
||||
raise AttributeError("'mappingproxy' object has no attribute "
|
||||
"'clear'")
|
||||
|
||||
def pop(self, key, default=None):
|
||||
# We gently pretend we're a Python 3 mappingproxy.
|
||||
raise AttributeError("'mappingproxy' object has no attribute "
|
||||
"'pop'")
|
||||
|
||||
def popitem(self):
|
||||
# We gently pretend we're a Python 3 mappingproxy.
|
||||
raise AttributeError("'mappingproxy' object has no attribute "
|
||||
"'popitem'")
|
||||
|
||||
def setdefault(self, key, default=None):
|
||||
# We gently pretend we're a Python 3 mappingproxy.
|
||||
raise AttributeError("'mappingproxy' object has no attribute "
|
||||
"'setdefault'")
|
||||
|
||||
def __repr__(self):
|
||||
# Override to be identical to the Python 3 version.
|
||||
return "mappingproxy(" + repr(self.data) + ")"
|
||||
|
||||
def metadata_proxy(d):
|
||||
res = ReadOnlyDict()
|
||||
res.data.update(d) # We blocked update, so we have to do it like this.
|
||||
return res
|
||||
|
||||
else:
|
||||
def isclass(klass):
|
||||
return isinstance(klass, type)
|
||||
|
||||
TYPE = "class"
|
||||
|
||||
def iteritems(d):
|
||||
return d.items()
|
||||
|
||||
def metadata_proxy(d):
|
||||
return types.MappingProxyType(dict(d))
|
||||
|
||||
|
||||
def import_ctypes():
|
||||
"""
|
||||
Moved into a function for testability.
|
||||
"""
|
||||
import ctypes
|
||||
return ctypes
|
||||
|
||||
|
||||
if not PY2:
|
||||
def just_warn(*args, **kw):
|
||||
"""
|
||||
We only warn on Python 3 because we are not aware of any concrete
|
||||
consequences of not setting the cell on Python 2.
|
||||
"""
|
||||
warnings.warn(
|
||||
"Missing ctypes. Some features like bare super() or accessing "
|
||||
"__class__ will not work with slots classes.",
|
||||
RuntimeWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
else:
|
||||
def just_warn(*args, **kw): # pragma: nocover
|
||||
"""
|
||||
We only warn on Python 3 because we are not aware of any concrete
|
||||
consequences of not setting the cell on Python 2.
|
||||
"""
|
||||
|
||||
|
||||
def make_set_closure_cell():
|
||||
"""
|
||||
Moved into a function for testability.
|
||||
"""
|
||||
if PYPY: # pragma: no cover
|
||||
def set_closure_cell(cell, value):
|
||||
cell.__setstate__((value,))
|
||||
else:
|
||||
try:
|
||||
ctypes = import_ctypes()
|
||||
|
||||
set_closure_cell = ctypes.pythonapi.PyCell_Set
|
||||
set_closure_cell.argtypes = (ctypes.py_object, ctypes.py_object)
|
||||
set_closure_cell.restype = ctypes.c_int
|
||||
except Exception:
|
||||
# We try best effort to set the cell, but sometimes it's not
|
||||
# possible. For example on Jython or on GAE.
|
||||
set_closure_cell = just_warn
|
||||
return set_closure_cell
|
||||
|
||||
|
||||
set_closure_cell = make_set_closure_cell()
|
||||
Vendored
-23
@@ -1,23 +0,0 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
|
||||
__all__ = ["set_run_validators", "get_run_validators"]
|
||||
|
||||
_run_validators = True
|
||||
|
||||
|
||||
def set_run_validators(run):
|
||||
"""
|
||||
Set whether or not validators are run. By default, they are run.
|
||||
"""
|
||||
if not isinstance(run, bool):
|
||||
raise TypeError("'run' must be bool.")
|
||||
global _run_validators
|
||||
_run_validators = run
|
||||
|
||||
|
||||
def get_run_validators():
|
||||
"""
|
||||
Return whether or not validators are run.
|
||||
"""
|
||||
return _run_validators
|
||||
Vendored
-212
@@ -1,212 +0,0 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
import copy
|
||||
|
||||
from ._compat import iteritems
|
||||
from ._make import NOTHING, _obj_setattr, fields
|
||||
from .exceptions import AttrsAttributeNotFoundError
|
||||
|
||||
|
||||
def asdict(inst, recurse=True, filter=None, dict_factory=dict,
|
||||
retain_collection_types=False):
|
||||
"""
|
||||
Return the ``attrs`` attribute values of *inst* as a dict.
|
||||
|
||||
Optionally recurse into other ``attrs``-decorated classes.
|
||||
|
||||
:param inst: Instance of an ``attrs``-decorated class.
|
||||
:param bool recurse: Recurse into classes that are also
|
||||
``attrs``-decorated.
|
||||
:param callable filter: A callable whose return code determines whether an
|
||||
attribute or element is included (``True``) or dropped (``False``). Is
|
||||
called with the :class:`attr.Attribute` as the first argument and the
|
||||
value as the second argument.
|
||||
:param callable dict_factory: A callable to produce dictionaries from. For
|
||||
example, to produce ordered dictionaries instead of normal Python
|
||||
dictionaries, pass in ``collections.OrderedDict``.
|
||||
:param bool retain_collection_types: Do not convert to ``list`` when
|
||||
encountering an attribute whose type is ``tuple`` or ``set``. Only
|
||||
meaningful if ``recurse`` is ``True``.
|
||||
|
||||
:rtype: return type of *dict_factory*
|
||||
|
||||
:raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
|
||||
class.
|
||||
|
||||
.. versionadded:: 16.0.0 *dict_factory*
|
||||
.. versionadded:: 16.1.0 *retain_collection_types*
|
||||
"""
|
||||
attrs = fields(inst.__class__)
|
||||
rv = dict_factory()
|
||||
for a in attrs:
|
||||
v = getattr(inst, a.name)
|
||||
if filter is not None and not filter(a, v):
|
||||
continue
|
||||
if recurse is True:
|
||||
if has(v.__class__):
|
||||
rv[a.name] = asdict(v, recurse=True, filter=filter,
|
||||
dict_factory=dict_factory)
|
||||
elif isinstance(v, (tuple, list, set)):
|
||||
cf = v.__class__ if retain_collection_types is True else list
|
||||
rv[a.name] = cf([
|
||||
asdict(i, recurse=True, filter=filter,
|
||||
dict_factory=dict_factory)
|
||||
if has(i.__class__) else i
|
||||
for i in v
|
||||
])
|
||||
elif isinstance(v, dict):
|
||||
df = dict_factory
|
||||
rv[a.name] = df((
|
||||
asdict(kk, dict_factory=df) if has(kk.__class__) else kk,
|
||||
asdict(vv, dict_factory=df) if has(vv.__class__) else vv)
|
||||
for kk, vv in iteritems(v))
|
||||
else:
|
||||
rv[a.name] = v
|
||||
else:
|
||||
rv[a.name] = v
|
||||
return rv
|
||||
|
||||
|
||||
def astuple(inst, recurse=True, filter=None, tuple_factory=tuple,
|
||||
retain_collection_types=False):
|
||||
"""
|
||||
Return the ``attrs`` attribute values of *inst* as a tuple.
|
||||
|
||||
Optionally recurse into other ``attrs``-decorated classes.
|
||||
|
||||
:param inst: Instance of an ``attrs``-decorated class.
|
||||
:param bool recurse: Recurse into classes that are also
|
||||
``attrs``-decorated.
|
||||
:param callable filter: A callable whose return code determines whether an
|
||||
attribute or element is included (``True``) or dropped (``False``). Is
|
||||
called with the :class:`attr.Attribute` as the first argument and the
|
||||
value as the second argument.
|
||||
:param callable tuple_factory: A callable to produce tuples from. For
|
||||
example, to produce lists instead of tuples.
|
||||
:param bool retain_collection_types: Do not convert to ``list``
|
||||
or ``dict`` when encountering an attribute which type is
|
||||
``tuple``, ``dict`` or ``set``. Only meaningful if ``recurse`` is
|
||||
``True``.
|
||||
|
||||
:rtype: return type of *tuple_factory*
|
||||
|
||||
:raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
|
||||
class.
|
||||
|
||||
.. versionadded:: 16.2.0
|
||||
"""
|
||||
attrs = fields(inst.__class__)
|
||||
rv = []
|
||||
retain = retain_collection_types # Very long. :/
|
||||
for a in attrs:
|
||||
v = getattr(inst, a.name)
|
||||
if filter is not None and not filter(a, v):
|
||||
continue
|
||||
if recurse is True:
|
||||
if has(v.__class__):
|
||||
rv.append(astuple(v, recurse=True, filter=filter,
|
||||
tuple_factory=tuple_factory,
|
||||
retain_collection_types=retain))
|
||||
elif isinstance(v, (tuple, list, set)):
|
||||
cf = v.__class__ if retain is True else list
|
||||
rv.append(cf([
|
||||
astuple(j, recurse=True, filter=filter,
|
||||
tuple_factory=tuple_factory,
|
||||
retain_collection_types=retain)
|
||||
if has(j.__class__) else j
|
||||
for j in v
|
||||
]))
|
||||
elif isinstance(v, dict):
|
||||
df = v.__class__ if retain is True else dict
|
||||
rv.append(df(
|
||||
(
|
||||
astuple(
|
||||
kk,
|
||||
tuple_factory=tuple_factory,
|
||||
retain_collection_types=retain
|
||||
) if has(kk.__class__) else kk,
|
||||
astuple(
|
||||
vv,
|
||||
tuple_factory=tuple_factory,
|
||||
retain_collection_types=retain
|
||||
) if has(vv.__class__) else vv
|
||||
)
|
||||
for kk, vv in iteritems(v)))
|
||||
else:
|
||||
rv.append(v)
|
||||
else:
|
||||
rv.append(v)
|
||||
return rv if tuple_factory is list else tuple_factory(rv)
|
||||
|
||||
|
||||
def has(cls):
|
||||
"""
|
||||
Check whether *cls* is a class with ``attrs`` attributes.
|
||||
|
||||
:param type cls: Class to introspect.
|
||||
:raise TypeError: If *cls* is not a class.
|
||||
|
||||
:rtype: :class:`bool`
|
||||
"""
|
||||
return getattr(cls, "__attrs_attrs__", None) is not None
|
||||
|
||||
|
||||
def assoc(inst, **changes):
|
||||
"""
|
||||
Copy *inst* and apply *changes*.
|
||||
|
||||
:param inst: Instance of a class with ``attrs`` attributes.
|
||||
:param changes: Keyword changes in the new copy.
|
||||
|
||||
:return: A copy of inst with *changes* incorporated.
|
||||
|
||||
:raise attr.exceptions.AttrsAttributeNotFoundError: If *attr_name* couldn't
|
||||
be found on *cls*.
|
||||
:raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
|
||||
class.
|
||||
|
||||
.. deprecated:: 17.1.0
|
||||
Use :func:`evolve` instead.
|
||||
"""
|
||||
import warnings
|
||||
warnings.warn("assoc is deprecated and will be removed after 2018/01.",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
new = copy.copy(inst)
|
||||
attrs = fields(inst.__class__)
|
||||
for k, v in iteritems(changes):
|
||||
a = getattr(attrs, k, NOTHING)
|
||||
if a is NOTHING:
|
||||
raise AttrsAttributeNotFoundError(
|
||||
"{k} is not an attrs attribute on {cl}."
|
||||
.format(k=k, cl=new.__class__)
|
||||
)
|
||||
_obj_setattr(new, k, v)
|
||||
return new
|
||||
|
||||
|
||||
def evolve(inst, **changes):
|
||||
"""
|
||||
Create a new instance, based on *inst* with *changes* applied.
|
||||
|
||||
:param inst: Instance of a class with ``attrs`` attributes.
|
||||
:param changes: Keyword changes in the new copy.
|
||||
|
||||
:return: A copy of inst with *changes* incorporated.
|
||||
|
||||
:raise TypeError: If *attr_name* couldn't be found in the class
|
||||
``__init__``.
|
||||
:raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
|
||||
class.
|
||||
|
||||
.. versionadded:: 17.1.0
|
||||
"""
|
||||
cls = inst.__class__
|
||||
attrs = fields(cls)
|
||||
for a in attrs:
|
||||
if not a.init:
|
||||
continue
|
||||
attr_name = a.name # To deal with private attributes.
|
||||
init_name = attr_name if attr_name[0] != "_" else attr_name[1:]
|
||||
if init_name not in changes:
|
||||
changes[init_name] = getattr(inst, attr_name)
|
||||
return cls(**changes)
|
||||
Vendored
-1680
File diff suppressed because it is too large
Load Diff
Vendored
-24
@@ -1,24 +0,0 @@
|
||||
"""
|
||||
Commonly useful converters.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
|
||||
def optional(converter):
|
||||
"""
|
||||
A converter that allows an attribute to be optional. An optional attribute
|
||||
is one which can be set to ``None``.
|
||||
|
||||
:param callable converter: the converter that is used for non-``None``
|
||||
values.
|
||||
|
||||
.. versionadded:: 17.1.0
|
||||
"""
|
||||
|
||||
def optional_converter(val):
|
||||
if val is None:
|
||||
return None
|
||||
return converter(val)
|
||||
|
||||
return optional_converter
|
||||
Vendored
-48
@@ -1,48 +0,0 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
|
||||
class FrozenInstanceError(AttributeError):
|
||||
"""
|
||||
A frozen/immutable instance has been attempted to be modified.
|
||||
|
||||
It mirrors the behavior of ``namedtuples`` by using the same error message
|
||||
and subclassing :exc:`AttributeError`.
|
||||
|
||||
.. versionadded:: 16.1.0
|
||||
"""
|
||||
msg = "can't set attribute"
|
||||
args = [msg]
|
||||
|
||||
|
||||
class AttrsAttributeNotFoundError(ValueError):
|
||||
"""
|
||||
An ``attrs`` function couldn't find an attribute that the user asked for.
|
||||
|
||||
.. versionadded:: 16.2.0
|
||||
"""
|
||||
|
||||
|
||||
class NotAnAttrsClassError(ValueError):
|
||||
"""
|
||||
A non-``attrs`` class has been passed into an ``attrs`` function.
|
||||
|
||||
.. versionadded:: 16.2.0
|
||||
"""
|
||||
|
||||
|
||||
class DefaultAlreadySetError(RuntimeError):
|
||||
"""
|
||||
A default has been set using ``attr.ib()`` and is attempted to be reset
|
||||
using the decorator.
|
||||
|
||||
.. versionadded:: 17.1.0
|
||||
"""
|
||||
|
||||
|
||||
class UnannotatedAttributeError(RuntimeError):
|
||||
"""
|
||||
A class with ``auto_attribs=True`` has an ``attr.ib()`` without a type
|
||||
annotation.
|
||||
|
||||
.. versionadded:: 17.3.0
|
||||
"""
|
||||
Vendored
-52
@@ -1,52 +0,0 @@
|
||||
"""
|
||||
Commonly useful filters for :func:`attr.asdict`.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
from ._compat import isclass
|
||||
from ._make import Attribute
|
||||
|
||||
|
||||
def _split_what(what):
|
||||
"""
|
||||
Returns a tuple of `frozenset`s of classes and attributes.
|
||||
"""
|
||||
return (
|
||||
frozenset(cls for cls in what if isclass(cls)),
|
||||
frozenset(cls for cls in what if isinstance(cls, Attribute)),
|
||||
)
|
||||
|
||||
|
||||
def include(*what):
|
||||
"""
|
||||
Whitelist *what*.
|
||||
|
||||
:param what: What to whitelist.
|
||||
:type what: :class:`list` of :class:`type` or :class:`attr.Attribute`\ s
|
||||
|
||||
:rtype: :class:`callable`
|
||||
"""
|
||||
cls, attrs = _split_what(what)
|
||||
|
||||
def include_(attribute, value):
|
||||
return value.__class__ in cls or attribute in attrs
|
||||
|
||||
return include_
|
||||
|
||||
|
||||
def exclude(*what):
|
||||
"""
|
||||
Blacklist *what*.
|
||||
|
||||
:param what: What to blacklist.
|
||||
:type what: :class:`list` of classes or :class:`attr.Attribute`\ s.
|
||||
|
||||
:rtype: :class:`callable`
|
||||
"""
|
||||
cls, attrs = _split_what(what)
|
||||
|
||||
def exclude_(attribute, value):
|
||||
return value.__class__ not in cls and attribute not in attrs
|
||||
|
||||
return exclude_
|
||||
Vendored
-166
@@ -1,166 +0,0 @@
|
||||
"""
|
||||
Commonly useful validators.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
from ._make import _AndValidator, and_, attrib, attrs
|
||||
|
||||
|
||||
__all__ = [
|
||||
"and_",
|
||||
"in_",
|
||||
"instance_of",
|
||||
"optional",
|
||||
"provides",
|
||||
]
|
||||
|
||||
|
||||
@attrs(repr=False, slots=True, hash=True)
|
||||
class _InstanceOfValidator(object):
|
||||
type = attrib()
|
||||
|
||||
def __call__(self, inst, attr, value):
|
||||
"""
|
||||
We use a callable class to be able to change the ``__repr__``.
|
||||
"""
|
||||
if not isinstance(value, self.type):
|
||||
raise TypeError(
|
||||
"'{name}' must be {type!r} (got {value!r} that is a "
|
||||
"{actual!r})."
|
||||
.format(name=attr.name, type=self.type,
|
||||
actual=value.__class__, value=value),
|
||||
attr, self.type, value,
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
"<instance_of validator for type {type!r}>"
|
||||
.format(type=self.type)
|
||||
)
|
||||
|
||||
|
||||
def instance_of(type):
|
||||
"""
|
||||
A validator that raises a :exc:`TypeError` if the initializer is called
|
||||
with a wrong type for this particular attribute (checks are performed using
|
||||
:func:`isinstance` therefore it's also valid to pass a tuple of types).
|
||||
|
||||
:param type: The type to check for.
|
||||
:type type: type or tuple of types
|
||||
|
||||
:raises TypeError: With a human readable error message, the attribute
|
||||
(of type :class:`attr.Attribute`), the expected type, and the value it
|
||||
got.
|
||||
"""
|
||||
return _InstanceOfValidator(type)
|
||||
|
||||
|
||||
@attrs(repr=False, slots=True, hash=True)
|
||||
class _ProvidesValidator(object):
|
||||
interface = attrib()
|
||||
|
||||
def __call__(self, inst, attr, value):
|
||||
"""
|
||||
We use a callable class to be able to change the ``__repr__``.
|
||||
"""
|
||||
if not self.interface.providedBy(value):
|
||||
raise TypeError(
|
||||
"'{name}' must provide {interface!r} which {value!r} "
|
||||
"doesn't."
|
||||
.format(name=attr.name, interface=self.interface, value=value),
|
||||
attr, self.interface, value,
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
"<provides validator for interface {interface!r}>"
|
||||
.format(interface=self.interface)
|
||||
)
|
||||
|
||||
|
||||
def provides(interface):
|
||||
"""
|
||||
A validator that raises a :exc:`TypeError` if the initializer is called
|
||||
with an object that does not provide the requested *interface* (checks are
|
||||
performed using ``interface.providedBy(value)`` (see `zope.interface
|
||||
<https://zopeinterface.readthedocs.io/en/latest/>`_).
|
||||
|
||||
:param zope.interface.Interface interface: The interface to check for.
|
||||
|
||||
:raises TypeError: With a human readable error message, the attribute
|
||||
(of type :class:`attr.Attribute`), the expected interface, and the
|
||||
value it got.
|
||||
"""
|
||||
return _ProvidesValidator(interface)
|
||||
|
||||
|
||||
@attrs(repr=False, slots=True, hash=True)
|
||||
class _OptionalValidator(object):
|
||||
validator = attrib()
|
||||
|
||||
def __call__(self, inst, attr, value):
|
||||
if value is None:
|
||||
return
|
||||
|
||||
self.validator(inst, attr, value)
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
"<optional validator for {what} or None>"
|
||||
.format(what=repr(self.validator))
|
||||
)
|
||||
|
||||
|
||||
def optional(validator):
|
||||
"""
|
||||
A validator that makes an attribute optional. An optional attribute is one
|
||||
which can be set to ``None`` in addition to satisfying the requirements of
|
||||
the sub-validator.
|
||||
|
||||
:param validator: A validator (or a list of validators) that is used for
|
||||
non-``None`` values.
|
||||
:type validator: callable or :class:`list` of callables.
|
||||
|
||||
.. versionadded:: 15.1.0
|
||||
.. versionchanged:: 17.1.0 *validator* can be a list of validators.
|
||||
"""
|
||||
if isinstance(validator, list):
|
||||
return _OptionalValidator(_AndValidator(validator))
|
||||
return _OptionalValidator(validator)
|
||||
|
||||
|
||||
@attrs(repr=False, slots=True, hash=True)
|
||||
class _InValidator(object):
|
||||
options = attrib()
|
||||
|
||||
def __call__(self, inst, attr, value):
|
||||
if value not in self.options:
|
||||
raise ValueError(
|
||||
"'{name}' must be in {options!r} (got {value!r})"
|
||||
.format(name=attr.name, options=self.options, value=value)
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
"<in_ validator with options {options!r}>"
|
||||
.format(options=self.options)
|
||||
)
|
||||
|
||||
|
||||
def in_(options):
|
||||
"""
|
||||
A validator that raises a :exc:`ValueError` if the initializer is called
|
||||
with a value that does not belong in the options provided. The check is
|
||||
performed using ``value in options``.
|
||||
|
||||
:param options: Allowed options.
|
||||
:type options: list, tuple, :class:`enum.Enum`, ...
|
||||
|
||||
:raises ValueError: With a human readable error message, the attribute (of
|
||||
type :class:`attr.Attribute`), the expected options, and the value it
|
||||
got.
|
||||
|
||||
.. versionadded:: 17.1.0
|
||||
"""
|
||||
return _InValidator(options)
|
||||
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,20 +1,27 @@
|
||||
import textwrap
|
||||
|
||||
from pipenv.cmdparse import Script
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.run
|
||||
@pytest.mark.script
|
||||
def test_parse():
|
||||
script = Script.parse(['python', '-c', "print('hello')"])
|
||||
assert script.command == 'python'
|
||||
assert script.args == ['-c', "print('hello')"], script
|
||||
|
||||
|
||||
@pytest.mark.run
|
||||
@pytest.mark.script
|
||||
def test_cmdify():
|
||||
script = Script.parse(['python', '-c', "print('hello')"])
|
||||
cmd = script.cmdify(['--verbose'])
|
||||
assert cmd == '"python" "-c" "print(\'hello\')" "--verbose"', script
|
||||
|
||||
|
||||
@pytest.mark.run
|
||||
@pytest.mark.script
|
||||
def test_cmdify_complex():
|
||||
script = Script.parse(' '.join([
|
||||
'"C:\\Program Files\\Python36\\python.exe" -c',
|
||||
|
||||
+29
-11
@@ -15,6 +15,7 @@ from pipenv.vendor import requests
|
||||
from pipenv.patched import pipfile
|
||||
from pipenv.project import Project
|
||||
from pipenv.vendor.six import PY2
|
||||
from flaky import flaky
|
||||
if PY2:
|
||||
class ResourceWarning(Warning):
|
||||
pass
|
||||
@@ -237,8 +238,16 @@ class TestPipenv:
|
||||
@pytest.mark.install
|
||||
def test_install_parse_error(self, pypi):
|
||||
with PipenvInstance(pypi=pypi) as p:
|
||||
|
||||
# Make sure unparseable packages don't wind up in the pipfile
|
||||
# Escape $ for shell input
|
||||
with open(p.pipfile_path, 'w') as f:
|
||||
contents = """
|
||||
[packages]
|
||||
|
||||
[dev-packages]
|
||||
""".strip()
|
||||
f.write(contents)
|
||||
c = p.pipenv('install requests u/\\/p@r\$34b13+pkg')
|
||||
assert c.return_code != 0
|
||||
assert 'u/\\/p@r$34b13+pkg' not in p.pipfile['packages']
|
||||
@@ -299,9 +308,10 @@ class TestPipenv:
|
||||
|
||||
@pytest.mark.dev
|
||||
@pytest.mark.install
|
||||
@flaky
|
||||
def test_install_without_dev(self, pypi):
|
||||
"""Ensure that running `pipenv install` doesn't install dev packages"""
|
||||
with PipenvInstance(pypi=pypi) as p:
|
||||
with PipenvInstance(pypi=pypi, chdir=True) as p:
|
||||
with open(p.pipfile_path, 'w') as f:
|
||||
contents = """
|
||||
[packages]
|
||||
@@ -419,8 +429,9 @@ tablib = "*"
|
||||
|
||||
@pytest.mark.extras
|
||||
@pytest.mark.install
|
||||
@flaky
|
||||
def test_extras_install(self, pypi):
|
||||
with PipenvInstance(pypi=pypi) as p:
|
||||
with PipenvInstance(pypi=pypi, chdir=True) as p:
|
||||
c = p.pipenv('install requests[socks]')
|
||||
assert c.return_code == 0
|
||||
assert 'requests' in p.pipfile['packages']
|
||||
@@ -469,8 +480,9 @@ setup(
|
||||
@pytest.mark.vcs
|
||||
@pytest.mark.install
|
||||
@needs_internet
|
||||
@flaky
|
||||
def test_basic_vcs_install(self, pip_src_dir, pypi):
|
||||
with PipenvInstance(pypi=pypi) as p:
|
||||
with PipenvInstance(pypi=pypi, chdir=True) as p:
|
||||
c = p.pipenv('install git+https://github.com/requests/requests.git#egg=requests')
|
||||
assert c.return_code == 0
|
||||
# edge case where normal package starts with VCS name shouldn't be flagged as vcs
|
||||
@@ -515,11 +527,12 @@ tablib = "<0.12"
|
||||
|
||||
@pytest.mark.run
|
||||
@pytest.mark.install
|
||||
@flaky
|
||||
def test_multiprocess_bug_and_install(self, pypi):
|
||||
with temp_environ():
|
||||
os.environ['PIPENV_MAX_SUBPROCESS'] = '2'
|
||||
|
||||
with PipenvInstance(pypi=pypi) as p:
|
||||
with PipenvInstance(pypi=pypi, chdir=True) as p:
|
||||
with open(p.pipfile_path, 'w') as f:
|
||||
contents = """
|
||||
[packages]
|
||||
@@ -545,9 +558,10 @@ tpfd = "*"
|
||||
|
||||
@pytest.mark.sequential
|
||||
@pytest.mark.install
|
||||
@flaky
|
||||
def test_sequential_mode(self, pypi):
|
||||
|
||||
with PipenvInstance(pypi=pypi) as p:
|
||||
with PipenvInstance(pypi=pypi, chdir=True) as p:
|
||||
with open(p.pipfile_path, 'w') as f:
|
||||
contents = """
|
||||
[packages]
|
||||
@@ -575,6 +589,7 @@ tpfd = "*"
|
||||
@pytest.mark.resolver
|
||||
@pytest.mark.backup_resolver
|
||||
@needs_internet
|
||||
@flaky
|
||||
def test_backup_resolver(self):
|
||||
with PipenvInstance() as p:
|
||||
with open(p.pipfile_path, 'w') as f:
|
||||
@@ -632,6 +647,7 @@ requests = {version = "*", os_name = "== 'splashwear'"}
|
||||
|
||||
@pytest.mark.markers
|
||||
@pytest.mark.install
|
||||
@flaky
|
||||
def test_top_level_overrides_environment_markers(self, pypi):
|
||||
"""Top-level environment markers should take precedence.
|
||||
"""
|
||||
@@ -1079,9 +1095,10 @@ requests = "==2.14.0"
|
||||
@pytest.mark.files
|
||||
@pytest.mark.urls
|
||||
@needs_internet
|
||||
def test_urls_work(self):
|
||||
@flaky
|
||||
def test_urls_work(self, pypi):
|
||||
|
||||
with PipenvInstance(chdir=True) as p:
|
||||
with PipenvInstance(chdir=True, pypi=pypi) as p:
|
||||
|
||||
c = p.pipenv('install https://github.com/divio/django-cms/archive/release/3.4.x.zip')
|
||||
assert c.return_code == 0
|
||||
@@ -1096,7 +1113,7 @@ requests = "==2.14.0"
|
||||
@pytest.mark.files
|
||||
@pytest.mark.resolver
|
||||
@pytest.mark.eggs
|
||||
def test_local_package(self, pip_src_dir):
|
||||
def test_local_package(self, pip_src_dir, pypi):
|
||||
"""This test ensures that local packages (directories with a setup.py)
|
||||
installed in editable mode have their dependencies resolved as well"""
|
||||
file_name = 'tablib-0.12.1.tar.gz'
|
||||
@@ -1104,7 +1121,7 @@ requests = "==2.14.0"
|
||||
# Not sure where travis/appveyor run tests from
|
||||
test_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
source_path = os.path.abspath(os.path.join(test_dir, 'test_artifacts', file_name))
|
||||
with PipenvInstance(chdir=True) as p:
|
||||
with PipenvInstance(chdir=True, pypi=pypi) as p:
|
||||
# This tests for a bug when installing a zipfile in the current dir
|
||||
copy_to = os.path.join(p.path, file_name)
|
||||
shutil.copy(source_path, copy_to)
|
||||
@@ -1117,13 +1134,13 @@ requests = "==2.14.0"
|
||||
|
||||
@pytest.mark.install
|
||||
@pytest.mark.files
|
||||
def test_local_zipfiles(self):
|
||||
def test_local_zipfiles(self, pypi):
|
||||
file_name = 'tablib-0.12.1.tar.gz'
|
||||
# Not sure where travis/appveyor run tests from
|
||||
test_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
source_path = os.path.abspath(os.path.join(test_dir, 'test_artifacts', file_name))
|
||||
|
||||
with PipenvInstance(chdir=True) as p:
|
||||
with PipenvInstance(chdir=True, pypi=pypi) as p:
|
||||
# This tests for a bug when installing a zipfile in the current dir
|
||||
shutil.copy(source_path, os.path.join(p.path, file_name))
|
||||
|
||||
@@ -1183,6 +1200,7 @@ requests = "==2.14.0"
|
||||
|
||||
@pytest.mark.install
|
||||
@pytest.mark.local_file
|
||||
@flaky
|
||||
def test_install_local_file_collision(self, pypi):
|
||||
with PipenvInstance(pypi=pypi) as p:
|
||||
target_package = 'alembic'
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
# -*- coding=utf-8 -*-
|
||||
import os
|
||||
import pytest
|
||||
from first import first
|
||||
from pipenv import requirements
|
||||
from pipenv.utils import get_requirement, convert_deps_from_pip, convert_deps_to_pip
|
||||
|
||||
|
||||
class TestRequirements:
|
||||
|
||||
@pytest.mark.requirement
|
||||
@pytest.mark.parametrize(
|
||||
'line, pipfile',
|
||||
[
|
||||
['requests', {'requests': '*'}],
|
||||
['requests[socks]', {'requests': {'extras': ['socks'], 'version': '*'}}],
|
||||
['django>1.10', {'django': '>1.10'}],
|
||||
['requests[socks]>1.10', {'requests': {'extras': ['socks'], 'version': '>1.10'}}],
|
||||
['-e git+git://github.com/pinax/pinax.git@1.4#egg=pinax', {'pinax': {'git': 'git://github.com/pinax/pinax.git', 'ref': '1.4', 'editable': True}}],
|
||||
['git+git://github.com/pinax/pinax.git@1.4#egg=pinax', {'pinax': {'git': 'git://github.com/pinax/pinax.git', 'ref': '1.4'}}],
|
||||
['FooProject==1.2 --hash=sha256:2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824', {'FooProject': {'version': '==1.2', 'hash': 'sha256:2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824'}}],
|
||||
['FooProject[stuff]==1.2 --hash=sha256:2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824', {'FooProject': {'version': '==1.2', 'extras': ['stuff'], 'hash': 'sha256:2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824'}}],
|
||||
['git+https://github.com/requests/requests.git@master#egg=requests[security]', {'requests': {'git': 'https://github.com/requests/requests.git', 'ref': 'master', 'extras': ['security']}}],
|
||||
['-e svn+svn://svn.myproject.org/svn/MyProject#egg=MyProject', {u'MyProject': {u'svn': u'svn://svn.myproject.org/svn/MyProject', 'editable': True}}],
|
||||
['hg+http://hg.myproject.org/MyProject@da39a3ee5e6b#egg=MyProject', {'MyProject': {'hg': 'http://hg.myproject.org/MyProject', 'ref': 'da39a3ee5e6b'}}]
|
||||
]
|
||||
)
|
||||
def test_pip_requirements(self, line, pipfile):
|
||||
from_line = requirements.PipenvRequirement.from_line(line)
|
||||
pipfile_pkgname = first([k for k in pipfile.keys()])
|
||||
pipfile_entry = pipfile[pipfile_pkgname]
|
||||
from_pipfile = requirements.PipenvRequirement.from_pipfile(pipfile_pkgname, [], pipfile_entry)
|
||||
assert from_line.as_pipfile() == pipfile
|
||||
assert from_pipfile.as_requirement() == line
|
||||
+1
-1
@@ -86,7 +86,7 @@ class TestUtils:
|
||||
# requests[socks]
|
||||
dep = 'requests[socks]'
|
||||
dep = pipenv.utils.convert_deps_from_pip(dep)
|
||||
assert dep == {'requests': {'extras': ['socks'], 'version': '*'}}
|
||||
assert dep == {'requests': {'extras': ['socks']}}
|
||||
# requests[socks] w/ version
|
||||
dep = 'requests[socks]==1.10'
|
||||
dep = pipenv.utils.convert_deps_from_pip(dep)
|
||||
|
||||
Reference in New Issue
Block a user