mirror of
https://github.com/kennethreitz/pipenv.git
synced 2026-06-05 22:50:18 +00:00
Update all vendored dependencies
- Clean up test config and environment variable handling - Unset env var changes performend by `pipenv run` - Make `environments.is_in_virtualenv()` more dynamic -- read environment on the fly - Split up tests on `pipenv run` to reduce complexity -- one test for global run (no virtualenv creation), one test for virtualenv creation - Add `warn_in_virtualenv` call to `run` command, why doesn't click invoke this automatically? Signed-off-by: Dan Ryan <dan@danryan.co>
This commit is contained in:
@@ -300,6 +300,7 @@ def uninstall(
|
||||
if retcode:
|
||||
sys.exit(retcode)
|
||||
|
||||
|
||||
@cli.command(short_help="Generates Pipfile.lock.", context_settings=CONTEXT_SETTINGS)
|
||||
@lock_options
|
||||
@pass_state
|
||||
@@ -399,8 +400,8 @@ def shell(
|
||||
@pass_state
|
||||
def run(state, command, args):
|
||||
"""Spawns a command installed into the virtualenv."""
|
||||
from ..core import do_run
|
||||
|
||||
from ..core import do_run, warn_in_virtualenv
|
||||
warn_in_virtualenv()
|
||||
do_run(
|
||||
command=command, args=args, three=state.three, python=state.python, pypi_mirror=state.pypi_mirror
|
||||
)
|
||||
|
||||
+18
-5
@@ -2158,7 +2158,7 @@ def do_shell(three=None, python=False, fancy=False, shell_args=None, pypi_mirror
|
||||
# Only set PIPENV_ACTIVE after finishing reading virtualenv_location
|
||||
# otherwise its value will be changed
|
||||
os.environ["PIPENV_ACTIVE"] = vistir.misc.fs_str("1")
|
||||
|
||||
|
||||
os.environ.pop("PIP_SHIMS_BASE_MODULE", None)
|
||||
|
||||
if fancy:
|
||||
@@ -2297,6 +2297,8 @@ def do_run(command, args, three=None, python=False, pypi_mirror=None):
|
||||
|
||||
load_dot_env()
|
||||
|
||||
previous_pip_shims_module = os.environ.pop("PIP_SHIMS_BASE_MODULE", None)
|
||||
|
||||
# Activate virtualenv under the current interpreter's environment
|
||||
inline_activate_virtual_environment()
|
||||
|
||||
@@ -2304,10 +2306,9 @@ def do_run(command, args, three=None, python=False, pypi_mirror=None):
|
||||
# Only set PIPENV_ACTIVE after finishing reading virtualenv_location
|
||||
# such as in inline_activate_virtual_environment
|
||||
# otherwise its value will be changed
|
||||
previous_pipenv_active_value = os.environ.get("PIPENV_ACTIVE")
|
||||
os.environ["PIPENV_ACTIVE"] = vistir.misc.fs_str("1")
|
||||
|
||||
os.environ.pop("PIP_SHIMS_BASE_MODULE", None)
|
||||
|
||||
try:
|
||||
script = project.build_script(command, args)
|
||||
cmd_string = ' '.join([script.command] + script.args)
|
||||
@@ -2315,10 +2316,22 @@ def do_run(command, args, three=None, python=False, pypi_mirror=None):
|
||||
click.echo(crayons.normal("$ {0}".format(cmd_string)), err=True)
|
||||
except ScriptEmptyError:
|
||||
click.echo("Can't run script {0!r}-it's empty?", err=True)
|
||||
run_args = [script]
|
||||
run_kwargs = {}
|
||||
if os.name == "nt":
|
||||
do_run_nt(script)
|
||||
run_fn = do_run_nt
|
||||
else:
|
||||
do_run_posix(script, command=command)
|
||||
run_fn = do_run_posix
|
||||
run_kwargs = {"command": command}
|
||||
try:
|
||||
run_fn(*run_args, **run_kwargs)
|
||||
finally:
|
||||
os.environ.pop("PIPENV_ACTIVE", None)
|
||||
if previous_pipenv_active_value is not None:
|
||||
os.environ["PIPENV_ACTIVE"] = previous_pipenv_active_value
|
||||
if previous_pip_shims_module is not None:
|
||||
os.environ["PIP_SHIMS_BASE_MODULE"] = previous_pip_shims_module
|
||||
|
||||
|
||||
|
||||
def do_check(
|
||||
|
||||
+17
-4
@@ -280,10 +280,23 @@ def is_quiet(threshold=-1):
|
||||
|
||||
|
||||
def is_in_virtualenv():
|
||||
pipenv_active = os.environ.get("PIPENV_ACTIVE")
|
||||
virtual_env = os.environ.get("VIRTUAL_ENV")
|
||||
return (PIPENV_USE_SYSTEM or virtual_env) and not (
|
||||
pipenv_active or PIPENV_IGNORE_VIRTUALENVS
|
||||
"""
|
||||
Check virtualenv membership dynamically
|
||||
|
||||
:return: True or false depending on whether we are in a regular virtualenv or not
|
||||
:rtype: bool
|
||||
"""
|
||||
|
||||
pipenv_active = os.environ.get("PIPENV_ACTIVE", False)
|
||||
virtual_env = None
|
||||
use_system = False
|
||||
ignore_virtualenvs = bool(os.environ.get("PIPENV_IGNORE_VIRTUALENVS", False))
|
||||
|
||||
if not pipenv_active and not ignore_virtualenvs:
|
||||
virtual_env = os.environ.get("VIRTUAL_ENV")
|
||||
use_system = bool(virtual_env)
|
||||
return (use_system or virtual_env) and not (
|
||||
pipenv_active or ignore_virtualenvs
|
||||
)
|
||||
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
__author__ = "Daniel Greenfeld"
|
||||
__email__ = "pydanny@gmail.com"
|
||||
__version__ = "1.4.3"
|
||||
__version__ = "1.5.1"
|
||||
__license__ = "BSD"
|
||||
|
||||
from time import time
|
||||
|
||||
Vendored
+2
-2
@@ -1,3 +1,3 @@
|
||||
from .core import where, old_where
|
||||
from .core import where
|
||||
|
||||
__version__ = "2018.10.15"
|
||||
__version__ = "2018.11.29"
|
||||
|
||||
Vendored
+242
@@ -4268,3 +4268,245 @@ rYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0EAwMDaAAwZQIwJsdpW9zV
|
||||
57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtkAjEA2zQg
|
||||
Mgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=GTS Root R1 O=Google Trust Services LLC
|
||||
# Subject: CN=GTS Root R1 O=Google Trust Services LLC
|
||||
# Label: "GTS Root R1"
|
||||
# Serial: 146587175971765017618439757810265552097
|
||||
# MD5 Fingerprint: 82:1a:ef:d4:d2:4a:f2:9f:e2:3d:97:06:14:70:72:85
|
||||
# SHA1 Fingerprint: e1:c9:50:e6:ef:22:f8:4c:56:45:72:8b:92:20:60:d7:d5:a7:a3:e8
|
||||
# SHA256 Fingerprint: 2a:57:54:71:e3:13:40:bc:21:58:1c:bd:2c:f1:3e:15:84:63:20:3e:ce:94:bc:f9:d3:cc:19:6b:f0:9a:54:72
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=GTS Root R2 O=Google Trust Services LLC
|
||||
# Subject: CN=GTS Root R2 O=Google Trust Services LLC
|
||||
# Label: "GTS Root R2"
|
||||
# Serial: 146587176055767053814479386953112547951
|
||||
# MD5 Fingerprint: 44:ed:9a:0e:a4:09:3b:00:f2:ae:4c:a3:c6:61:b0:8b
|
||||
# SHA1 Fingerprint: d2:73:96:2a:2a:5e:39:9f:73:3f:e1:c7:1e:64:3f:03:38:34:fc:4d
|
||||
# SHA256 Fingerprint: c4:5d:7b:b0:8e:6d:67:e6:2e:42:35:11:0b:56:4e:5f:78:fd:92:ef:05:8c:84:0a:ea:4e:64:55:d7:58:5c:60
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=GTS Root R3 O=Google Trust Services LLC
|
||||
# Subject: CN=GTS Root R3 O=Google Trust Services LLC
|
||||
# Label: "GTS Root R3"
|
||||
# Serial: 146587176140553309517047991083707763997
|
||||
# MD5 Fingerprint: 1a:79:5b:6b:04:52:9c:5d:c7:74:33:1b:25:9a:f9:25
|
||||
# SHA1 Fingerprint: 30:d4:24:6f:07:ff:db:91:89:8a:0b:e9:49:66:11:eb:8c:5e:46:e5
|
||||
# SHA256 Fingerprint: 15:d5:b8:77:46:19:ea:7d:54:ce:1c:a6:d0:b0:c4:03:e0:37:a9:17:f1:31:e8:a0:4e:1e:6b:7a:71:ba:bc:e5
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICDDCCAZGgAwIBAgIQbkepx2ypcyRAiQ8DVd2NHTAKBggqhkjOPQQDAzBHMQsw
|
||||
CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
|
||||
MBIGA1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw
|
||||
MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
|
||||
Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQA
|
||||
IgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout
|
||||
736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2A
|
||||
DDL24CejQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
|
||||
DgQWBBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEAgFuk
|
||||
fCPAlaUs3L6JbyO5o91lAFJekazInXJ0glMLfalAvWhgxeG4VDvBNhcl2MG9AjEA
|
||||
njWSdIUlUfUk7GRSJFClH9voy8l27OyCbvWFGFPouOOaKaqW04MjyaR7YbPMAuhd
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=GTS Root R4 O=Google Trust Services LLC
|
||||
# Subject: CN=GTS Root R4 O=Google Trust Services LLC
|
||||
# Label: "GTS Root R4"
|
||||
# Serial: 146587176229350439916519468929765261721
|
||||
# MD5 Fingerprint: 5d:b6:6a:c4:60:17:24:6a:1a:99:a8:4b:ee:5e:b4:26
|
||||
# SHA1 Fingerprint: 2a:1d:60:27:d9:4a:b1:0a:1c:4d:91:5c:cd:33:a0:cb:3e:2d:54:cb
|
||||
# SHA256 Fingerprint: 71:cc:a5:39:1f:9e:79:4b:04:80:25:30:b3:63:e1:21:da:8a:30:43:bb:26:66:2f:ea:4d:ca:7f:c9:51:a4:bd
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICCjCCAZGgAwIBAgIQbkepyIuUtui7OyrYorLBmTAKBggqhkjOPQQDAzBHMQsw
|
||||
CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
|
||||
MBIGA1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw
|
||||
MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
|
||||
Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQA
|
||||
IgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzu
|
||||
hXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/l
|
||||
xKvRHYqjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
|
||||
DgQWBBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNnADBkAjBqUFJ0
|
||||
CMRw3J5QdCHojXohw0+WbhXRIjVhLfoIN+4Zba3bssx9BzT1YBkstTTZbyACMANx
|
||||
sbqjYAuG7ZoIapVon+Kz4ZNkfF6Tpt95LY2F45TPI11xzPKwTdb+mciUqXWi4w==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=UCA Global G2 Root O=UniTrust
|
||||
# Subject: CN=UCA Global G2 Root O=UniTrust
|
||||
# Label: "UCA Global G2 Root"
|
||||
# Serial: 124779693093741543919145257850076631279
|
||||
# MD5 Fingerprint: 80:fe:f0:c4:4a:f0:5c:62:32:9f:1c:ba:78:a9:50:f8
|
||||
# SHA1 Fingerprint: 28:f9:78:16:19:7a:ff:18:25:18:aa:44:fe:c1:a0:ce:5c:b6:4c:8a
|
||||
# SHA256 Fingerprint: 9b:ea:11:c9:76:fe:01:47:64:c1:be:56:a6:f9:14:b5:a5:60:31:7a:bd:99:88:39:33:82:e5:16:1a:a0:49:3c
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
UB+K+wb1whnw0A==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=UCA Extended Validation Root O=UniTrust
|
||||
# Subject: CN=UCA Extended Validation Root O=UniTrust
|
||||
# Label: "UCA Extended Validation Root"
|
||||
# Serial: 106100277556486529736699587978573607008
|
||||
# MD5 Fingerprint: a1:f3:5f:43:c6:34:9b:da:bf:8c:7e:05:53:ad:96:e2
|
||||
# SHA1 Fingerprint: a3:a1:b0:6f:24:61:23:4a:e3:36:a5:c2:37:fc:a6:ff:dd:f0:d7:3a
|
||||
# SHA256 Fingerprint: d4:3a:f9:b3:54:73:75:5c:96:84:fc:06:d7:d8:cb:70:ee:5c:28:e7:73:fb:29:4e:b4:1e:e7:17:22:92:4d:24
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Certigna Root CA O=Dhimyotis OU=0002 48146308100036
|
||||
# Subject: CN=Certigna Root CA O=Dhimyotis OU=0002 48146308100036
|
||||
# Label: "Certigna Root CA"
|
||||
# Serial: 269714418870597844693661054334862075617
|
||||
# MD5 Fingerprint: 0e:5c:30:62:27:eb:5b:bc:d7:ae:62:ba:e9:d5:df:77
|
||||
# SHA1 Fingerprint: 2d:0d:52:14:ff:9e:ad:99:24:01:74:20:47:6e:6c:85:27:27:f5:43
|
||||
# SHA256 Fingerprint: d4:8d:3d:23:ee:db:50:a4:59:e5:51:97:60:1c:27:77:4b:9d:7b:18:c9:4d:5a:05:95:11:a1:02:50:b9:31:68
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAw
|
||||
WjELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAw
|
||||
MiA0ODE0NjMwODEwMDAzNjEZMBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0x
|
||||
MzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjdaMFoxCzAJBgNVBAYTAkZSMRIwEAYD
|
||||
VQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYzMDgxMDAwMzYxGTAX
|
||||
BgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
|
||||
ggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sO
|
||||
ty3tRQgXstmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9M
|
||||
CiBtnyN6tMbaLOQdLNyzKNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPu
|
||||
I9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8JXrJhFwLrN1CTivngqIkicuQstDuI7pm
|
||||
TLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16XdG+RCYyKfHx9WzMfgIh
|
||||
C59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq4NYKpkDf
|
||||
ePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3Yz
|
||||
IoejwpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWT
|
||||
Co/1VTp2lc5ZmIoJlXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1k
|
||||
JWumIWmbat10TWuXekG9qxf5kBdIjzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5
|
||||
hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp//TBt2dzhauH8XwIDAQABo4IB
|
||||
GjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE
|
||||
FBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of
|
||||
1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczov
|
||||
L3d3d3cuY2VydGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilo
|
||||
dHRwOi8vY3JsLmNlcnRpZ25hLmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYr
|
||||
aHR0cDovL2NybC5kaGlteW90aXMuY29tL2NlcnRpZ25hcm9vdGNhLmNybDANBgkq
|
||||
hkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOItOoldaDgvUSILSo3L
|
||||
6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxPTGRG
|
||||
HVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH6
|
||||
0BGM+RFq7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncB
|
||||
lA2c5uk5jR+mUYyZDDl34bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdi
|
||||
o2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1
|
||||
gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS6Cvu5zHbugRqh5jnxV/v
|
||||
faci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaYtlu3zM63
|
||||
Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayh
|
||||
jWZSaX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw
|
||||
3kAP+HwV96LOPNdeE4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
Vendored
-17
@@ -8,14 +8,6 @@ certifi.py
|
||||
This module returns the installation location of cacert.pem.
|
||||
"""
|
||||
import os
|
||||
import warnings
|
||||
|
||||
|
||||
class DeprecatedBundleWarning(DeprecationWarning):
|
||||
"""
|
||||
The weak security bundle is being deprecated. Please bother your service
|
||||
provider to get them to stop using cross-signed roots.
|
||||
"""
|
||||
|
||||
|
||||
def where():
|
||||
@@ -24,14 +16,5 @@ def where():
|
||||
return os.path.join(f, 'cacert.pem')
|
||||
|
||||
|
||||
def old_where():
|
||||
warnings.warn(
|
||||
"The weak security bundle has been removed. certifi.old_where() is now an alias "
|
||||
"of certifi.where(). Please update your code to use certifi.where() instead. "
|
||||
"certifi.old_where() will be removed in 2018.",
|
||||
DeprecatedBundleWarning
|
||||
)
|
||||
return where()
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(where())
|
||||
|
||||
Vendored
-1
@@ -25,4 +25,3 @@ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
Vendored
+1
-2
@@ -3,5 +3,4 @@ from .initialise import init, deinit, reinit, colorama_text
|
||||
from .ansi import Fore, Back, Style, Cursor
|
||||
from .ansitowin32 import AnsiToWin32
|
||||
|
||||
__version__ = '0.3.9'
|
||||
|
||||
__version__ = '0.4.1'
|
||||
|
||||
+32
-11
@@ -13,14 +13,6 @@ if windll is not None:
|
||||
winterm = WinTerm()
|
||||
|
||||
|
||||
def is_stream_closed(stream):
|
||||
return not hasattr(stream, 'closed') or stream.closed
|
||||
|
||||
|
||||
def is_a_tty(stream):
|
||||
return hasattr(stream, 'isatty') and stream.isatty()
|
||||
|
||||
|
||||
class StreamWrapper(object):
|
||||
'''
|
||||
Wraps a stream (such as stdout), acting as a transparent proxy for all
|
||||
@@ -36,9 +28,38 @@ class StreamWrapper(object):
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.__wrapped, name)
|
||||
|
||||
def __enter__(self, *args, **kwargs):
|
||||
# special method lookup bypasses __getattr__/__getattribute__, see
|
||||
# https://stackoverflow.com/questions/12632894/why-doesnt-getattr-work-with-exit
|
||||
# thus, contextlib magic methods are not proxied via __getattr__
|
||||
return self.__wrapped.__enter__(*args, **kwargs)
|
||||
|
||||
def __exit__(self, *args, **kwargs):
|
||||
return self.__wrapped.__exit__(*args, **kwargs)
|
||||
|
||||
def write(self, text):
|
||||
self.__convertor.write(text)
|
||||
|
||||
def isatty(self):
|
||||
stream = self.__wrapped
|
||||
if 'PYCHARM_HOSTED' in os.environ:
|
||||
if stream is not None and (stream is sys.__stdout__ or stream is sys.__stderr__):
|
||||
return True
|
||||
try:
|
||||
stream_isatty = stream.isatty
|
||||
except AttributeError:
|
||||
return False
|
||||
else:
|
||||
return stream_isatty()
|
||||
|
||||
@property
|
||||
def closed(self):
|
||||
stream = self.__wrapped
|
||||
try:
|
||||
return stream.closed
|
||||
except AttributeError:
|
||||
return True
|
||||
|
||||
|
||||
class AnsiToWin32(object):
|
||||
'''
|
||||
@@ -68,12 +89,12 @@ class AnsiToWin32(object):
|
||||
|
||||
# should we strip ANSI sequences from our output?
|
||||
if strip is None:
|
||||
strip = conversion_supported or (not is_stream_closed(wrapped) and not is_a_tty(wrapped))
|
||||
strip = conversion_supported or (not self.stream.closed and not self.stream.isatty())
|
||||
self.strip = strip
|
||||
|
||||
# should we should convert ANSI sequences into win32 calls?
|
||||
if convert is None:
|
||||
convert = conversion_supported and not is_stream_closed(wrapped) and is_a_tty(wrapped)
|
||||
convert = conversion_supported and not self.stream.closed and self.stream.isatty()
|
||||
self.convert = convert
|
||||
|
||||
# dict of ansi codes to win32 functions and parameters
|
||||
@@ -149,7 +170,7 @@ class AnsiToWin32(object):
|
||||
def reset_all(self):
|
||||
if self.convert:
|
||||
self.call_win32('m', (0,))
|
||||
elif not self.strip and not is_stream_closed(self.wrapped):
|
||||
elif not self.strip and not self.stream.closed:
|
||||
self.wrapped.write(Style.RESET_ALL)
|
||||
|
||||
|
||||
|
||||
Vendored
-2
@@ -78,5 +78,3 @@ def wrap_stream(stream, convert, strip, autoreset, wrap):
|
||||
if wrapper.should_wrap():
|
||||
stream = wrapper.stream
|
||||
return stream
|
||||
|
||||
|
||||
|
||||
Vendored
+7
-11
@@ -89,11 +89,6 @@ else:
|
||||
]
|
||||
_SetConsoleTitleW.restype = wintypes.BOOL
|
||||
|
||||
handles = {
|
||||
STDOUT: _GetStdHandle(STDOUT),
|
||||
STDERR: _GetStdHandle(STDERR),
|
||||
}
|
||||
|
||||
def _winapi_test(handle):
|
||||
csbi = CONSOLE_SCREEN_BUFFER_INFO()
|
||||
success = _GetConsoleScreenBufferInfo(
|
||||
@@ -101,17 +96,18 @@ else:
|
||||
return bool(success)
|
||||
|
||||
def winapi_test():
|
||||
return any(_winapi_test(h) for h in handles.values())
|
||||
return any(_winapi_test(h) for h in
|
||||
(_GetStdHandle(STDOUT), _GetStdHandle(STDERR)))
|
||||
|
||||
def GetConsoleScreenBufferInfo(stream_id=STDOUT):
|
||||
handle = handles[stream_id]
|
||||
handle = _GetStdHandle(stream_id)
|
||||
csbi = CONSOLE_SCREEN_BUFFER_INFO()
|
||||
success = _GetConsoleScreenBufferInfo(
|
||||
handle, byref(csbi))
|
||||
return csbi
|
||||
|
||||
def SetConsoleTextAttribute(stream_id, attrs):
|
||||
handle = handles[stream_id]
|
||||
handle = _GetStdHandle(stream_id)
|
||||
return _SetConsoleTextAttribute(handle, attrs)
|
||||
|
||||
def SetConsoleCursorPosition(stream_id, position, adjust=True):
|
||||
@@ -129,11 +125,11 @@ else:
|
||||
adjusted_position.Y += sr.Top
|
||||
adjusted_position.X += sr.Left
|
||||
# Resume normal processing
|
||||
handle = handles[stream_id]
|
||||
handle = _GetStdHandle(stream_id)
|
||||
return _SetConsoleCursorPosition(handle, adjusted_position)
|
||||
|
||||
def FillConsoleOutputCharacter(stream_id, char, length, start):
|
||||
handle = handles[stream_id]
|
||||
handle = _GetStdHandle(stream_id)
|
||||
char = c_char(char.encode())
|
||||
length = wintypes.DWORD(length)
|
||||
num_written = wintypes.DWORD(0)
|
||||
@@ -144,7 +140,7 @@ else:
|
||||
|
||||
def FillConsoleOutputAttribute(stream_id, attr, length, start):
|
||||
''' FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )'''
|
||||
handle = handles[stream_id]
|
||||
handle = _GetStdHandle(stream_id)
|
||||
attribute = wintypes.WORD(attr)
|
||||
length = wintypes.DWORD(length)
|
||||
num_written = wintypes.DWORD(0)
|
||||
|
||||
Vendored
+9
-2
@@ -44,6 +44,7 @@ class WinTerm(object):
|
||||
def reset_all(self, on_stderr=None):
|
||||
self.set_attrs(self._default)
|
||||
self.set_console(attrs=self._default)
|
||||
self._light = 0
|
||||
|
||||
def fore(self, fore=None, light=False, on_stderr=False):
|
||||
if fore is None:
|
||||
@@ -122,12 +123,15 @@ class WinTerm(object):
|
||||
if mode == 0:
|
||||
from_coord = csbi.dwCursorPosition
|
||||
cells_to_erase = cells_in_screen - cells_before_cursor
|
||||
if mode == 1:
|
||||
elif mode == 1:
|
||||
from_coord = win32.COORD(0, 0)
|
||||
cells_to_erase = cells_before_cursor
|
||||
elif mode == 2:
|
||||
from_coord = win32.COORD(0, 0)
|
||||
cells_to_erase = cells_in_screen
|
||||
else:
|
||||
# invalid mode
|
||||
return
|
||||
# fill the entire screen with blanks
|
||||
win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord)
|
||||
# now set the buffer's attributes accordingly
|
||||
@@ -147,12 +151,15 @@ class WinTerm(object):
|
||||
if mode == 0:
|
||||
from_coord = csbi.dwCursorPosition
|
||||
cells_to_erase = csbi.dwSize.X - csbi.dwCursorPosition.X
|
||||
if mode == 1:
|
||||
elif mode == 1:
|
||||
from_coord = win32.COORD(0, csbi.dwCursorPosition.Y)
|
||||
cells_to_erase = csbi.dwCursorPosition.X
|
||||
elif mode == 2:
|
||||
from_coord = win32.COORD(0, csbi.dwCursorPosition.Y)
|
||||
cells_to_erase = csbi.dwSize.X
|
||||
else:
|
||||
# invalid mode
|
||||
return
|
||||
# fill the entire screen with blanks
|
||||
win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord)
|
||||
# now set the buffer's attributes accordingly
|
||||
|
||||
Vendored
+5
@@ -1,4 +1,9 @@
|
||||
import sys
|
||||
try:
|
||||
from StringIO import StringIO # noqa
|
||||
except ImportError:
|
||||
from io import StringIO # noqa
|
||||
|
||||
PY2 = sys.version_info[0] == 2
|
||||
WIN = sys.platform.startswith('win')
|
||||
text_type = unicode if PY2 else str # noqa
|
||||
|
||||
Vendored
+54
@@ -0,0 +1,54 @@
|
||||
import os
|
||||
|
||||
|
||||
class UndefinedValueError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Undefined(object):
|
||||
"""Class to represent undefined type. """
|
||||
pass
|
||||
|
||||
|
||||
# Reference instance to represent undefined values
|
||||
undefined = Undefined()
|
||||
|
||||
|
||||
def _cast_boolean(value):
|
||||
"""
|
||||
Helper to convert config values to boolean as ConfigParser do.
|
||||
"""
|
||||
_BOOLEANS = {'1': True, 'yes': True, 'true': True, 'on': True,
|
||||
'0': False, 'no': False, 'false': False, 'off': False, '': False}
|
||||
value = str(value)
|
||||
if value.lower() not in _BOOLEANS:
|
||||
raise ValueError('Not a boolean: %s' % value)
|
||||
|
||||
return _BOOLEANS[value.lower()]
|
||||
|
||||
|
||||
def getenv(option, default=undefined, cast=undefined):
|
||||
"""
|
||||
Return the value for option or default if defined.
|
||||
"""
|
||||
|
||||
# We can't avoid __contains__ because value may be empty.
|
||||
if option in os.environ:
|
||||
value = os.environ[option]
|
||||
else:
|
||||
if isinstance(default, Undefined):
|
||||
raise UndefinedValueError('{} not found. Declare it as envvar or define a default value.'.format(option))
|
||||
|
||||
value = default
|
||||
|
||||
if isinstance(cast, Undefined):
|
||||
return value
|
||||
|
||||
if cast is bool:
|
||||
value = _cast_boolean(value)
|
||||
elif cast is list:
|
||||
value = [x for x in value.split(',') if x]
|
||||
else:
|
||||
value = cast(value)
|
||||
|
||||
return value
|
||||
Vendored
+126
-88
@@ -2,47 +2,90 @@
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
import codecs
|
||||
import fileinput
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
from subprocess import Popen, PIPE, STDOUT
|
||||
from subprocess import Popen
|
||||
import tempfile
|
||||
import warnings
|
||||
from collections import OrderedDict
|
||||
from collections import OrderedDict, namedtuple
|
||||
from contextlib import contextmanager
|
||||
|
||||
from .compat import StringIO
|
||||
from .compat import StringIO, PY2, WIN, text_type
|
||||
|
||||
__escape_decoder = codecs.getdecoder('unicode_escape')
|
||||
__posix_variable = re.compile('\$\{[^\}]*\}')
|
||||
__posix_variable = re.compile(r'\$\{[^\}]*\}')
|
||||
|
||||
_binding = re.compile(
|
||||
r"""
|
||||
(
|
||||
\s* # leading whitespace
|
||||
(?:export{0}+)? # export
|
||||
|
||||
( '[^']+' # single-quoted key
|
||||
| [^=\#\s]+ # or unquoted key
|
||||
)?
|
||||
|
||||
(?:
|
||||
(?:{0}*={0}*) # equal sign
|
||||
|
||||
( '(?:\\'|[^'])*' # single-quoted value
|
||||
| "(?:\\"|[^"])*" # or double-quoted value
|
||||
| [^\#\r\n]* # or unquoted value
|
||||
)
|
||||
)?
|
||||
|
||||
\s* # trailing whitespace
|
||||
(?:\#[^\r\n]*)? # comment
|
||||
(?:\r|\n|\r\n)? # newline
|
||||
)
|
||||
""".format(r'[^\S\r\n]'),
|
||||
re.MULTILINE | re.VERBOSE,
|
||||
)
|
||||
|
||||
_escape_sequence = re.compile(r"\\[\\'\"abfnrtv]")
|
||||
|
||||
|
||||
def decode_escaped(escaped):
|
||||
return __escape_decoder(escaped)[0]
|
||||
Binding = namedtuple('Binding', 'key value original')
|
||||
|
||||
|
||||
def parse_line(line):
|
||||
line = line.strip()
|
||||
def decode_escapes(string):
|
||||
def decode_match(match):
|
||||
return codecs.decode(match.group(0), 'unicode-escape')
|
||||
|
||||
# Ignore lines with `#` or which doesn't have `=` in it.
|
||||
if not line or line.startswith('#') or '=' not in line:
|
||||
return None, None
|
||||
return _escape_sequence.sub(decode_match, string)
|
||||
|
||||
k, v = line.split('=', 1)
|
||||
|
||||
if k.startswith('export '):
|
||||
(_, _, k) = k.partition('export ')
|
||||
def is_surrounded_by(string, char):
|
||||
return (
|
||||
len(string) > 1
|
||||
and string[0] == string[-1] == char
|
||||
)
|
||||
|
||||
# Remove any leading and trailing spaces in key, value
|
||||
k, v = k.strip(), v.strip()
|
||||
|
||||
if v:
|
||||
v = v.encode('unicode-escape').decode('ascii')
|
||||
quoted = v[0] == v[-1] in ['"', "'"]
|
||||
if quoted:
|
||||
v = decode_escaped(v[1:-1])
|
||||
def parse_binding(string, position):
|
||||
match = _binding.match(string, position)
|
||||
(matched, key, value) = match.groups()
|
||||
if key is None or value is None:
|
||||
key = None
|
||||
value = None
|
||||
else:
|
||||
value_quoted = is_surrounded_by(value, "'") or is_surrounded_by(value, '"')
|
||||
if value_quoted:
|
||||
value = decode_escapes(value[1:-1])
|
||||
else:
|
||||
value = value.strip()
|
||||
return (Binding(key=key, value=value, original=matched), match.end())
|
||||
|
||||
return k, v
|
||||
|
||||
def parse_stream(stream):
|
||||
string = stream.read()
|
||||
position = 0
|
||||
length = len(string)
|
||||
while position < length:
|
||||
(binding, position) = parse_binding(string, position)
|
||||
yield binding
|
||||
|
||||
|
||||
class DotEnv():
|
||||
@@ -52,19 +95,17 @@ class DotEnv():
|
||||
self._dict = None
|
||||
self.verbose = verbose
|
||||
|
||||
@contextmanager
|
||||
def _get_stream(self):
|
||||
self._is_file = False
|
||||
if isinstance(self.dotenv_path, StringIO):
|
||||
return self.dotenv_path
|
||||
|
||||
if os.path.exists(self.dotenv_path):
|
||||
self._is_file = True
|
||||
return io.open(self.dotenv_path)
|
||||
|
||||
if self.verbose:
|
||||
warnings.warn("File doesn't exist {}".format(self.dotenv_path))
|
||||
|
||||
return StringIO('')
|
||||
yield self.dotenv_path
|
||||
elif os.path.isfile(self.dotenv_path):
|
||||
with io.open(self.dotenv_path) as stream:
|
||||
yield stream
|
||||
else:
|
||||
if self.verbose:
|
||||
warnings.warn("File doesn't exist {}".format(self.dotenv_path))
|
||||
yield StringIO('')
|
||||
|
||||
def dict(self):
|
||||
"""Return dotenv as dict"""
|
||||
@@ -76,17 +117,10 @@ class DotEnv():
|
||||
return self._dict
|
||||
|
||||
def parse(self):
|
||||
f = self._get_stream()
|
||||
|
||||
for line in f:
|
||||
key, value = parse_line(line)
|
||||
if not key:
|
||||
continue
|
||||
|
||||
yield key, value
|
||||
|
||||
if self._is_file:
|
||||
f.close()
|
||||
with self._get_stream() as stream:
|
||||
for mapping in parse_stream(stream):
|
||||
if mapping.key is not None and mapping.value is not None:
|
||||
yield mapping.key, mapping.value
|
||||
|
||||
def set_as_environment_variables(self, override=False):
|
||||
"""
|
||||
@@ -95,13 +129,12 @@ class DotEnv():
|
||||
for k, v in self.dict().items():
|
||||
if k in os.environ and not override:
|
||||
continue
|
||||
# With Python 2 on Windows, ensuree environment variables are
|
||||
# system strings to avoid "TypeError: environment can only contain
|
||||
# strings" in Python's subprocess module.
|
||||
if sys.version_info.major < 3 and sys.platform == 'win32':
|
||||
from pipenv.utils import fs_str
|
||||
k = fs_str(k)
|
||||
v = fs_str(v)
|
||||
# With Python2 on Windows, force environment variables to str to avoid
|
||||
# "TypeError: environment can only contain strings" in Python's subprocess.py.
|
||||
if PY2 and WIN:
|
||||
if isinstance(k, text_type) or isinstance(v, text_type):
|
||||
k = k.encode('ascii')
|
||||
v = v.encode('ascii')
|
||||
os.environ[k] = v
|
||||
|
||||
return True
|
||||
@@ -127,6 +160,20 @@ def get_key(dotenv_path, key_to_get):
|
||||
return DotEnv(dotenv_path, verbose=True).get(key_to_get)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def rewrite(path):
|
||||
try:
|
||||
with tempfile.NamedTemporaryFile(mode="w+", delete=False) as dest:
|
||||
with io.open(path) as source:
|
||||
yield (source, dest)
|
||||
except BaseException:
|
||||
if os.path.isfile(dest.name):
|
||||
os.unlink(dest.name)
|
||||
raise
|
||||
else:
|
||||
shutil.move(dest.name, path)
|
||||
|
||||
|
||||
def set_key(dotenv_path, key_to_set, value_to_set, quote_mode="always"):
|
||||
"""
|
||||
Adds or Updates a key/value to the given .env
|
||||
@@ -142,20 +189,19 @@ def set_key(dotenv_path, key_to_set, value_to_set, quote_mode="always"):
|
||||
if " " in value_to_set:
|
||||
quote_mode = "always"
|
||||
|
||||
line_template = '{}="{}"' if quote_mode == "always" else '{}={}'
|
||||
line_template = '{}="{}"\n' if quote_mode == "always" else '{}={}\n'
|
||||
line_out = line_template.format(key_to_set, value_to_set)
|
||||
|
||||
replaced = False
|
||||
for line in fileinput.input(dotenv_path, inplace=True):
|
||||
k, v = parse_line(line)
|
||||
if k == key_to_set:
|
||||
replaced = True
|
||||
line = line_out
|
||||
print(line, end='')
|
||||
|
||||
if not replaced:
|
||||
with io.open(dotenv_path, "a") as f:
|
||||
f.write("{}\n".format(line_out))
|
||||
with rewrite(dotenv_path) as (source, dest):
|
||||
replaced = False
|
||||
for mapping in parse_stream(source):
|
||||
if mapping.key == key_to_set:
|
||||
dest.write(line_out)
|
||||
replaced = True
|
||||
else:
|
||||
dest.write(mapping.original)
|
||||
if not replaced:
|
||||
dest.write(line_out)
|
||||
|
||||
return True, key_to_set, value_to_set
|
||||
|
||||
@@ -167,18 +213,17 @@ def unset_key(dotenv_path, key_to_unset, quote_mode="always"):
|
||||
If the .env path given doesn't exist, fails
|
||||
If the given key doesn't exist in the .env, fails
|
||||
"""
|
||||
removed = False
|
||||
|
||||
if not os.path.exists(dotenv_path):
|
||||
warnings.warn("can't delete from %s - it doesn't exist." % dotenv_path)
|
||||
return None, key_to_unset
|
||||
|
||||
for line in fileinput.input(dotenv_path, inplace=True):
|
||||
k, v = parse_line(line)
|
||||
if k == key_to_unset:
|
||||
removed = True
|
||||
line = ''
|
||||
print(line, end='')
|
||||
removed = False
|
||||
with rewrite(dotenv_path) as (source, dest):
|
||||
for mapping in parse_stream(source):
|
||||
if mapping.key == key_to_unset:
|
||||
removed = True
|
||||
else:
|
||||
dest.write(mapping.original)
|
||||
|
||||
if not removed:
|
||||
warnings.warn("key %s not removed from %s - key doesn't exist." % (key_to_unset, dotenv_path))
|
||||
@@ -194,7 +239,7 @@ def resolve_nested_variables(values):
|
||||
first search in environ, if not found,
|
||||
then look into the dotenv variables
|
||||
"""
|
||||
ret = os.getenv(name, values.get(name, ""))
|
||||
ret = os.getenv(name, new_values.get(name, ""))
|
||||
return ret
|
||||
|
||||
def _re_sub_callback(match_object):
|
||||
@@ -204,10 +249,12 @@ def resolve_nested_variables(values):
|
||||
"""
|
||||
return _replacement(match_object.group()[2:-1])
|
||||
|
||||
for k, v in values.items():
|
||||
values[k] = __posix_variable.sub(_re_sub_callback, v)
|
||||
new_values = {}
|
||||
|
||||
return values
|
||||
for k, v in values.items():
|
||||
new_values[k] = __posix_variable.sub(_re_sub_callback, v)
|
||||
|
||||
return new_values
|
||||
|
||||
|
||||
def _walk_to_root(path):
|
||||
@@ -248,7 +295,7 @@ def find_dotenv(filename='.env', raise_error_if_not_found=False, usecwd=False):
|
||||
|
||||
for dirname in _walk_to_root(path):
|
||||
check_path = os.path.join(dirname, filename)
|
||||
if os.path.exists(check_path):
|
||||
if os.path.isfile(check_path):
|
||||
return check_path
|
||||
|
||||
if raise_error_if_not_found:
|
||||
@@ -292,19 +339,10 @@ def run_command(command, env):
|
||||
cmd_env.update(env)
|
||||
|
||||
p = Popen(command,
|
||||
stdin=PIPE,
|
||||
stdout=PIPE,
|
||||
stderr=STDOUT,
|
||||
universal_newlines=True,
|
||||
bufsize=0,
|
||||
shell=False,
|
||||
env=cmd_env)
|
||||
try:
|
||||
out, _ = p.communicate()
|
||||
print(out)
|
||||
except Exception:
|
||||
warnings.warn('An error occured, running the command:')
|
||||
out, _ = p.communicate()
|
||||
warnings.warn(out)
|
||||
_, _ = p.communicate()
|
||||
|
||||
return p.returncode
|
||||
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
__version__ = "0.9.1"
|
||||
__version__ = "0.10.1"
|
||||
|
||||
+1
-4
@@ -267,10 +267,7 @@ def alabel(label):
|
||||
|
||||
try:
|
||||
label = label.encode('ascii')
|
||||
try:
|
||||
ulabel(label)
|
||||
except IDNAError:
|
||||
raise IDNAError('The label {0} is not a valid A-label'.format(label))
|
||||
ulabel(label)
|
||||
if not valid_label_length(label):
|
||||
raise IDNAError('Label too long')
|
||||
return label
|
||||
|
||||
+104
-18
@@ -1,6 +1,6 @@
|
||||
# This file is automatically generated by tools/idna-data
|
||||
|
||||
__version__ = "10.0.0"
|
||||
__version__ = "11.0.0"
|
||||
scripts = {
|
||||
'Greek': (
|
||||
0x37000000374,
|
||||
@@ -49,7 +49,7 @@ scripts = {
|
||||
0x30210000302a,
|
||||
0x30380000303c,
|
||||
0x340000004db6,
|
||||
0x4e0000009feb,
|
||||
0x4e0000009ff0,
|
||||
0xf9000000fa6e,
|
||||
0xfa700000fada,
|
||||
0x200000002a6d7,
|
||||
@@ -62,7 +62,7 @@ scripts = {
|
||||
'Hebrew': (
|
||||
0x591000005c8,
|
||||
0x5d0000005eb,
|
||||
0x5f0000005f5,
|
||||
0x5ef000005f5,
|
||||
0xfb1d0000fb37,
|
||||
0xfb380000fb3d,
|
||||
0xfb3e0000fb3f,
|
||||
@@ -248,6 +248,7 @@ joining_types = {
|
||||
0x6fb: 68,
|
||||
0x6fc: 68,
|
||||
0x6ff: 68,
|
||||
0x70f: 84,
|
||||
0x710: 82,
|
||||
0x712: 68,
|
||||
0x713: 68,
|
||||
@@ -522,6 +523,7 @@ joining_types = {
|
||||
0x1875: 68,
|
||||
0x1876: 68,
|
||||
0x1877: 68,
|
||||
0x1878: 68,
|
||||
0x1880: 85,
|
||||
0x1881: 85,
|
||||
0x1882: 85,
|
||||
@@ -690,6 +692,70 @@ joining_types = {
|
||||
0x10bad: 68,
|
||||
0x10bae: 68,
|
||||
0x10baf: 85,
|
||||
0x10d00: 76,
|
||||
0x10d01: 68,
|
||||
0x10d02: 68,
|
||||
0x10d03: 68,
|
||||
0x10d04: 68,
|
||||
0x10d05: 68,
|
||||
0x10d06: 68,
|
||||
0x10d07: 68,
|
||||
0x10d08: 68,
|
||||
0x10d09: 68,
|
||||
0x10d0a: 68,
|
||||
0x10d0b: 68,
|
||||
0x10d0c: 68,
|
||||
0x10d0d: 68,
|
||||
0x10d0e: 68,
|
||||
0x10d0f: 68,
|
||||
0x10d10: 68,
|
||||
0x10d11: 68,
|
||||
0x10d12: 68,
|
||||
0x10d13: 68,
|
||||
0x10d14: 68,
|
||||
0x10d15: 68,
|
||||
0x10d16: 68,
|
||||
0x10d17: 68,
|
||||
0x10d18: 68,
|
||||
0x10d19: 68,
|
||||
0x10d1a: 68,
|
||||
0x10d1b: 68,
|
||||
0x10d1c: 68,
|
||||
0x10d1d: 68,
|
||||
0x10d1e: 68,
|
||||
0x10d1f: 68,
|
||||
0x10d20: 68,
|
||||
0x10d21: 68,
|
||||
0x10d22: 82,
|
||||
0x10d23: 68,
|
||||
0x10f30: 68,
|
||||
0x10f31: 68,
|
||||
0x10f32: 68,
|
||||
0x10f33: 82,
|
||||
0x10f34: 68,
|
||||
0x10f35: 68,
|
||||
0x10f36: 68,
|
||||
0x10f37: 68,
|
||||
0x10f38: 68,
|
||||
0x10f39: 68,
|
||||
0x10f3a: 68,
|
||||
0x10f3b: 68,
|
||||
0x10f3c: 68,
|
||||
0x10f3d: 68,
|
||||
0x10f3e: 68,
|
||||
0x10f3f: 68,
|
||||
0x10f40: 68,
|
||||
0x10f41: 68,
|
||||
0x10f42: 68,
|
||||
0x10f43: 68,
|
||||
0x10f44: 68,
|
||||
0x10f45: 85,
|
||||
0x10f51: 68,
|
||||
0x10f52: 68,
|
||||
0x10f53: 68,
|
||||
0x10f54: 82,
|
||||
0x110bd: 85,
|
||||
0x110cd: 85,
|
||||
0x1e900: 68,
|
||||
0x1e901: 68,
|
||||
0x1e902: 68,
|
||||
@@ -1034,14 +1100,15 @@ codepoint_classes = {
|
||||
0x52d0000052e,
|
||||
0x52f00000530,
|
||||
0x5590000055a,
|
||||
0x56100000587,
|
||||
0x56000000587,
|
||||
0x58800000589,
|
||||
0x591000005be,
|
||||
0x5bf000005c0,
|
||||
0x5c1000005c3,
|
||||
0x5c4000005c6,
|
||||
0x5c7000005c8,
|
||||
0x5d0000005eb,
|
||||
0x5f0000005f3,
|
||||
0x5ef000005f3,
|
||||
0x6100000061b,
|
||||
0x62000000640,
|
||||
0x64100000660,
|
||||
@@ -1054,12 +1121,13 @@ codepoint_classes = {
|
||||
0x7100000074b,
|
||||
0x74d000007b2,
|
||||
0x7c0000007f6,
|
||||
0x7fd000007fe,
|
||||
0x8000000082e,
|
||||
0x8400000085c,
|
||||
0x8600000086b,
|
||||
0x8a0000008b5,
|
||||
0x8b6000008be,
|
||||
0x8d4000008e2,
|
||||
0x8d3000008e2,
|
||||
0x8e300000958,
|
||||
0x96000000964,
|
||||
0x96600000970,
|
||||
@@ -1077,6 +1145,7 @@ codepoint_classes = {
|
||||
0x9e0000009e4,
|
||||
0x9e6000009f2,
|
||||
0x9fc000009fd,
|
||||
0x9fe000009ff,
|
||||
0xa0100000a04,
|
||||
0xa0500000a0b,
|
||||
0xa0f00000a11,
|
||||
@@ -1136,8 +1205,7 @@ codepoint_classes = {
|
||||
0xbd000000bd1,
|
||||
0xbd700000bd8,
|
||||
0xbe600000bf0,
|
||||
0xc0000000c04,
|
||||
0xc0500000c0d,
|
||||
0xc0000000c0d,
|
||||
0xc0e00000c11,
|
||||
0xc1200000c29,
|
||||
0xc2a00000c3a,
|
||||
@@ -1276,7 +1344,7 @@ codepoint_classes = {
|
||||
0x17dc000017de,
|
||||
0x17e0000017ea,
|
||||
0x18100000181a,
|
||||
0x182000001878,
|
||||
0x182000001879,
|
||||
0x1880000018ab,
|
||||
0x18b0000018f6,
|
||||
0x19000000191f,
|
||||
@@ -1544,11 +1612,11 @@ codepoint_classes = {
|
||||
0x309d0000309f,
|
||||
0x30a1000030fb,
|
||||
0x30fc000030ff,
|
||||
0x31050000312f,
|
||||
0x310500003130,
|
||||
0x31a0000031bb,
|
||||
0x31f000003200,
|
||||
0x340000004db6,
|
||||
0x4e0000009feb,
|
||||
0x4e0000009ff0,
|
||||
0xa0000000a48d,
|
||||
0xa4d00000a4fe,
|
||||
0xa5000000a60d,
|
||||
@@ -1655,8 +1723,10 @@ codepoint_classes = {
|
||||
0xa7a50000a7a6,
|
||||
0xa7a70000a7a8,
|
||||
0xa7a90000a7aa,
|
||||
0xa7af0000a7b0,
|
||||
0xa7b50000a7b6,
|
||||
0xa7b70000a7b8,
|
||||
0xa7b90000a7ba,
|
||||
0xa7f70000a7f8,
|
||||
0xa7fa0000a828,
|
||||
0xa8400000a874,
|
||||
@@ -1664,8 +1734,7 @@ codepoint_classes = {
|
||||
0xa8d00000a8da,
|
||||
0xa8e00000a8f8,
|
||||
0xa8fb0000a8fc,
|
||||
0xa8fd0000a8fe,
|
||||
0xa9000000a92e,
|
||||
0xa8fd0000a92e,
|
||||
0xa9300000a954,
|
||||
0xa9800000a9c1,
|
||||
0xa9cf0000a9da,
|
||||
@@ -1743,7 +1812,7 @@ codepoint_classes = {
|
||||
0x10a0500010a07,
|
||||
0x10a0c00010a14,
|
||||
0x10a1500010a18,
|
||||
0x10a1900010a34,
|
||||
0x10a1900010a36,
|
||||
0x10a3800010a3b,
|
||||
0x10a3f00010a40,
|
||||
0x10a6000010a7d,
|
||||
@@ -1756,6 +1825,11 @@ codepoint_classes = {
|
||||
0x10b8000010b92,
|
||||
0x10c0000010c49,
|
||||
0x10cc000010cf3,
|
||||
0x10d0000010d28,
|
||||
0x10d3000010d3a,
|
||||
0x10f0000010f1d,
|
||||
0x10f2700010f28,
|
||||
0x10f3000010f51,
|
||||
0x1100000011047,
|
||||
0x1106600011070,
|
||||
0x1107f000110bb,
|
||||
@@ -1763,10 +1837,11 @@ codepoint_classes = {
|
||||
0x110f0000110fa,
|
||||
0x1110000011135,
|
||||
0x1113600011140,
|
||||
0x1114400011147,
|
||||
0x1115000011174,
|
||||
0x1117600011177,
|
||||
0x11180000111c5,
|
||||
0x111ca000111cd,
|
||||
0x111c9000111cd,
|
||||
0x111d0000111db,
|
||||
0x111dc000111dd,
|
||||
0x1120000011212,
|
||||
@@ -1786,7 +1861,7 @@ codepoint_classes = {
|
||||
0x1132a00011331,
|
||||
0x1133200011334,
|
||||
0x113350001133a,
|
||||
0x1133c00011345,
|
||||
0x1133b00011345,
|
||||
0x1134700011349,
|
||||
0x1134b0001134e,
|
||||
0x1135000011351,
|
||||
@@ -1796,6 +1871,7 @@ codepoint_classes = {
|
||||
0x1137000011375,
|
||||
0x114000001144b,
|
||||
0x114500001145a,
|
||||
0x1145e0001145f,
|
||||
0x11480000114c6,
|
||||
0x114c7000114c8,
|
||||
0x114d0000114da,
|
||||
@@ -1807,15 +1883,17 @@ codepoint_classes = {
|
||||
0x116500001165a,
|
||||
0x11680000116b8,
|
||||
0x116c0000116ca,
|
||||
0x117000001171a,
|
||||
0x117000001171b,
|
||||
0x1171d0001172c,
|
||||
0x117300001173a,
|
||||
0x118000001183b,
|
||||
0x118c0000118ea,
|
||||
0x118ff00011900,
|
||||
0x11a0000011a3f,
|
||||
0x11a4700011a48,
|
||||
0x11a5000011a84,
|
||||
0x11a8600011a9a,
|
||||
0x11a9d00011a9e,
|
||||
0x11ac000011af9,
|
||||
0x11c0000011c09,
|
||||
0x11c0a00011c37,
|
||||
@@ -1831,6 +1909,13 @@ codepoint_classes = {
|
||||
0x11d3c00011d3e,
|
||||
0x11d3f00011d48,
|
||||
0x11d5000011d5a,
|
||||
0x11d6000011d66,
|
||||
0x11d6700011d69,
|
||||
0x11d6a00011d8f,
|
||||
0x11d9000011d92,
|
||||
0x11d9300011d99,
|
||||
0x11da000011daa,
|
||||
0x11ee000011ef7,
|
||||
0x120000001239a,
|
||||
0x1248000012544,
|
||||
0x130000001342f,
|
||||
@@ -1845,11 +1930,12 @@ codepoint_classes = {
|
||||
0x16b5000016b5a,
|
||||
0x16b6300016b78,
|
||||
0x16b7d00016b90,
|
||||
0x16e6000016e80,
|
||||
0x16f0000016f45,
|
||||
0x16f5000016f7f,
|
||||
0x16f8f00016fa0,
|
||||
0x16fe000016fe2,
|
||||
0x17000000187ed,
|
||||
0x17000000187f2,
|
||||
0x1880000018af3,
|
||||
0x1b0000001b11f,
|
||||
0x1b1700001b2fc,
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
__version__ = '2.7'
|
||||
__version__ = '2.8'
|
||||
|
||||
|
||||
+343
-317
File diff suppressed because it is too large
Load Diff
Vendored
+10
-4
@@ -4,18 +4,24 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
__all__ = [
|
||||
"__title__", "__summary__", "__uri__", "__version__", "__author__",
|
||||
"__email__", "__license__", "__copyright__",
|
||||
"__title__",
|
||||
"__summary__",
|
||||
"__uri__",
|
||||
"__version__",
|
||||
"__author__",
|
||||
"__email__",
|
||||
"__license__",
|
||||
"__copyright__",
|
||||
]
|
||||
|
||||
__title__ = "packaging"
|
||||
__summary__ = "Core utilities for Python packages"
|
||||
__uri__ = "https://github.com/pypa/packaging"
|
||||
|
||||
__version__ = "18.0"
|
||||
__version__ = "19.0"
|
||||
|
||||
__author__ = "Donald Stufft and individual contributors"
|
||||
__email__ = "donald@stufft.io"
|
||||
|
||||
__license__ = "BSD or Apache License, Version 2.0"
|
||||
__copyright__ = "Copyright 2014-2018 %s" % __author__
|
||||
__copyright__ = "Copyright 2014-2019 %s" % __author__
|
||||
|
||||
Vendored
+16
-4
@@ -4,11 +4,23 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
from .__about__ import (
|
||||
__author__, __copyright__, __email__, __license__, __summary__, __title__,
|
||||
__uri__, __version__
|
||||
__author__,
|
||||
__copyright__,
|
||||
__email__,
|
||||
__license__,
|
||||
__summary__,
|
||||
__title__,
|
||||
__uri__,
|
||||
__version__,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"__title__", "__summary__", "__uri__", "__version__", "__author__",
|
||||
"__email__", "__license__", "__copyright__",
|
||||
"__title__",
|
||||
"__summary__",
|
||||
"__uri__",
|
||||
"__version__",
|
||||
"__author__",
|
||||
"__email__",
|
||||
"__license__",
|
||||
"__copyright__",
|
||||
]
|
||||
|
||||
Vendored
+4
-3
@@ -12,9 +12,9 @@ PY3 = sys.version_info[0] == 3
|
||||
# flake8: noqa
|
||||
|
||||
if PY3:
|
||||
string_types = str,
|
||||
string_types = (str,)
|
||||
else:
|
||||
string_types = basestring,
|
||||
string_types = (basestring,)
|
||||
|
||||
|
||||
def with_metaclass(meta, *bases):
|
||||
@@ -27,4 +27,5 @@ def with_metaclass(meta, *bases):
|
||||
class metaclass(meta):
|
||||
def __new__(cls, name, this_bases, d):
|
||||
return meta(name, bases, d)
|
||||
return type.__new__(metaclass, 'temporary_class', (), {})
|
||||
|
||||
return type.__new__(metaclass, "temporary_class", (), {})
|
||||
|
||||
-2
@@ -5,7 +5,6 @@ from __future__ import absolute_import, division, print_function
|
||||
|
||||
|
||||
class Infinity(object):
|
||||
|
||||
def __repr__(self):
|
||||
return "Infinity"
|
||||
|
||||
@@ -38,7 +37,6 @@ Infinity = Infinity()
|
||||
|
||||
|
||||
class NegativeInfinity(object):
|
||||
|
||||
def __repr__(self):
|
||||
return "-Infinity"
|
||||
|
||||
|
||||
Vendored
+42
-47
@@ -17,8 +17,11 @@ from .specifiers import Specifier, InvalidSpecifier
|
||||
|
||||
|
||||
__all__ = [
|
||||
"InvalidMarker", "UndefinedComparison", "UndefinedEnvironmentName",
|
||||
"Marker", "default_environment",
|
||||
"InvalidMarker",
|
||||
"UndefinedComparison",
|
||||
"UndefinedEnvironmentName",
|
||||
"Marker",
|
||||
"default_environment",
|
||||
]
|
||||
|
||||
|
||||
@@ -42,7 +45,6 @@ class UndefinedEnvironmentName(ValueError):
|
||||
|
||||
|
||||
class Node(object):
|
||||
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
@@ -57,62 +59,52 @@ class Node(object):
|
||||
|
||||
|
||||
class Variable(Node):
|
||||
|
||||
def serialize(self):
|
||||
return str(self)
|
||||
|
||||
|
||||
class Value(Node):
|
||||
|
||||
def serialize(self):
|
||||
return '"{0}"'.format(self)
|
||||
|
||||
|
||||
class Op(Node):
|
||||
|
||||
def serialize(self):
|
||||
return str(self)
|
||||
|
||||
|
||||
VARIABLE = (
|
||||
L("implementation_version") |
|
||||
L("platform_python_implementation") |
|
||||
L("implementation_name") |
|
||||
L("python_full_version") |
|
||||
L("platform_release") |
|
||||
L("platform_version") |
|
||||
L("platform_machine") |
|
||||
L("platform_system") |
|
||||
L("python_version") |
|
||||
L("sys_platform") |
|
||||
L("os_name") |
|
||||
L("os.name") | # PEP-345
|
||||
L("sys.platform") | # PEP-345
|
||||
L("platform.version") | # PEP-345
|
||||
L("platform.machine") | # PEP-345
|
||||
L("platform.python_implementation") | # PEP-345
|
||||
L("python_implementation") | # undocumented setuptools legacy
|
||||
L("extra")
|
||||
L("implementation_version")
|
||||
| L("platform_python_implementation")
|
||||
| L("implementation_name")
|
||||
| L("python_full_version")
|
||||
| L("platform_release")
|
||||
| L("platform_version")
|
||||
| L("platform_machine")
|
||||
| L("platform_system")
|
||||
| L("python_version")
|
||||
| L("sys_platform")
|
||||
| L("os_name")
|
||||
| L("os.name")
|
||||
| L("sys.platform") # PEP-345
|
||||
| L("platform.version") # PEP-345
|
||||
| L("platform.machine") # PEP-345
|
||||
| L("platform.python_implementation") # PEP-345
|
||||
| L("python_implementation") # PEP-345
|
||||
| L("extra") # undocumented setuptools legacy
|
||||
)
|
||||
ALIASES = {
|
||||
'os.name': 'os_name',
|
||||
'sys.platform': 'sys_platform',
|
||||
'platform.version': 'platform_version',
|
||||
'platform.machine': 'platform_machine',
|
||||
'platform.python_implementation': 'platform_python_implementation',
|
||||
'python_implementation': 'platform_python_implementation'
|
||||
"os.name": "os_name",
|
||||
"sys.platform": "sys_platform",
|
||||
"platform.version": "platform_version",
|
||||
"platform.machine": "platform_machine",
|
||||
"platform.python_implementation": "platform_python_implementation",
|
||||
"python_implementation": "platform_python_implementation",
|
||||
}
|
||||
VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0])))
|
||||
|
||||
VERSION_CMP = (
|
||||
L("===") |
|
||||
L("==") |
|
||||
L(">=") |
|
||||
L("<=") |
|
||||
L("!=") |
|
||||
L("~=") |
|
||||
L(">") |
|
||||
L("<")
|
||||
L("===") | L("==") | L(">=") | L("<=") | L("!=") | L("~=") | L(">") | L("<")
|
||||
)
|
||||
|
||||
MARKER_OP = VERSION_CMP | L("not in") | L("in")
|
||||
@@ -152,8 +144,11 @@ def _format_marker(marker, first=True):
|
||||
# where the single item is itself it's own list. In that case we want skip
|
||||
# the rest of this function so that we don't get extraneous () on the
|
||||
# outside.
|
||||
if (isinstance(marker, list) and len(marker) == 1 and
|
||||
isinstance(marker[0], (list, tuple))):
|
||||
if (
|
||||
isinstance(marker, list)
|
||||
and len(marker) == 1
|
||||
and isinstance(marker[0], (list, tuple))
|
||||
):
|
||||
return _format_marker(marker[0])
|
||||
|
||||
if isinstance(marker, list):
|
||||
@@ -239,20 +234,20 @@ def _evaluate_markers(markers, environment):
|
||||
|
||||
|
||||
def format_full_version(info):
|
||||
version = '{0.major}.{0.minor}.{0.micro}'.format(info)
|
||||
version = "{0.major}.{0.minor}.{0.micro}".format(info)
|
||||
kind = info.releaselevel
|
||||
if kind != 'final':
|
||||
if kind != "final":
|
||||
version += kind[0] + str(info.serial)
|
||||
return version
|
||||
|
||||
|
||||
def default_environment():
|
||||
if hasattr(sys, 'implementation'):
|
||||
if hasattr(sys, "implementation"):
|
||||
iver = format_full_version(sys.implementation.version)
|
||||
implementation_name = sys.implementation.name
|
||||
else:
|
||||
iver = '0'
|
||||
implementation_name = ''
|
||||
iver = "0"
|
||||
implementation_name = ""
|
||||
|
||||
return {
|
||||
"implementation_name": implementation_name,
|
||||
@@ -270,13 +265,13 @@ def default_environment():
|
||||
|
||||
|
||||
class Marker(object):
|
||||
|
||||
def __init__(self, marker):
|
||||
try:
|
||||
self._markers = _coerce_parse_result(MARKER.parseString(marker))
|
||||
except ParseException as e:
|
||||
err_str = "Invalid marker: {0!r}, parse error at {1!r}".format(
|
||||
marker, marker[e.loc:e.loc + 8])
|
||||
marker, marker[e.loc : e.loc + 8]
|
||||
)
|
||||
raise InvalidMarker(err_str)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
+21
-13
@@ -38,8 +38,8 @@ IDENTIFIER = Combine(ALPHANUM + ZeroOrMore(IDENTIFIER_END))
|
||||
NAME = IDENTIFIER("name")
|
||||
EXTRA = IDENTIFIER
|
||||
|
||||
URI = Regex(r'[^ ]+')("url")
|
||||
URL = (AT + URI)
|
||||
URI = Regex(r"[^ ]+")("url")
|
||||
URL = AT + URI
|
||||
|
||||
EXTRAS_LIST = EXTRA + ZeroOrMore(COMMA + EXTRA)
|
||||
EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)("extras")
|
||||
@@ -48,17 +48,18 @@ VERSION_PEP440 = Regex(Specifier._regex_str, re.VERBOSE | re.IGNORECASE)
|
||||
VERSION_LEGACY = Regex(LegacySpecifier._regex_str, re.VERBOSE | re.IGNORECASE)
|
||||
|
||||
VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY
|
||||
VERSION_MANY = Combine(VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE),
|
||||
joinString=",", adjacent=False)("_raw_spec")
|
||||
VERSION_MANY = Combine(
|
||||
VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), joinString=",", adjacent=False
|
||||
)("_raw_spec")
|
||||
_VERSION_SPEC = Optional(((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY))
|
||||
_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or '')
|
||||
_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or "")
|
||||
|
||||
VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier")
|
||||
VERSION_SPEC.setParseAction(lambda s, l, t: t[1])
|
||||
|
||||
MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker")
|
||||
MARKER_EXPR.setParseAction(
|
||||
lambda s, l, t: Marker(s[t._original_start:t._original_end])
|
||||
lambda s, l, t: Marker(s[t._original_start : t._original_end])
|
||||
)
|
||||
MARKER_SEPARATOR = SEMICOLON
|
||||
MARKER = MARKER_SEPARATOR + MARKER_EXPR
|
||||
@@ -66,8 +67,7 @@ MARKER = MARKER_SEPARATOR + MARKER_EXPR
|
||||
VERSION_AND_MARKER = VERSION_SPEC + Optional(MARKER)
|
||||
URL_AND_MARKER = URL + Optional(MARKER)
|
||||
|
||||
NAMED_REQUIREMENT = \
|
||||
NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER)
|
||||
NAMED_REQUIREMENT = NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER)
|
||||
|
||||
REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd
|
||||
# pyparsing isn't thread safe during initialization, so we do it eagerly, see
|
||||
@@ -92,15 +92,21 @@ class Requirement(object):
|
||||
try:
|
||||
req = REQUIREMENT.parseString(requirement_string)
|
||||
except ParseException as e:
|
||||
raise InvalidRequirement("Parse error at \"{0!r}\": {1}".format(
|
||||
requirement_string[e.loc:e.loc + 8], e.msg
|
||||
))
|
||||
raise InvalidRequirement(
|
||||
'Parse error at "{0!r}": {1}'.format(
|
||||
requirement_string[e.loc : e.loc + 8], e.msg
|
||||
)
|
||||
)
|
||||
|
||||
self.name = req.name
|
||||
if req.url:
|
||||
parsed_url = urlparse.urlparse(req.url)
|
||||
if not (parsed_url.scheme and parsed_url.netloc) or (
|
||||
not parsed_url.scheme and not parsed_url.netloc):
|
||||
if parsed_url.scheme == "file":
|
||||
if urlparse.urlunparse(parsed_url) != req.url:
|
||||
raise InvalidRequirement("Invalid URL given")
|
||||
elif not (parsed_url.scheme and parsed_url.netloc) or (
|
||||
not parsed_url.scheme and not parsed_url.netloc
|
||||
):
|
||||
raise InvalidRequirement("Invalid URL: {0}".format(req.url))
|
||||
self.url = req.url
|
||||
else:
|
||||
@@ -120,6 +126,8 @@ class Requirement(object):
|
||||
|
||||
if self.url:
|
||||
parts.append("@ {0}".format(self.url))
|
||||
if self.marker:
|
||||
parts.append(" ")
|
||||
|
||||
if self.marker:
|
||||
parts.append("; {0}".format(self.marker))
|
||||
|
||||
+21
-46
@@ -19,7 +19,6 @@ class InvalidSpecifier(ValueError):
|
||||
|
||||
|
||||
class BaseSpecifier(with_metaclass(abc.ABCMeta, object)):
|
||||
|
||||
@abc.abstractmethod
|
||||
def __str__(self):
|
||||
"""
|
||||
@@ -84,10 +83,7 @@ class _IndividualSpecifier(BaseSpecifier):
|
||||
if not match:
|
||||
raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec))
|
||||
|
||||
self._spec = (
|
||||
match.group("operator").strip(),
|
||||
match.group("version").strip(),
|
||||
)
|
||||
self._spec = (match.group("operator").strip(), match.group("version").strip())
|
||||
|
||||
# Store whether or not this Specifier should accept prereleases
|
||||
self._prereleases = prereleases
|
||||
@@ -99,11 +95,7 @@ class _IndividualSpecifier(BaseSpecifier):
|
||||
else ""
|
||||
)
|
||||
|
||||
return "<{0}({1!r}{2})>".format(
|
||||
self.__class__.__name__,
|
||||
str(self),
|
||||
pre,
|
||||
)
|
||||
return "<{0}({1!r}{2})>".format(self.__class__.__name__, str(self), pre)
|
||||
|
||||
def __str__(self):
|
||||
return "{0}{1}".format(*self._spec)
|
||||
@@ -194,8 +186,9 @@ class _IndividualSpecifier(BaseSpecifier):
|
||||
# If our version is a prerelease, and we were not set to allow
|
||||
# prereleases, then we'll store it for later incase nothing
|
||||
# else matches this specifier.
|
||||
if (parsed_version.is_prerelease and not
|
||||
(prereleases or self.prereleases)):
|
||||
if parsed_version.is_prerelease and not (
|
||||
prereleases or self.prereleases
|
||||
):
|
||||
found_prereleases.append(version)
|
||||
# Either this is not a prerelease, or we should have been
|
||||
# accepting prereleases from the beginning.
|
||||
@@ -213,8 +206,7 @@ class _IndividualSpecifier(BaseSpecifier):
|
||||
|
||||
class LegacySpecifier(_IndividualSpecifier):
|
||||
|
||||
_regex_str = (
|
||||
r"""
|
||||
_regex_str = r"""
|
||||
(?P<operator>(==|!=|<=|>=|<|>))
|
||||
\s*
|
||||
(?P<version>
|
||||
@@ -225,10 +217,8 @@ class LegacySpecifier(_IndividualSpecifier):
|
||||
# them, and a comma since it's a version separator.
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
_regex = re.compile(
|
||||
r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE)
|
||||
_regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE)
|
||||
|
||||
_operators = {
|
||||
"==": "equal",
|
||||
@@ -269,13 +259,13 @@ def _require_version_compare(fn):
|
||||
if not isinstance(prospective, Version):
|
||||
return False
|
||||
return fn(self, prospective, spec)
|
||||
|
||||
return wrapped
|
||||
|
||||
|
||||
class Specifier(_IndividualSpecifier):
|
||||
|
||||
_regex_str = (
|
||||
r"""
|
||||
_regex_str = r"""
|
||||
(?P<operator>(~=|==|!=|<=|>=|<|>|===))
|
||||
(?P<version>
|
||||
(?:
|
||||
@@ -367,10 +357,8 @@ class Specifier(_IndividualSpecifier):
|
||||
)
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
_regex = re.compile(
|
||||
r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE)
|
||||
_regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE)
|
||||
|
||||
_operators = {
|
||||
"~=": "compatible",
|
||||
@@ -397,8 +385,7 @@ class Specifier(_IndividualSpecifier):
|
||||
prefix = ".".join(
|
||||
list(
|
||||
itertools.takewhile(
|
||||
lambda x: (not x.startswith("post") and not
|
||||
x.startswith("dev")),
|
||||
lambda x: (not x.startswith("post") and not x.startswith("dev")),
|
||||
_version_split(spec),
|
||||
)
|
||||
)[:-1]
|
||||
@@ -407,8 +394,9 @@ class Specifier(_IndividualSpecifier):
|
||||
# Add the prefix notation to the end of our string
|
||||
prefix += ".*"
|
||||
|
||||
return (self._get_operator(">=")(prospective, spec) and
|
||||
self._get_operator("==")(prospective, prefix))
|
||||
return self._get_operator(">=")(prospective, spec) and self._get_operator("==")(
|
||||
prospective, prefix
|
||||
)
|
||||
|
||||
@_require_version_compare
|
||||
def _compare_equal(self, prospective, spec):
|
||||
@@ -428,7 +416,7 @@ class Specifier(_IndividualSpecifier):
|
||||
# Shorten the prospective version to be the same length as the spec
|
||||
# so that we can determine if the specifier is a prefix of the
|
||||
# prospective version or not.
|
||||
prospective = prospective[:len(spec)]
|
||||
prospective = prospective[: len(spec)]
|
||||
|
||||
# Pad out our two sides with zeros so that they both equal the same
|
||||
# length.
|
||||
@@ -567,27 +555,17 @@ def _pad_version(left, right):
|
||||
right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right)))
|
||||
|
||||
# Get the rest of our versions
|
||||
left_split.append(left[len(left_split[0]):])
|
||||
right_split.append(right[len(right_split[0]):])
|
||||
left_split.append(left[len(left_split[0]) :])
|
||||
right_split.append(right[len(right_split[0]) :])
|
||||
|
||||
# Insert our padding
|
||||
left_split.insert(
|
||||
1,
|
||||
["0"] * max(0, len(right_split[0]) - len(left_split[0])),
|
||||
)
|
||||
right_split.insert(
|
||||
1,
|
||||
["0"] * max(0, len(left_split[0]) - len(right_split[0])),
|
||||
)
|
||||
left_split.insert(1, ["0"] * max(0, len(right_split[0]) - len(left_split[0])))
|
||||
right_split.insert(1, ["0"] * max(0, len(left_split[0]) - len(right_split[0])))
|
||||
|
||||
return (
|
||||
list(itertools.chain(*left_split)),
|
||||
list(itertools.chain(*right_split)),
|
||||
)
|
||||
return (list(itertools.chain(*left_split)), list(itertools.chain(*right_split)))
|
||||
|
||||
|
||||
class SpecifierSet(BaseSpecifier):
|
||||
|
||||
def __init__(self, specifiers="", prereleases=None):
|
||||
# Split on , to break each indidivual specifier into it's own item, and
|
||||
# strip each item to remove leading/trailing whitespace.
|
||||
@@ -721,10 +699,7 @@ class SpecifierSet(BaseSpecifier):
|
||||
# given version is contained within all of them.
|
||||
# Note: This use of all() here means that an empty set of specifiers
|
||||
# will always return True, this is an explicit design decision.
|
||||
return all(
|
||||
s.contains(item, prereleases=prereleases)
|
||||
for s in self._specs
|
||||
)
|
||||
return all(s.contains(item, prereleases=prereleases) for s in self._specs)
|
||||
|
||||
def filter(self, iterable, prereleases=None):
|
||||
# Determine if we're forcing a prerelease or not, if we're not forcing
|
||||
|
||||
Vendored
+1
-7
@@ -36,13 +36,7 @@ def canonicalize_version(version):
|
||||
|
||||
# Release segment
|
||||
# NB: This strips trailing '.0's to normalize
|
||||
parts.append(
|
||||
re.sub(
|
||||
r'(\.0)+$',
|
||||
'',
|
||||
".".join(str(x) for x in version.release)
|
||||
)
|
||||
)
|
||||
parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in version.release)))
|
||||
|
||||
# Pre-release
|
||||
if version.pre is not None:
|
||||
|
||||
Vendored
+14
-35
@@ -10,14 +10,11 @@ import re
|
||||
from ._structures import Infinity
|
||||
|
||||
|
||||
__all__ = [
|
||||
"parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"
|
||||
]
|
||||
__all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"]
|
||||
|
||||
|
||||
_Version = collections.namedtuple(
|
||||
"_Version",
|
||||
["epoch", "release", "dev", "pre", "post", "local"],
|
||||
"_Version", ["epoch", "release", "dev", "pre", "post", "local"]
|
||||
)
|
||||
|
||||
|
||||
@@ -40,7 +37,6 @@ class InvalidVersion(ValueError):
|
||||
|
||||
|
||||
class _BaseVersion(object):
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self._key)
|
||||
|
||||
@@ -70,7 +66,6 @@ class _BaseVersion(object):
|
||||
|
||||
|
||||
class LegacyVersion(_BaseVersion):
|
||||
|
||||
def __init__(self, version):
|
||||
self._version = str(version)
|
||||
self._key = _legacy_cmpkey(self._version)
|
||||
@@ -126,12 +121,14 @@ class LegacyVersion(_BaseVersion):
|
||||
return False
|
||||
|
||||
|
||||
_legacy_version_component_re = re.compile(
|
||||
r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE,
|
||||
)
|
||||
_legacy_version_component_re = re.compile(r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE)
|
||||
|
||||
_legacy_version_replacement_map = {
|
||||
"pre": "c", "preview": "c", "-": "final-", "rc": "c", "dev": "@",
|
||||
"pre": "c",
|
||||
"preview": "c",
|
||||
"-": "final-",
|
||||
"rc": "c",
|
||||
"dev": "@",
|
||||
}
|
||||
|
||||
|
||||
@@ -215,10 +212,7 @@ VERSION_PATTERN = r"""
|
||||
|
||||
class Version(_BaseVersion):
|
||||
|
||||
_regex = re.compile(
|
||||
r"^\s*" + VERSION_PATTERN + r"\s*$",
|
||||
re.VERBOSE | re.IGNORECASE,
|
||||
)
|
||||
_regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE)
|
||||
|
||||
def __init__(self, version):
|
||||
# Validate the version and parse it into pieces
|
||||
@@ -230,18 +224,11 @@ class Version(_BaseVersion):
|
||||
self._version = _Version(
|
||||
epoch=int(match.group("epoch")) if match.group("epoch") else 0,
|
||||
release=tuple(int(i) for i in match.group("release").split(".")),
|
||||
pre=_parse_letter_version(
|
||||
match.group("pre_l"),
|
||||
match.group("pre_n"),
|
||||
),
|
||||
pre=_parse_letter_version(match.group("pre_l"), match.group("pre_n")),
|
||||
post=_parse_letter_version(
|
||||
match.group("post_l"),
|
||||
match.group("post_n1") or match.group("post_n2"),
|
||||
),
|
||||
dev=_parse_letter_version(
|
||||
match.group("dev_l"),
|
||||
match.group("dev_n"),
|
||||
match.group("post_l"), match.group("post_n1") or match.group("post_n2")
|
||||
),
|
||||
dev=_parse_letter_version(match.group("dev_l"), match.group("dev_n")),
|
||||
local=_parse_local_version(match.group("local")),
|
||||
)
|
||||
|
||||
@@ -395,12 +382,7 @@ def _cmpkey(epoch, release, pre, post, dev, local):
|
||||
# re-reverse it back into the correct order and make it a tuple and use
|
||||
# that for our sorting key.
|
||||
release = tuple(
|
||||
reversed(list(
|
||||
itertools.dropwhile(
|
||||
lambda x: x == 0,
|
||||
reversed(release),
|
||||
)
|
||||
))
|
||||
reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release))))
|
||||
)
|
||||
|
||||
# We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0.
|
||||
@@ -433,9 +415,6 @@ def _cmpkey(epoch, release, pre, post, dev, local):
|
||||
# - Numeric segments sort numerically
|
||||
# - Shorter versions sort before longer versions when the prefixes
|
||||
# match exactly
|
||||
local = tuple(
|
||||
(i, "") if isinstance(i, int) else (-Infinity, i)
|
||||
for i in local
|
||||
)
|
||||
local = tuple((i, "") if isinstance(i, int) else (-Infinity, i) for i in local)
|
||||
|
||||
return epoch, release, pre, post, dev, local
|
||||
|
||||
Vendored
+2
-2
@@ -20,13 +20,13 @@ class Project(passa.models.projects.Project):
|
||||
pipfile = root.joinpath("Pipfile")
|
||||
if not pipfile.is_file():
|
||||
raise argparse.ArgumentError(
|
||||
"{0!r} is not a Pipfile project".format(root),
|
||||
"project", "{0!r} is not a Pipfile project".format(root),
|
||||
)
|
||||
try:
|
||||
super(Project, self).__init__(root.as_posix(), *args, **kwargs)
|
||||
except tomlkit.exceptions.ParseError as e:
|
||||
raise argparse.ArgumentError(
|
||||
"failed to parse Pipfile: {0!r}".format(str(e)),
|
||||
"project", "failed to parse Pipfile: {0!r}".format(str(e)),
|
||||
)
|
||||
|
||||
def __name__(self):
|
||||
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Thomas Kluyver
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
"""Wrappers to build Python packages using PEP 517 hooks
|
||||
"""
|
||||
|
||||
__version__ = '0.5.0'
|
||||
Vendored
+207
@@ -0,0 +1,207 @@
|
||||
"""This is invoked in a subprocess to call the build backend hooks.
|
||||
|
||||
It expects:
|
||||
- Command line args: hook_name, control_dir
|
||||
- Environment variable: PEP517_BUILD_BACKEND=entry.point:spec
|
||||
- control_dir/input.json:
|
||||
- {"kwargs": {...}}
|
||||
|
||||
Results:
|
||||
- control_dir/output.json
|
||||
- {"return_val": ...}
|
||||
"""
|
||||
from glob import glob
|
||||
from importlib import import_module
|
||||
import os
|
||||
from os.path import join as pjoin
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
# This is run as a script, not a module, so it can't do a relative import
|
||||
import compat
|
||||
|
||||
|
||||
class BackendUnavailable(Exception):
|
||||
"""Raised if we cannot import the backend"""
|
||||
|
||||
|
||||
def _build_backend():
|
||||
"""Find and load the build backend"""
|
||||
ep = os.environ['PEP517_BUILD_BACKEND']
|
||||
mod_path, _, obj_path = ep.partition(':')
|
||||
try:
|
||||
obj = import_module(mod_path)
|
||||
except ImportError:
|
||||
raise BackendUnavailable
|
||||
if obj_path:
|
||||
for path_part in obj_path.split('.'):
|
||||
obj = getattr(obj, path_part)
|
||||
return obj
|
||||
|
||||
|
||||
def get_requires_for_build_wheel(config_settings):
|
||||
"""Invoke the optional get_requires_for_build_wheel hook
|
||||
|
||||
Returns [] if the hook is not defined.
|
||||
"""
|
||||
backend = _build_backend()
|
||||
try:
|
||||
hook = backend.get_requires_for_build_wheel
|
||||
except AttributeError:
|
||||
return []
|
||||
else:
|
||||
return hook(config_settings)
|
||||
|
||||
|
||||
def prepare_metadata_for_build_wheel(metadata_directory, config_settings):
|
||||
"""Invoke optional prepare_metadata_for_build_wheel
|
||||
|
||||
Implements a fallback by building a wheel if the hook isn't defined.
|
||||
"""
|
||||
backend = _build_backend()
|
||||
try:
|
||||
hook = backend.prepare_metadata_for_build_wheel
|
||||
except AttributeError:
|
||||
return _get_wheel_metadata_from_wheel(backend, metadata_directory,
|
||||
config_settings)
|
||||
else:
|
||||
return hook(metadata_directory, config_settings)
|
||||
|
||||
|
||||
WHEEL_BUILT_MARKER = 'PEP517_ALREADY_BUILT_WHEEL'
|
||||
|
||||
|
||||
def _dist_info_files(whl_zip):
|
||||
"""Identify the .dist-info folder inside a wheel ZipFile."""
|
||||
res = []
|
||||
for path in whl_zip.namelist():
|
||||
m = re.match(r'[^/\\]+-[^/\\]+\.dist-info/', path)
|
||||
if m:
|
||||
res.append(path)
|
||||
if res:
|
||||
return res
|
||||
raise Exception("No .dist-info folder found in wheel")
|
||||
|
||||
|
||||
def _get_wheel_metadata_from_wheel(
|
||||
backend, metadata_directory, config_settings):
|
||||
"""Build a wheel and extract the metadata from it.
|
||||
|
||||
Fallback for when the build backend does not
|
||||
define the 'get_wheel_metadata' hook.
|
||||
"""
|
||||
from zipfile import ZipFile
|
||||
whl_basename = backend.build_wheel(metadata_directory, config_settings)
|
||||
with open(os.path.join(metadata_directory, WHEEL_BUILT_MARKER), 'wb'):
|
||||
pass # Touch marker file
|
||||
|
||||
whl_file = os.path.join(metadata_directory, whl_basename)
|
||||
with ZipFile(whl_file) as zipf:
|
||||
dist_info = _dist_info_files(zipf)
|
||||
zipf.extractall(path=metadata_directory, members=dist_info)
|
||||
return dist_info[0].split('/')[0]
|
||||
|
||||
|
||||
def _find_already_built_wheel(metadata_directory):
|
||||
"""Check for a wheel already built during the get_wheel_metadata hook.
|
||||
"""
|
||||
if not metadata_directory:
|
||||
return None
|
||||
metadata_parent = os.path.dirname(metadata_directory)
|
||||
if not os.path.isfile(pjoin(metadata_parent, WHEEL_BUILT_MARKER)):
|
||||
return None
|
||||
|
||||
whl_files = glob(os.path.join(metadata_parent, '*.whl'))
|
||||
if not whl_files:
|
||||
print('Found wheel built marker, but no .whl files')
|
||||
return None
|
||||
if len(whl_files) > 1:
|
||||
print('Found multiple .whl files; unspecified behaviour. '
|
||||
'Will call build_wheel.')
|
||||
return None
|
||||
|
||||
# Exactly one .whl file
|
||||
return whl_files[0]
|
||||
|
||||
|
||||
def build_wheel(wheel_directory, config_settings, metadata_directory=None):
|
||||
"""Invoke the mandatory build_wheel hook.
|
||||
|
||||
If a wheel was already built in the
|
||||
prepare_metadata_for_build_wheel fallback, this
|
||||
will copy it rather than rebuilding the wheel.
|
||||
"""
|
||||
prebuilt_whl = _find_already_built_wheel(metadata_directory)
|
||||
if prebuilt_whl:
|
||||
shutil.copy2(prebuilt_whl, wheel_directory)
|
||||
return os.path.basename(prebuilt_whl)
|
||||
|
||||
return _build_backend().build_wheel(wheel_directory, config_settings,
|
||||
metadata_directory)
|
||||
|
||||
|
||||
def get_requires_for_build_sdist(config_settings):
|
||||
"""Invoke the optional get_requires_for_build_wheel hook
|
||||
|
||||
Returns [] if the hook is not defined.
|
||||
"""
|
||||
backend = _build_backend()
|
||||
try:
|
||||
hook = backend.get_requires_for_build_sdist
|
||||
except AttributeError:
|
||||
return []
|
||||
else:
|
||||
return hook(config_settings)
|
||||
|
||||
|
||||
class _DummyException(Exception):
|
||||
"""Nothing should ever raise this exception"""
|
||||
|
||||
|
||||
class GotUnsupportedOperation(Exception):
|
||||
"""For internal use when backend raises UnsupportedOperation"""
|
||||
|
||||
|
||||
def build_sdist(sdist_directory, config_settings):
|
||||
"""Invoke the mandatory build_sdist hook."""
|
||||
backend = _build_backend()
|
||||
try:
|
||||
return backend.build_sdist(sdist_directory, config_settings)
|
||||
except getattr(backend, 'UnsupportedOperation', _DummyException):
|
||||
raise GotUnsupportedOperation
|
||||
|
||||
|
||||
HOOK_NAMES = {
|
||||
'get_requires_for_build_wheel',
|
||||
'prepare_metadata_for_build_wheel',
|
||||
'build_wheel',
|
||||
'get_requires_for_build_sdist',
|
||||
'build_sdist',
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 3:
|
||||
sys.exit("Needs args: hook_name, control_dir")
|
||||
hook_name = sys.argv[1]
|
||||
control_dir = sys.argv[2]
|
||||
if hook_name not in HOOK_NAMES:
|
||||
sys.exit("Unknown hook: %s" % hook_name)
|
||||
hook = globals()[hook_name]
|
||||
|
||||
hook_input = compat.read_json(pjoin(control_dir, 'input.json'))
|
||||
|
||||
json_out = {'unsupported': False, 'return_val': None}
|
||||
try:
|
||||
json_out['return_val'] = hook(**hook_input['kwargs'])
|
||||
except BackendUnavailable:
|
||||
json_out['no_backend'] = True
|
||||
except GotUnsupportedOperation:
|
||||
json_out['unsupported'] = True
|
||||
|
||||
compat.write_json(json_out, pjoin(control_dir, 'output.json'), indent=2)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Vendored
+108
@@ -0,0 +1,108 @@
|
||||
"""Build a project using PEP 517 hooks.
|
||||
"""
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import contextlib
|
||||
import pytoml
|
||||
import shutil
|
||||
import errno
|
||||
import tempfile
|
||||
|
||||
from .envbuild import BuildEnvironment
|
||||
from .wrappers import Pep517HookCaller
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def tempdir():
|
||||
td = tempfile.mkdtemp()
|
||||
try:
|
||||
yield td
|
||||
finally:
|
||||
shutil.rmtree(td)
|
||||
|
||||
|
||||
def _do_build(hooks, env, dist, dest):
|
||||
get_requires_name = 'get_requires_for_build_{dist}'.format(**locals())
|
||||
get_requires = getattr(hooks, get_requires_name)
|
||||
reqs = get_requires({})
|
||||
log.info('Got build requires: %s', reqs)
|
||||
|
||||
env.pip_install(reqs)
|
||||
log.info('Installed dynamic build dependencies')
|
||||
|
||||
with tempdir() as td:
|
||||
log.info('Trying to build %s in %s', dist, td)
|
||||
build_name = 'build_{dist}'.format(**locals())
|
||||
build = getattr(hooks, build_name)
|
||||
filename = build(td, {})
|
||||
source = os.path.join(td, filename)
|
||||
shutil.move(source, os.path.join(dest, os.path.basename(filename)))
|
||||
|
||||
|
||||
def mkdir_p(*args, **kwargs):
|
||||
"""Like `mkdir`, but does not raise an exception if the
|
||||
directory already exists.
|
||||
"""
|
||||
try:
|
||||
return os.mkdir(*args, **kwargs)
|
||||
except OSError as exc:
|
||||
if exc.errno != errno.EEXIST:
|
||||
raise
|
||||
|
||||
|
||||
def build(source_dir, dist, dest=None):
|
||||
pyproject = os.path.join(source_dir, 'pyproject.toml')
|
||||
dest = os.path.join(source_dir, dest or 'dist')
|
||||
mkdir_p(dest)
|
||||
|
||||
with open(pyproject) as f:
|
||||
pyproject_data = pytoml.load(f)
|
||||
# Ensure the mandatory data can be loaded
|
||||
buildsys = pyproject_data['build-system']
|
||||
requires = buildsys['requires']
|
||||
backend = buildsys['build-backend']
|
||||
|
||||
hooks = Pep517HookCaller(source_dir, backend)
|
||||
|
||||
with BuildEnvironment() as env:
|
||||
env.pip_install(requires)
|
||||
_do_build(hooks, env, dist, dest)
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
'source_dir',
|
||||
help="A directory containing pyproject.toml",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--binary', '-b',
|
||||
action='store_true',
|
||||
default=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
'--source', '-s',
|
||||
action='store_true',
|
||||
default=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
'--out-dir', '-o',
|
||||
help="Destination in which to save the builds relative to source dir",
|
||||
)
|
||||
|
||||
|
||||
def main(args):
|
||||
# determine which dists to build
|
||||
dists = list(filter(None, (
|
||||
'sdist' if args.source or not args.binary else None,
|
||||
'wheel' if args.binary or not args.source else None,
|
||||
)))
|
||||
|
||||
for dist in dists:
|
||||
build(args.source_dir, dist, args.out_dir)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(parser.parse_args())
|
||||
Vendored
+202
@@ -0,0 +1,202 @@
|
||||
"""Check a project and backend by attempting to build using PEP 517 hooks.
|
||||
"""
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
from os.path import isfile, join as pjoin
|
||||
from pytoml import TomlError, load as toml_load
|
||||
import shutil
|
||||
from subprocess import CalledProcessError
|
||||
import sys
|
||||
import tarfile
|
||||
from tempfile import mkdtemp
|
||||
import zipfile
|
||||
|
||||
from .colorlog import enable_colourful_output
|
||||
from .envbuild import BuildEnvironment
|
||||
from .wrappers import Pep517HookCaller
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def check_build_sdist(hooks, build_sys_requires):
|
||||
with BuildEnvironment() as env:
|
||||
try:
|
||||
env.pip_install(build_sys_requires)
|
||||
log.info('Installed static build dependencies')
|
||||
except CalledProcessError:
|
||||
log.error('Failed to install static build dependencies')
|
||||
return False
|
||||
|
||||
try:
|
||||
reqs = hooks.get_requires_for_build_sdist({})
|
||||
log.info('Got build requires: %s', reqs)
|
||||
except Exception:
|
||||
log.error('Failure in get_requires_for_build_sdist', exc_info=True)
|
||||
return False
|
||||
|
||||
try:
|
||||
env.pip_install(reqs)
|
||||
log.info('Installed dynamic build dependencies')
|
||||
except CalledProcessError:
|
||||
log.error('Failed to install dynamic build dependencies')
|
||||
return False
|
||||
|
||||
td = mkdtemp()
|
||||
log.info('Trying to build sdist in %s', td)
|
||||
try:
|
||||
try:
|
||||
filename = hooks.build_sdist(td, {})
|
||||
log.info('build_sdist returned %r', filename)
|
||||
except Exception:
|
||||
log.info('Failure in build_sdist', exc_info=True)
|
||||
return False
|
||||
|
||||
if not filename.endswith('.tar.gz'):
|
||||
log.error(
|
||||
"Filename %s doesn't have .tar.gz extension", filename)
|
||||
return False
|
||||
|
||||
path = pjoin(td, filename)
|
||||
if isfile(path):
|
||||
log.info("Output file %s exists", path)
|
||||
else:
|
||||
log.error("Output file %s does not exist", path)
|
||||
return False
|
||||
|
||||
if tarfile.is_tarfile(path):
|
||||
log.info("Output file is a tar file")
|
||||
else:
|
||||
log.error("Output file is not a tar file")
|
||||
return False
|
||||
|
||||
finally:
|
||||
shutil.rmtree(td)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def check_build_wheel(hooks, build_sys_requires):
|
||||
with BuildEnvironment() as env:
|
||||
try:
|
||||
env.pip_install(build_sys_requires)
|
||||
log.info('Installed static build dependencies')
|
||||
except CalledProcessError:
|
||||
log.error('Failed to install static build dependencies')
|
||||
return False
|
||||
|
||||
try:
|
||||
reqs = hooks.get_requires_for_build_wheel({})
|
||||
log.info('Got build requires: %s', reqs)
|
||||
except Exception:
|
||||
log.error('Failure in get_requires_for_build_sdist', exc_info=True)
|
||||
return False
|
||||
|
||||
try:
|
||||
env.pip_install(reqs)
|
||||
log.info('Installed dynamic build dependencies')
|
||||
except CalledProcessError:
|
||||
log.error('Failed to install dynamic build dependencies')
|
||||
return False
|
||||
|
||||
td = mkdtemp()
|
||||
log.info('Trying to build wheel in %s', td)
|
||||
try:
|
||||
try:
|
||||
filename = hooks.build_wheel(td, {})
|
||||
log.info('build_wheel returned %r', filename)
|
||||
except Exception:
|
||||
log.info('Failure in build_wheel', exc_info=True)
|
||||
return False
|
||||
|
||||
if not filename.endswith('.whl'):
|
||||
log.error("Filename %s doesn't have .whl extension", filename)
|
||||
return False
|
||||
|
||||
path = pjoin(td, filename)
|
||||
if isfile(path):
|
||||
log.info("Output file %s exists", path)
|
||||
else:
|
||||
log.error("Output file %s does not exist", path)
|
||||
return False
|
||||
|
||||
if zipfile.is_zipfile(path):
|
||||
log.info("Output file is a zip file")
|
||||
else:
|
||||
log.error("Output file is not a zip file")
|
||||
return False
|
||||
|
||||
finally:
|
||||
shutil.rmtree(td)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def check(source_dir):
|
||||
pyproject = pjoin(source_dir, 'pyproject.toml')
|
||||
if isfile(pyproject):
|
||||
log.info('Found pyproject.toml')
|
||||
else:
|
||||
log.error('Missing pyproject.toml')
|
||||
return False
|
||||
|
||||
try:
|
||||
with open(pyproject) as f:
|
||||
pyproject_data = toml_load(f)
|
||||
# Ensure the mandatory data can be loaded
|
||||
buildsys = pyproject_data['build-system']
|
||||
requires = buildsys['requires']
|
||||
backend = buildsys['build-backend']
|
||||
log.info('Loaded pyproject.toml')
|
||||
except (TomlError, KeyError):
|
||||
log.error("Invalid pyproject.toml", exc_info=True)
|
||||
return False
|
||||
|
||||
hooks = Pep517HookCaller(source_dir, backend)
|
||||
|
||||
sdist_ok = check_build_sdist(hooks, requires)
|
||||
wheel_ok = check_build_wheel(hooks, requires)
|
||||
|
||||
if not sdist_ok:
|
||||
log.warning('Sdist checks failed; scroll up to see')
|
||||
if not wheel_ok:
|
||||
log.warning('Wheel checks failed')
|
||||
|
||||
return sdist_ok
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument(
|
||||
'source_dir',
|
||||
help="A directory containing pyproject.toml")
|
||||
args = ap.parse_args(argv)
|
||||
|
||||
enable_colourful_output()
|
||||
|
||||
ok = check(args.source_dir)
|
||||
|
||||
if ok:
|
||||
print(ansi('Checks passed', 'green'))
|
||||
else:
|
||||
print(ansi('Checks failed', 'red'))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
ansi_codes = {
|
||||
'reset': '\x1b[0m',
|
||||
'bold': '\x1b[1m',
|
||||
'red': '\x1b[31m',
|
||||
'green': '\x1b[32m',
|
||||
}
|
||||
|
||||
|
||||
def ansi(s, attr):
|
||||
if os.name != 'nt' and sys.stdout.isatty():
|
||||
return ansi_codes[attr] + str(s) + ansi_codes['reset']
|
||||
else:
|
||||
return str(s)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Vendored
+115
@@ -0,0 +1,115 @@
|
||||
"""Nicer log formatting with colours.
|
||||
|
||||
Code copied from Tornado, Apache licensed.
|
||||
"""
|
||||
# Copyright 2012 Facebook
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
try:
|
||||
import curses
|
||||
except ImportError:
|
||||
curses = None
|
||||
|
||||
|
||||
def _stderr_supports_color():
|
||||
color = False
|
||||
if curses and hasattr(sys.stderr, 'isatty') and sys.stderr.isatty():
|
||||
try:
|
||||
curses.setupterm()
|
||||
if curses.tigetnum("colors") > 0:
|
||||
color = True
|
||||
except Exception:
|
||||
pass
|
||||
return color
|
||||
|
||||
|
||||
class LogFormatter(logging.Formatter):
|
||||
"""Log formatter with colour support
|
||||
"""
|
||||
DEFAULT_COLORS = {
|
||||
logging.INFO: 2, # Green
|
||||
logging.WARNING: 3, # Yellow
|
||||
logging.ERROR: 1, # Red
|
||||
logging.CRITICAL: 1,
|
||||
}
|
||||
|
||||
def __init__(self, color=True, datefmt=None):
|
||||
r"""
|
||||
:arg bool color: Enables color support.
|
||||
:arg string fmt: Log message format.
|
||||
It will be applied to the attributes dict of log records. The
|
||||
text between ``%(color)s`` and ``%(end_color)s`` will be colored
|
||||
depending on the level if color support is on.
|
||||
:arg dict colors: color mappings from logging level to terminal color
|
||||
code
|
||||
:arg string datefmt: Datetime format.
|
||||
Used for formatting ``(asctime)`` placeholder in ``prefix_fmt``.
|
||||
.. versionchanged:: 3.2
|
||||
Added ``fmt`` and ``datefmt`` arguments.
|
||||
"""
|
||||
logging.Formatter.__init__(self, datefmt=datefmt)
|
||||
self._colors = {}
|
||||
if color and _stderr_supports_color():
|
||||
# The curses module has some str/bytes confusion in
|
||||
# python3. Until version 3.2.3, most methods return
|
||||
# bytes, but only accept strings. In addition, we want to
|
||||
# output these strings with the logging module, which
|
||||
# works with unicode strings. The explicit calls to
|
||||
# unicode() below are harmless in python2 but will do the
|
||||
# right conversion in python 3.
|
||||
fg_color = (curses.tigetstr("setaf") or
|
||||
curses.tigetstr("setf") or "")
|
||||
if (3, 0) < sys.version_info < (3, 2, 3):
|
||||
fg_color = str(fg_color, "ascii")
|
||||
|
||||
for levelno, code in self.DEFAULT_COLORS.items():
|
||||
self._colors[levelno] = str(
|
||||
curses.tparm(fg_color, code), "ascii")
|
||||
self._normal = str(curses.tigetstr("sgr0"), "ascii")
|
||||
|
||||
scr = curses.initscr()
|
||||
self.termwidth = scr.getmaxyx()[1]
|
||||
curses.endwin()
|
||||
else:
|
||||
self._normal = ''
|
||||
# Default width is usually 80, but too wide is
|
||||
# worse than too narrow
|
||||
self.termwidth = 70
|
||||
|
||||
def formatMessage(self, record):
|
||||
mlen = len(record.message)
|
||||
right_text = '{initial}-{name}'.format(initial=record.levelname[0],
|
||||
name=record.name)
|
||||
if mlen + len(right_text) < self.termwidth:
|
||||
space = ' ' * (self.termwidth - (mlen + len(right_text)))
|
||||
else:
|
||||
space = ' '
|
||||
|
||||
if record.levelno in self._colors:
|
||||
start_color = self._colors[record.levelno]
|
||||
end_color = self._normal
|
||||
else:
|
||||
start_color = end_color = ''
|
||||
|
||||
return record.message + space + start_color + right_text + end_color
|
||||
|
||||
|
||||
def enable_colourful_output(level=logging.INFO):
|
||||
handler = logging.StreamHandler()
|
||||
handler.setFormatter(LogFormatter())
|
||||
logging.root.addHandler(handler)
|
||||
logging.root.setLevel(level)
|
||||
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
"""Handle reading and writing JSON in UTF-8, on Python 3 and 2."""
|
||||
import json
|
||||
import sys
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
# Python 3
|
||||
def write_json(obj, path, **kwargs):
|
||||
with open(path, 'w', encoding='utf-8') as f:
|
||||
json.dump(obj, f, **kwargs)
|
||||
|
||||
def read_json(path):
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
|
||||
else:
|
||||
# Python 2
|
||||
def write_json(obj, path, **kwargs):
|
||||
with open(path, 'wb') as f:
|
||||
json.dump(obj, f, encoding='utf-8', **kwargs)
|
||||
|
||||
def read_json(path):
|
||||
with open(path, 'rb') as f:
|
||||
return json.load(f)
|
||||
Vendored
+158
@@ -0,0 +1,158 @@
|
||||
"""Build wheels/sdists by installing build deps to a temporary environment.
|
||||
"""
|
||||
|
||||
import os
|
||||
import logging
|
||||
import pytoml
|
||||
import shutil
|
||||
from subprocess import check_call
|
||||
import sys
|
||||
from sysconfig import get_paths
|
||||
from tempfile import mkdtemp
|
||||
|
||||
from .wrappers import Pep517HookCaller
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _load_pyproject(source_dir):
|
||||
with open(os.path.join(source_dir, 'pyproject.toml')) as f:
|
||||
pyproject_data = pytoml.load(f)
|
||||
buildsys = pyproject_data['build-system']
|
||||
return buildsys['requires'], buildsys['build-backend']
|
||||
|
||||
|
||||
class BuildEnvironment(object):
|
||||
"""Context manager to install build deps in a simple temporary environment
|
||||
|
||||
Based on code I wrote for pip, which is MIT licensed.
|
||||
"""
|
||||
# Copyright (c) 2008-2016 The pip developers (see AUTHORS.txt file)
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
path = None
|
||||
|
||||
def __init__(self, cleanup=True):
|
||||
self._cleanup = cleanup
|
||||
|
||||
def __enter__(self):
|
||||
self.path = mkdtemp(prefix='pep517-build-env-')
|
||||
log.info('Temporary build environment: %s', self.path)
|
||||
|
||||
self.save_path = os.environ.get('PATH', None)
|
||||
self.save_pythonpath = os.environ.get('PYTHONPATH', None)
|
||||
|
||||
install_scheme = 'nt' if (os.name == 'nt') else 'posix_prefix'
|
||||
install_dirs = get_paths(install_scheme, vars={
|
||||
'base': self.path,
|
||||
'platbase': self.path,
|
||||
})
|
||||
|
||||
scripts = install_dirs['scripts']
|
||||
if self.save_path:
|
||||
os.environ['PATH'] = scripts + os.pathsep + self.save_path
|
||||
else:
|
||||
os.environ['PATH'] = scripts + os.pathsep + os.defpath
|
||||
|
||||
if install_dirs['purelib'] == install_dirs['platlib']:
|
||||
lib_dirs = install_dirs['purelib']
|
||||
else:
|
||||
lib_dirs = install_dirs['purelib'] + os.pathsep + \
|
||||
install_dirs['platlib']
|
||||
if self.save_pythonpath:
|
||||
os.environ['PYTHONPATH'] = lib_dirs + os.pathsep + \
|
||||
self.save_pythonpath
|
||||
else:
|
||||
os.environ['PYTHONPATH'] = lib_dirs
|
||||
|
||||
return self
|
||||
|
||||
def pip_install(self, reqs):
|
||||
"""Install dependencies into this env by calling pip in a subprocess"""
|
||||
if not reqs:
|
||||
return
|
||||
log.info('Calling pip to install %s', reqs)
|
||||
check_call([
|
||||
sys.executable, '-m', 'pip', 'install', '--ignore-installed',
|
||||
'--prefix', self.path] + list(reqs))
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
needs_cleanup = (
|
||||
self._cleanup and
|
||||
self.path is not None and
|
||||
os.path.isdir(self.path)
|
||||
)
|
||||
if needs_cleanup:
|
||||
shutil.rmtree(self.path)
|
||||
|
||||
if self.save_path is None:
|
||||
os.environ.pop('PATH', None)
|
||||
else:
|
||||
os.environ['PATH'] = self.save_path
|
||||
|
||||
if self.save_pythonpath is None:
|
||||
os.environ.pop('PYTHONPATH', None)
|
||||
else:
|
||||
os.environ['PYTHONPATH'] = self.save_pythonpath
|
||||
|
||||
|
||||
def build_wheel(source_dir, wheel_dir, config_settings=None):
|
||||
"""Build a wheel from a source directory using PEP 517 hooks.
|
||||
|
||||
:param str source_dir: Source directory containing pyproject.toml
|
||||
:param str wheel_dir: Target directory to create wheel in
|
||||
:param dict config_settings: Options to pass to build backend
|
||||
|
||||
This is a blocking function which will run pip in a subprocess to install
|
||||
build requirements.
|
||||
"""
|
||||
if config_settings is None:
|
||||
config_settings = {}
|
||||
requires, backend = _load_pyproject(source_dir)
|
||||
hooks = Pep517HookCaller(source_dir, backend)
|
||||
|
||||
with BuildEnvironment() as env:
|
||||
env.pip_install(requires)
|
||||
reqs = hooks.get_requires_for_build_wheel(config_settings)
|
||||
env.pip_install(reqs)
|
||||
return hooks.build_wheel(wheel_dir, config_settings)
|
||||
|
||||
|
||||
def build_sdist(source_dir, sdist_dir, config_settings=None):
|
||||
"""Build an sdist from a source directory using PEP 517 hooks.
|
||||
|
||||
:param str source_dir: Source directory containing pyproject.toml
|
||||
:param str sdist_dir: Target directory to place sdist in
|
||||
:param dict config_settings: Options to pass to build backend
|
||||
|
||||
This is a blocking function which will run pip in a subprocess to install
|
||||
build requirements.
|
||||
"""
|
||||
if config_settings is None:
|
||||
config_settings = {}
|
||||
requires, backend = _load_pyproject(source_dir)
|
||||
hooks = Pep517HookCaller(source_dir, backend)
|
||||
|
||||
with BuildEnvironment() as env:
|
||||
env.pip_install(requires)
|
||||
reqs = hooks.get_requires_for_build_sdist(config_settings)
|
||||
env.pip_install(reqs)
|
||||
return hooks.build_sdist(sdist_dir, config_settings)
|
||||
Vendored
+163
@@ -0,0 +1,163 @@
|
||||
from contextlib import contextmanager
|
||||
import os
|
||||
from os.path import dirname, abspath, join as pjoin
|
||||
import shutil
|
||||
from subprocess import check_call
|
||||
import sys
|
||||
from tempfile import mkdtemp
|
||||
|
||||
from . import compat
|
||||
|
||||
_in_proc_script = pjoin(dirname(abspath(__file__)), '_in_process.py')
|
||||
|
||||
|
||||
@contextmanager
|
||||
def tempdir():
|
||||
td = mkdtemp()
|
||||
try:
|
||||
yield td
|
||||
finally:
|
||||
shutil.rmtree(td)
|
||||
|
||||
|
||||
class BackendUnavailable(Exception):
|
||||
"""Will be raised if the backend cannot be imported in the hook process."""
|
||||
|
||||
|
||||
class UnsupportedOperation(Exception):
|
||||
"""May be raised by build_sdist if the backend indicates that it can't."""
|
||||
|
||||
|
||||
def default_subprocess_runner(cmd, cwd=None, extra_environ=None):
|
||||
"""The default method of calling the wrapper subprocess."""
|
||||
env = os.environ.copy()
|
||||
if extra_environ:
|
||||
env.update(extra_environ)
|
||||
|
||||
check_call(cmd, cwd=cwd, env=env)
|
||||
|
||||
|
||||
class Pep517HookCaller(object):
|
||||
"""A wrapper around a source directory to be built with a PEP 517 backend.
|
||||
|
||||
source_dir : The path to the source directory, containing pyproject.toml.
|
||||
backend : The build backend spec, as per PEP 517, from pyproject.toml.
|
||||
"""
|
||||
def __init__(self, source_dir, build_backend):
|
||||
self.source_dir = abspath(source_dir)
|
||||
self.build_backend = build_backend
|
||||
self._subprocess_runner = default_subprocess_runner
|
||||
|
||||
# TODO: Is this over-engineered? Maybe frontends only need to
|
||||
# set this when creating the wrapper, not on every call.
|
||||
@contextmanager
|
||||
def subprocess_runner(self, runner):
|
||||
prev = self._subprocess_runner
|
||||
self._subprocess_runner = runner
|
||||
yield
|
||||
self._subprocess_runner = prev
|
||||
|
||||
def get_requires_for_build_wheel(self, config_settings=None):
|
||||
"""Identify packages required for building a wheel
|
||||
|
||||
Returns a list of dependency specifications, e.g.:
|
||||
["wheel >= 0.25", "setuptools"]
|
||||
|
||||
This does not include requirements specified in pyproject.toml.
|
||||
It returns the result of calling the equivalently named hook in a
|
||||
subprocess.
|
||||
"""
|
||||
return self._call_hook('get_requires_for_build_wheel', {
|
||||
'config_settings': config_settings
|
||||
})
|
||||
|
||||
def prepare_metadata_for_build_wheel(
|
||||
self, metadata_directory, config_settings=None):
|
||||
"""Prepare a *.dist-info folder with metadata for this project.
|
||||
|
||||
Returns the name of the newly created folder.
|
||||
|
||||
If the build backend defines a hook with this name, it will be called
|
||||
in a subprocess. If not, the backend will be asked to build a wheel,
|
||||
and the dist-info extracted from that.
|
||||
"""
|
||||
return self._call_hook('prepare_metadata_for_build_wheel', {
|
||||
'metadata_directory': abspath(metadata_directory),
|
||||
'config_settings': config_settings,
|
||||
})
|
||||
|
||||
def build_wheel(
|
||||
self, wheel_directory, config_settings=None,
|
||||
metadata_directory=None):
|
||||
"""Build a wheel from this project.
|
||||
|
||||
Returns the name of the newly created file.
|
||||
|
||||
In general, this will call the 'build_wheel' hook in the backend.
|
||||
However, if that was previously called by
|
||||
'prepare_metadata_for_build_wheel', and the same metadata_directory is
|
||||
used, the previously built wheel will be copied to wheel_directory.
|
||||
"""
|
||||
if metadata_directory is not None:
|
||||
metadata_directory = abspath(metadata_directory)
|
||||
return self._call_hook('build_wheel', {
|
||||
'wheel_directory': abspath(wheel_directory),
|
||||
'config_settings': config_settings,
|
||||
'metadata_directory': metadata_directory,
|
||||
})
|
||||
|
||||
def get_requires_for_build_sdist(self, config_settings=None):
|
||||
"""Identify packages required for building a wheel
|
||||
|
||||
Returns a list of dependency specifications, e.g.:
|
||||
["setuptools >= 26"]
|
||||
|
||||
This does not include requirements specified in pyproject.toml.
|
||||
It returns the result of calling the equivalently named hook in a
|
||||
subprocess.
|
||||
"""
|
||||
return self._call_hook('get_requires_for_build_sdist', {
|
||||
'config_settings': config_settings
|
||||
})
|
||||
|
||||
def build_sdist(self, sdist_directory, config_settings=None):
|
||||
"""Build an sdist from this project.
|
||||
|
||||
Returns the name of the newly created file.
|
||||
|
||||
This calls the 'build_sdist' backend hook in a subprocess.
|
||||
"""
|
||||
return self._call_hook('build_sdist', {
|
||||
'sdist_directory': abspath(sdist_directory),
|
||||
'config_settings': config_settings,
|
||||
})
|
||||
|
||||
def _call_hook(self, hook_name, kwargs):
|
||||
# On Python 2, pytoml returns Unicode values (which is correct) but the
|
||||
# environment passed to check_call needs to contain string values. We
|
||||
# convert here by encoding using ASCII (the backend can only contain
|
||||
# letters, digits and _, . and : characters, and will be used as a
|
||||
# Python identifier, so non-ASCII content is wrong on Python 2 in
|
||||
# any case).
|
||||
if sys.version_info[0] == 2:
|
||||
build_backend = self.build_backend.encode('ASCII')
|
||||
else:
|
||||
build_backend = self.build_backend
|
||||
|
||||
with tempdir() as td:
|
||||
compat.write_json({'kwargs': kwargs}, pjoin(td, 'input.json'),
|
||||
indent=2)
|
||||
|
||||
# Run the hook in a subprocess
|
||||
self._subprocess_runner(
|
||||
[sys.executable, _in_proc_script, hook_name, td],
|
||||
cwd=self.source_dir,
|
||||
extra_environ={'PEP517_BUILD_BACKEND': build_backend}
|
||||
)
|
||||
|
||||
data = compat.read_json(pjoin(td, 'output.json'))
|
||||
if data.get('unsupported'):
|
||||
raise UnsupportedOperation
|
||||
if data.get('no_backend'):
|
||||
raise BackendUnavailable
|
||||
return data['return_val']
|
||||
Vendored
+1
-1
@@ -22,7 +22,7 @@ import pkg_resources
|
||||
# from graphviz import backend, Digraph
|
||||
|
||||
|
||||
__version__ = '0.13.0'
|
||||
__version__ = '0.13.1'
|
||||
|
||||
|
||||
flatten = chain.from_iterable
|
||||
|
||||
Vendored
+1497
-816
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -5,8 +5,8 @@
|
||||
__title__ = 'requests'
|
||||
__description__ = 'Python HTTP for Humans.'
|
||||
__url__ = 'http://python-requests.org'
|
||||
__version__ = '2.20.1'
|
||||
__build__ = 0x022001
|
||||
__version__ = '2.21.0'
|
||||
__build__ = 0x022100
|
||||
__author__ = 'Kenneth Reitz'
|
||||
__author_email__ = 'me@kennethreitz.org'
|
||||
__license__ = 'Apache 2.0'
|
||||
|
||||
Vendored
+1
-1
@@ -781,7 +781,7 @@ class Response(object):
|
||||
|
||||
return chunks
|
||||
|
||||
def iter_lines(self, chunk_size=ITER_CHUNK_SIZE, decode_unicode=None, delimiter=None):
|
||||
def iter_lines(self, chunk_size=ITER_CHUNK_SIZE, decode_unicode=False, delimiter=None):
|
||||
"""Iterates over the response data, one line at a time. When
|
||||
stream=True is set on the request, this avoids reading the
|
||||
content at once into memory for large responses.
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
# -*- coding=utf-8 -*-
|
||||
__version__ = '1.3.2'
|
||||
from __future__ import absolute_import, print_function
|
||||
__version__ = '1.4.0'
|
||||
|
||||
import logging
|
||||
import warnings
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import copy
|
||||
import os
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# -*- coding=utf-8 -*-
|
||||
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
from __future__ import absolute_import, unicode_literals, print_function
|
||||
|
||||
import collections
|
||||
import io
|
||||
|
||||
+875
-217
File diff suppressed because it is too large
Load Diff
+119
-61
@@ -1,4 +1,6 @@
|
||||
# -*- coding=utf-8 -*-
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import contextlib
|
||||
import os
|
||||
import sys
|
||||
@@ -22,7 +24,9 @@ from vistir.contextmanagers import cd
|
||||
from vistir.misc import run
|
||||
from vistir.path import create_tracked_tempdir, ensure_mkdir_p, mkdir_p
|
||||
|
||||
from .utils import init_requirement, get_pyproject
|
||||
from .utils import init_requirement, get_pyproject, get_name_variants
|
||||
from ..environment import MYPY_RUNNING
|
||||
from ..exceptions import RequirementError
|
||||
|
||||
try:
|
||||
from os import scandir
|
||||
@@ -30,6 +34,12 @@ except ImportError:
|
||||
from scandir import scandir
|
||||
|
||||
|
||||
if MYPY_RUNNING:
|
||||
from typing import Any, Dict, List, Generator, Optional, Union
|
||||
from pip_shims.shims import InstallRequirement
|
||||
from pkg_resources import Requirement as PkgResourcesRequirement
|
||||
|
||||
|
||||
CACHE_DIR = os.environ.get("PIPENV_CACHE_DIR", user_cache_dir("pipenv"))
|
||||
|
||||
# The following are necessary for people who like to use "if __name__" conditionals
|
||||
@@ -40,6 +50,7 @@ _setup_distribution = None
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _suppress_distutils_logs():
|
||||
# type: () -> None
|
||||
"""Hack to hide noise generated by `setup.py develop`.
|
||||
|
||||
There isn't a good way to suppress them now, so let's monky-patch.
|
||||
@@ -58,17 +69,22 @@ def _suppress_distutils_logs():
|
||||
|
||||
|
||||
@ensure_mkdir_p(mode=0o775)
|
||||
def _get_src_dir():
|
||||
def _get_src_dir(root):
|
||||
# type: (str) -> str
|
||||
src = os.environ.get("PIP_SRC")
|
||||
if src:
|
||||
return src
|
||||
virtual_env = os.environ.get("VIRTUAL_ENV")
|
||||
if virtual_env:
|
||||
return os.path.join(virtual_env, "src")
|
||||
return os.path.join(os.getcwd(), "src") # Match pip's behavior.
|
||||
if not root:
|
||||
# Intentionally don't match pip's behavior here -- this is a temporary copy
|
||||
root = create_tracked_tempdir(prefix="requirementslib-", suffix="-src")
|
||||
return os.path.join(root, "src")
|
||||
|
||||
|
||||
def ensure_reqs(reqs):
|
||||
# type: (List[Union[str, PkgResourcesRequirement]]) -> List[PkgResourcesRequirement]
|
||||
import pkg_resources
|
||||
if not isinstance(reqs, Iterable):
|
||||
raise TypeError("Expecting an Iterable, got %r" % reqs)
|
||||
@@ -82,24 +98,27 @@ def ensure_reqs(reqs):
|
||||
return new_reqs
|
||||
|
||||
|
||||
def _prepare_wheel_building_kwargs(ireq):
|
||||
download_dir = os.path.join(CACHE_DIR, "pkgs")
|
||||
def _prepare_wheel_building_kwargs(ireq=None, src_root=None, editable=False):
|
||||
# type: (Optional[InstallRequirement], Optional[str], bool) -> Dict[str, str]
|
||||
download_dir = os.path.join(CACHE_DIR, "pkgs") # type: str
|
||||
mkdir_p(download_dir)
|
||||
|
||||
wheel_download_dir = os.path.join(CACHE_DIR, "wheels")
|
||||
wheel_download_dir = os.path.join(CACHE_DIR, "wheels") # type: str
|
||||
mkdir_p(wheel_download_dir)
|
||||
|
||||
if ireq.source_dir is not None:
|
||||
if ireq is None:
|
||||
src_dir = _get_src_dir(root=src_root) # type: str
|
||||
elif ireq is not None and ireq.source_dir is not None:
|
||||
src_dir = ireq.source_dir
|
||||
elif ireq.editable:
|
||||
src_dir = _get_src_dir()
|
||||
elif ireq is not None and ireq.editable:
|
||||
src_dir = _get_src_dir(root=src_root)
|
||||
else:
|
||||
src_dir = create_tracked_tempdir(prefix="reqlib-src")
|
||||
|
||||
# This logic matches pip's behavior, although I don't fully understand the
|
||||
# intention. I guess the idea is to build editables in-place, otherwise out
|
||||
# of the source tree?
|
||||
if ireq.editable:
|
||||
if ireq is None and editable or (ireq is not None and ireq.editable):
|
||||
build_dir = src_dir
|
||||
else:
|
||||
build_dir = create_tracked_tempdir(prefix="reqlib-build")
|
||||
@@ -113,16 +132,25 @@ def _prepare_wheel_building_kwargs(ireq):
|
||||
|
||||
|
||||
def iter_egginfos(path, pkg_name=None):
|
||||
# type: (str, Optional[str]) -> Generator
|
||||
if pkg_name is not None:
|
||||
pkg_variants = get_name_variants(pkg_name)
|
||||
non_matching_dirs = []
|
||||
for entry in scandir(path):
|
||||
if entry.is_dir():
|
||||
if not entry.name.endswith("egg-info"):
|
||||
for dir_entry in iter_egginfos(entry.path, pkg_name=pkg_name):
|
||||
yield dir_entry
|
||||
elif pkg_name is None or entry.name.startswith(pkg_name.replace("-", "_")):
|
||||
yield entry
|
||||
entry_name, ext = os.path.splitext(entry.name)
|
||||
if ext.endswith("egg-info"):
|
||||
if pkg_name is None or entry_name in pkg_variants:
|
||||
yield entry
|
||||
elif not entry.name.endswith("egg-info"):
|
||||
non_matching_dirs.append(entry)
|
||||
for entry in non_matching_dirs:
|
||||
for dir_entry in iter_egginfos(entry.path, pkg_name=pkg_name):
|
||||
yield dir_entry
|
||||
|
||||
|
||||
def find_egginfo(target, pkg_name=None):
|
||||
# type: (str, Optional[str]) -> Generator
|
||||
egg_dirs = (egg_dir for egg_dir in iter_egginfos(target, pkg_name=pkg_name))
|
||||
if pkg_name:
|
||||
yield next(iter(egg_dirs), None)
|
||||
@@ -132,8 +160,6 @@ def find_egginfo(target, pkg_name=None):
|
||||
|
||||
|
||||
def get_metadata(path, pkg_name=None):
|
||||
if pkg_name:
|
||||
pkg_name = packaging.utils.canonicalize_name(pkg_name)
|
||||
egg_dir = next(iter(find_egginfo(path, pkg_name=pkg_name)), None)
|
||||
if egg_dir is not None:
|
||||
import pkg_resources
|
||||
@@ -148,7 +174,7 @@ def get_metadata(path, pkg_name=None):
|
||||
if dist:
|
||||
try:
|
||||
requires = dist.requires()
|
||||
except exception:
|
||||
except Exception:
|
||||
requires = []
|
||||
try:
|
||||
dep_map = dist._build_dep_map()
|
||||
@@ -199,8 +225,9 @@ class SetupInfo(object):
|
||||
ireq = attr.ib(default=None)
|
||||
extra_kwargs = attr.ib(default=attr.Factory(dict), type=dict)
|
||||
|
||||
def parse_setup_cfg(self):
|
||||
if self.setup_cfg is not None and self.setup_cfg.exists():
|
||||
@classmethod
|
||||
def get_setup_cfg(cls, setup_cfg_path):
|
||||
if os.path.exists(setup_cfg_path):
|
||||
default_opts = {
|
||||
"metadata": {"name": "", "version": ""},
|
||||
"options": {
|
||||
@@ -212,46 +239,54 @@ class SetupInfo(object):
|
||||
},
|
||||
}
|
||||
parser = configparser.ConfigParser(default_opts)
|
||||
parser.read(self.setup_cfg.as_posix())
|
||||
parser.read(setup_cfg_path)
|
||||
results = {}
|
||||
if parser.has_option("metadata", "name"):
|
||||
name = parser.get("metadata", "name")
|
||||
if not self.name and name is not None:
|
||||
self.name = name
|
||||
results["name"] = parser.get("metadata", "name")
|
||||
if parser.has_option("metadata", "version"):
|
||||
version = parser.get("metadata", "version")
|
||||
if not self.version and version is not None:
|
||||
self.version = version
|
||||
results["version"] = parser.get("metadata", "version")
|
||||
install_requires = {}
|
||||
if parser.has_option("options", "install_requires"):
|
||||
self.requires.update(
|
||||
{
|
||||
dep.strip(): init_requirement(dep.strip())
|
||||
for dep in parser.get("options", "install_requires").split("\n")
|
||||
if dep
|
||||
}
|
||||
)
|
||||
install_requires = {
|
||||
dep.strip(): init_requirement(dep.strip())
|
||||
for dep in parser.get("options", "install_requires").split("\n")
|
||||
if dep
|
||||
}
|
||||
results["install_requires"] = install_requires
|
||||
if parser.has_option("options", "python_requires"):
|
||||
python_requires = parser.get("options", "python_requires")
|
||||
if python_requires and not self.python_requires:
|
||||
self.python_requires = python_requires
|
||||
results["python_requires"] = parser.get("options", "python_requires")
|
||||
extras_require = {}
|
||||
if "options.extras_require" in parser.sections():
|
||||
self.extras.update(
|
||||
{
|
||||
section: [
|
||||
init_requirement(dep.strip())
|
||||
for dep in parser.get(
|
||||
"options.extras_require", section
|
||||
).split("\n")
|
||||
if dep
|
||||
]
|
||||
for section in parser.options("options.extras_require")
|
||||
if section not in ["options", "metadata"]
|
||||
}
|
||||
)
|
||||
if self.ireq.extras:
|
||||
self.requires.update({
|
||||
extra: self.extras[extra]
|
||||
for extra in self.ireq.extras if extra in self.extras
|
||||
})
|
||||
extras_require = {
|
||||
section: [
|
||||
init_requirement(dep.strip())
|
||||
for dep in parser.get(
|
||||
"options.extras_require", section
|
||||
).split("\n")
|
||||
if dep
|
||||
]
|
||||
for section in parser.options("options.extras_require")
|
||||
if section not in ["options", "metadata"]
|
||||
}
|
||||
results["extras_require"] = extras_require
|
||||
return results
|
||||
|
||||
def parse_setup_cfg(self):
|
||||
if self.setup_cfg is not None and self.setup_cfg.exists():
|
||||
parsed = self.get_setup_cfg(self.setup_cfg.as_posix())
|
||||
if self.name is None:
|
||||
self.name = parsed.get("name")
|
||||
if self.version is None:
|
||||
self.version = parsed.get("version")
|
||||
self.requires.update(parsed["install_requires"])
|
||||
if self.python_requires is None:
|
||||
self.python_requires = parsed.get("python_requires")
|
||||
self.extras.update(parsed["extras_require"])
|
||||
if self.ireq is not None and self.ireq.extras:
|
||||
self.requires.update({
|
||||
extra: self.extras[extra]
|
||||
for extra in self.ireq.extras if extra in self.extras
|
||||
})
|
||||
|
||||
def run_setup(self):
|
||||
if self.setup_py is not None and self.setup_py.exists():
|
||||
@@ -304,6 +339,9 @@ class SetupInfo(object):
|
||||
install_requires = dist.install_requires
|
||||
if install_requires and not self.requires:
|
||||
requirements = [init_requirement(req) for req in install_requires]
|
||||
if getattr(self.ireq, "extras", None):
|
||||
for extra in self.ireq.extras:
|
||||
requirements.extend(list(self.extras.get(extra, [])))
|
||||
self.requires.update({req.key: req for req in requirements})
|
||||
if dist.setup_requires and not self.setup_requires:
|
||||
self.setup_requires = dist.setup_requires
|
||||
@@ -314,7 +352,7 @@ class SetupInfo(object):
|
||||
if self.setup_py is not None and self.setup_py.exists():
|
||||
metadata = get_metadata(self.setup_py.parent.as_posix(), pkg_name=self.name)
|
||||
if metadata:
|
||||
if not self.name:
|
||||
if self.name is None:
|
||||
self.name = metadata.get("name", self.name)
|
||||
if not self.version:
|
||||
self.version = metadata.get("version", self.version)
|
||||
@@ -342,22 +380,35 @@ class SetupInfo(object):
|
||||
self.build_requires = requires
|
||||
|
||||
def get_info(self):
|
||||
initial_path = os.path.abspath(os.getcwd())
|
||||
if self.setup_cfg and self.setup_cfg.exists():
|
||||
self.parse_setup_cfg()
|
||||
try:
|
||||
self.parse_setup_cfg()
|
||||
finally:
|
||||
os.chdir(initial_path)
|
||||
if self.setup_py and self.setup_py.exists():
|
||||
if not self.requires or not self.name:
|
||||
try:
|
||||
self.run_setup()
|
||||
except Exception:
|
||||
self.get_egg_metadata()
|
||||
finally:
|
||||
os.chdir(initial_path)
|
||||
if not self.requires or not self.name:
|
||||
self.get_egg_metadata()
|
||||
try:
|
||||
self.get_egg_metadata()
|
||||
finally:
|
||||
os.chdir(initial_path)
|
||||
|
||||
if self.pyproject and self.pyproject.exists():
|
||||
self.run_pyproject()
|
||||
try:
|
||||
self.run_pyproject()
|
||||
finally:
|
||||
os.chdir(initial_path)
|
||||
return self.as_dict()
|
||||
|
||||
def as_dict(self):
|
||||
# type: () -> Dict[str, Any]
|
||||
prop_dict = {
|
||||
"name": self.name,
|
||||
"version": self.version,
|
||||
@@ -411,7 +462,12 @@ class SetupInfo(object):
|
||||
path = pip_shims.shims.url_to_path(unquote(ireq.link.url_without_fragment))
|
||||
if pip_shims.shims.is_installable_dir(path):
|
||||
ireq_src_dir = path
|
||||
if not ireq.editable or not (pip_shims.is_file_url(ireq.link) and ireq_src_dir):
|
||||
elif os.path.isdir(path):
|
||||
raise RequirementError(
|
||||
"The file URL points to a directory not installable: {}"
|
||||
.format(ireq.link)
|
||||
)
|
||||
if not ireq.editable or not ireq.link.scheme == "file":
|
||||
pip_shims.shims.unpack_url(
|
||||
ireq.link,
|
||||
ireq.source_dir,
|
||||
@@ -454,4 +510,6 @@ class SetupInfo(object):
|
||||
creation_kwargs["setup_cfg"] = setup_cfg
|
||||
if ireq:
|
||||
creation_kwargs["ireq"] = ireq
|
||||
return cls(**creation_kwargs)
|
||||
created = cls(**creation_kwargs)
|
||||
created.get_info()
|
||||
return created
|
||||
|
||||
+100
-15
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import io
|
||||
import os
|
||||
@@ -21,26 +21,45 @@ from vistir.misc import dedup
|
||||
|
||||
from ..utils import SCHEME_LIST, VCS_LIST, is_star, add_ssh_scheme_to_git_uri
|
||||
|
||||
from ..environment import MYPY_RUNNING
|
||||
|
||||
if MYPY_RUNNING:
|
||||
from typing import Union, Optional, List, Set, Any, TypeVar
|
||||
from attr import _ValidatorType
|
||||
from pkg_resources import Requirement as PkgResourcesRequirement
|
||||
from pip_shims import Link
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
HASH_STRING = " --hash={0}"
|
||||
|
||||
|
||||
def filter_none(k, v):
|
||||
# type: (str, Any) -> bool
|
||||
if v:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def optional_instance_of(cls):
|
||||
# type: (Any) -> _ValidatorType[Optional[_T]]
|
||||
return validators.optional(validators.instance_of(cls))
|
||||
|
||||
|
||||
def create_link(link):
|
||||
# type: (str) -> Link
|
||||
|
||||
if not isinstance(link, six.string_types):
|
||||
raise TypeError("must provide a string to instantiate a new link")
|
||||
from pip_shims import Link
|
||||
return Link(link)
|
||||
|
||||
|
||||
def init_requirement(name):
|
||||
# type: (str) -> PkgResourcesRequirement
|
||||
|
||||
if not isinstance(name, six.string_types):
|
||||
raise TypeError("must supply a name to generate a requirement")
|
||||
from pkg_resources import Requirement
|
||||
req = Requirement.parse(name)
|
||||
req.vcs = None
|
||||
@@ -62,14 +81,22 @@ def extras_to_string(extras):
|
||||
|
||||
|
||||
def parse_extras(extras_str):
|
||||
"""Turn a string of extras into a parsed extras list"""
|
||||
# type: (str) -> List
|
||||
"""
|
||||
Turn a string of extras into a parsed extras list
|
||||
"""
|
||||
|
||||
from pkg_resources import Requirement
|
||||
extras = Requirement.parse("fakepkg{0}".format(extras_to_string(extras_str))).extras
|
||||
return sorted(dedup([extra.lower() for extra in extras]))
|
||||
|
||||
|
||||
def specs_to_string(specs):
|
||||
"""Turn a list of specifier tuples into a string"""
|
||||
# type: (List[str, Specifier]) -> str
|
||||
"""
|
||||
Turn a list of specifier tuples into a string
|
||||
"""
|
||||
|
||||
if specs:
|
||||
if isinstance(specs, six.string_types):
|
||||
return specs
|
||||
@@ -81,13 +108,20 @@ def specs_to_string(specs):
|
||||
return ""
|
||||
|
||||
|
||||
def build_vcs_link(vcs, uri, name=None, ref=None, subdirectory=None, extras=None):
|
||||
def build_vcs_uri(
|
||||
vcs, # type: str
|
||||
uri, # type: str
|
||||
name=None, # type: Optional[str]
|
||||
ref=None, # type: Optional[str]
|
||||
subdirectory=None, # type: Optional[str]
|
||||
extras=None # type: Optional[List[str]]
|
||||
):
|
||||
# type: (...) -> str
|
||||
if extras is None:
|
||||
extras = []
|
||||
vcs_start = "{0}+".format(vcs)
|
||||
if not uri.startswith(vcs_start):
|
||||
uri = "{0}{1}".format(vcs_start, uri)
|
||||
uri = add_ssh_scheme_to_git_uri(uri)
|
||||
if ref:
|
||||
uri = "{0}@{1}".format(uri, ref)
|
||||
if name:
|
||||
@@ -97,7 +131,7 @@ def build_vcs_link(vcs, uri, name=None, ref=None, subdirectory=None, extras=None
|
||||
uri = "{0}{1}".format(uri, extras)
|
||||
if subdirectory:
|
||||
uri = "{0}&subdirectory={1}".format(uri, subdirectory)
|
||||
return create_link(uri)
|
||||
return uri
|
||||
|
||||
|
||||
def get_version(pipfile_entry):
|
||||
@@ -115,6 +149,15 @@ def get_version(pipfile_entry):
|
||||
|
||||
|
||||
def get_pyproject(path):
|
||||
"""
|
||||
Given a base path, look for the corresponding ``pyproject.toml`` file and return its
|
||||
build_requires and build_backend.
|
||||
|
||||
:param str path: The root path of the project, should be a directory (will be truncated)
|
||||
:return: A 2 tuple of build requirements and the build backend
|
||||
:rtype: Tuple[List[str], str]
|
||||
"""
|
||||
|
||||
from vistir.compat import Path
|
||||
if not path:
|
||||
return
|
||||
@@ -146,7 +189,7 @@ def get_pyproject(path):
|
||||
pyproject_data["build_system"] = build_system
|
||||
else:
|
||||
requires = build_system.get("requires")
|
||||
backend = build_system.get("build-backend")
|
||||
backend = build_system.get("build-backend", "setuptools.build_meta")
|
||||
return (requires, backend)
|
||||
|
||||
|
||||
@@ -232,6 +275,7 @@ def _requirement_to_str_lowercase_name(requirement):
|
||||
important stuff that should not be lowercased (such as the marker). See
|
||||
this issue for more information: https://github.com/pypa/pipenv/issues/2113.
|
||||
"""
|
||||
|
||||
parts = [requirement.name.lower()]
|
||||
|
||||
if requirement.extras:
|
||||
@@ -254,6 +298,7 @@ def format_requirement(ireq):
|
||||
Generic formatter for pretty printing InstallRequirements to the terminal
|
||||
in a less verbose way than using its `__str__` method.
|
||||
"""
|
||||
|
||||
if ireq.editable:
|
||||
line = '-e {}'.format(ireq.link)
|
||||
else:
|
||||
@@ -282,7 +327,8 @@ def format_specifier(ireq):
|
||||
|
||||
|
||||
def get_pinned_version(ireq):
|
||||
"""Get the pinned version of an InstallRequirement.
|
||||
"""
|
||||
Get the pinned version of an InstallRequirement.
|
||||
|
||||
An InstallRequirement is considered pinned if:
|
||||
|
||||
@@ -300,6 +346,7 @@ def get_pinned_version(ireq):
|
||||
Raises `TypeError` if the input is not a valid InstallRequirement, or
|
||||
`ValueError` if the InstallRequirement is not pinned.
|
||||
"""
|
||||
|
||||
try:
|
||||
specifier = ireq.specifier
|
||||
except AttributeError:
|
||||
@@ -324,7 +371,8 @@ def get_pinned_version(ireq):
|
||||
|
||||
|
||||
def is_pinned_requirement(ireq):
|
||||
"""Returns whether an InstallRequirement is a "pinned" requirement.
|
||||
"""
|
||||
Returns whether an InstallRequirement is a "pinned" requirement.
|
||||
|
||||
An InstallRequirement is considered pinned if:
|
||||
|
||||
@@ -339,6 +387,7 @@ def is_pinned_requirement(ireq):
|
||||
django~=1.8 # NOT pinned
|
||||
django==1.* # NOT pinned
|
||||
"""
|
||||
|
||||
try:
|
||||
get_pinned_version(ireq)
|
||||
except (TypeError, ValueError):
|
||||
@@ -350,6 +399,7 @@ def as_tuple(ireq):
|
||||
"""
|
||||
Pulls out the (name: str, version:str, extras:(str)) tuple from the pinned InstallRequirement.
|
||||
"""
|
||||
|
||||
if not is_pinned_requirement(ireq):
|
||||
raise TypeError('Expected a pinned InstallRequirement, got {}'.format(ireq))
|
||||
|
||||
@@ -360,12 +410,18 @@ def as_tuple(ireq):
|
||||
|
||||
|
||||
def full_groupby(iterable, key=None):
|
||||
"""Like groupby(), but sorts the input on the group key first."""
|
||||
"""
|
||||
Like groupby(), but sorts the input on the group key first.
|
||||
"""
|
||||
|
||||
return groupby(sorted(iterable, key=key), key=key)
|
||||
|
||||
|
||||
def flat_map(fn, collection):
|
||||
"""Map a function over a collection and flatten the result by one-level"""
|
||||
"""
|
||||
Map a function over a collection and flatten the result by one-level
|
||||
"""
|
||||
|
||||
return chain.from_iterable(map(fn, collection))
|
||||
|
||||
|
||||
@@ -385,8 +441,7 @@ def lookup_table(values, key=None, keyval=None, unique=False, use_lists=False):
|
||||
For key functions that uniquely identify values, set unique=True:
|
||||
|
||||
>>> assert lookup_table(
|
||||
... ['foo', 'bar', 'baz', 'qux', 'quux'], lambda s: s[0],
|
||||
... unique=True) == {
|
||||
... ['foo', 'bar', 'baz', 'qux', 'quux'], lambda s: s[0], unique=True) == {
|
||||
... 'b': 'baz',
|
||||
... 'f': 'foo',
|
||||
... 'q': 'quux'
|
||||
@@ -404,8 +459,8 @@ def lookup_table(values, key=None, keyval=None, unique=False, use_lists=False):
|
||||
... 'f': {'oo'},
|
||||
... 'q': {'uux', 'ux'}
|
||||
... }
|
||||
|
||||
"""
|
||||
|
||||
if keyval is None:
|
||||
if key is None:
|
||||
keyval = (lambda v: v)
|
||||
@@ -443,7 +498,8 @@ def name_from_req(req):
|
||||
|
||||
|
||||
def make_install_requirement(name, version, extras, markers, constraint=False):
|
||||
"""make_install_requirement Generates an :class:`~pip._internal.req.req_install.InstallRequirement`.
|
||||
"""
|
||||
Generates an :class:`~pip._internal.req.req_install.InstallRequirement`.
|
||||
|
||||
Create an InstallRequirement from the supplied metadata.
|
||||
|
||||
@@ -539,6 +595,7 @@ def fix_requires_python_marker(requires_python):
|
||||
|
||||
|
||||
def normalize_name(pkg):
|
||||
# type: (str) -> str
|
||||
"""Given a package name, return its normalized, non-canonicalized form.
|
||||
|
||||
:param str pkg: The name of a package
|
||||
@@ -548,3 +605,31 @@ def normalize_name(pkg):
|
||||
|
||||
assert isinstance(pkg, six.string_types)
|
||||
return pkg.replace("_", "-").lower()
|
||||
|
||||
|
||||
def get_name_variants(pkg):
|
||||
# type: (str) -> Set[str]
|
||||
"""
|
||||
Given a packager name, get the variants of its name for both the canonicalized
|
||||
and "safe" forms.
|
||||
|
||||
:param str pkg: The package to lookup
|
||||
:returns: A list of names.
|
||||
:rtype: Set
|
||||
"""
|
||||
|
||||
if not isinstance(pkg, six.string_types):
|
||||
raise TypeError("must provide a string to derive package names")
|
||||
from pkg_resources import safe_name
|
||||
from packaging.utils import canonicalize_name
|
||||
names = {safe_name(pkg), canonicalize_name(pkg)}
|
||||
return names
|
||||
|
||||
|
||||
SETUPTOOLS_SHIM = (
|
||||
"import setuptools, tokenize;__file__=%r;"
|
||||
"f=getattr(tokenize, 'open', open)(__file__);"
|
||||
"code=f.read().replace('\\r\\n', '\\n');"
|
||||
"f.close();"
|
||||
"exec(compile(code, __file__, 'exec'))"
|
||||
)
|
||||
|
||||
+33
-2
@@ -1,11 +1,18 @@
|
||||
# -*- coding=utf-8 -*-
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import attr
|
||||
import importlib
|
||||
import os
|
||||
import pip_shims
|
||||
import six
|
||||
import sys
|
||||
|
||||
|
||||
@attr.s
|
||||
class VCSRepository(object):
|
||||
DEFAULT_RUN_ARGS = None
|
||||
|
||||
url = attr.ib()
|
||||
name = attr.ib()
|
||||
checkout_directory = attr.ib()
|
||||
@@ -14,13 +21,21 @@ class VCSRepository(object):
|
||||
commit_sha = attr.ib(default=None)
|
||||
ref = attr.ib(default=None)
|
||||
repo_instance = attr.ib()
|
||||
clone_log = attr.ib(default=None)
|
||||
|
||||
@repo_instance.default
|
||||
def get_repo_instance(self):
|
||||
from pip_shims import VcsSupport
|
||||
if self.DEFAULT_RUN_ARGS is None:
|
||||
default_run_args = self.monkeypatch_pip()
|
||||
else:
|
||||
default_run_args = self.DEFAULT_RUN_ARGS
|
||||
from pip_shims.shims import VcsSupport
|
||||
VCS_SUPPORT = VcsSupport()
|
||||
backend = VCS_SUPPORT._registry.get(self.vcs_type)
|
||||
return backend(url=self.url)
|
||||
repo = backend(url=self.url)
|
||||
if repo.run_command.__func__.__defaults__ != default_run_args:
|
||||
repo.run_command.__func__.__defaults__ = default_run_args
|
||||
return repo
|
||||
|
||||
@property
|
||||
def is_local(self):
|
||||
@@ -58,3 +73,19 @@ class VCSRepository(object):
|
||||
|
||||
def get_commit_hash(self, ref=None):
|
||||
return self.repo_instance.get_revision(self.checkout_directory)
|
||||
|
||||
@classmethod
|
||||
def monkeypatch_pip(cls):
|
||||
target_module = pip_shims.shims.VcsSupport.__module__
|
||||
pip_vcs = importlib.import_module(target_module)
|
||||
run_command_defaults = pip_vcs.VersionControl.run_command.__defaults__
|
||||
# set the default to not write stdout, the first option sets this value
|
||||
new_defaults = [False,] + list(run_command_defaults)[1:]
|
||||
new_defaults = tuple(new_defaults)
|
||||
if six.PY3:
|
||||
pip_vcs.VersionControl.run_command.__defaults__ = new_defaults
|
||||
else:
|
||||
pip_vcs.VersionControl.run_command.__func__.__defaults__ = new_defaults
|
||||
sys.modules[target_module] = pip_vcs
|
||||
cls.DEFAULT_RUN_ARGS = new_defaults
|
||||
return new_defaults
|
||||
|
||||
+92
-17
@@ -1,5 +1,5 @@
|
||||
# -*- coding=utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import contextlib
|
||||
import logging
|
||||
@@ -8,13 +8,14 @@ import os
|
||||
import six
|
||||
import sys
|
||||
import tomlkit
|
||||
import vistir
|
||||
|
||||
six.add_move(six.MovedAttribute("Mapping", "collections", "collections.abc"))
|
||||
six.add_move(six.MovedAttribute("Sequence", "collections", "collections.abc"))
|
||||
six.add_move(six.MovedAttribute("Set", "collections", "collections.abc"))
|
||||
six.add_move(six.MovedAttribute("ItemsView", "collections", "collections.abc"))
|
||||
from six.moves import Mapping, Sequence, Set, ItemsView
|
||||
from six.moves.urllib.parse import urlparse, urlsplit
|
||||
from six.moves.urllib.parse import urlparse, urlsplit, urlunparse
|
||||
|
||||
import pip_shims.shims
|
||||
from vistir.compat import Path
|
||||
@@ -81,18 +82,34 @@ def is_installable_dir(path):
|
||||
|
||||
|
||||
def strip_ssh_from_git_uri(uri):
|
||||
# type: (str) -> str
|
||||
"""Return git+ssh:// formatted URI to git+git@ format"""
|
||||
if isinstance(uri, six.string_types):
|
||||
uri = uri.replace("git+ssh://", "git+", 1)
|
||||
if "git+ssh://" in uri:
|
||||
parsed = urlparse(uri)
|
||||
# split the path on the first separating / so we can put the first segment
|
||||
# into the 'netloc' section with a : separator
|
||||
path_part, _, path = parsed.path.lstrip("/").partition("/")
|
||||
path = "/{0}".format(path)
|
||||
parsed = parsed._replace(
|
||||
netloc="{0}:{1}".format(parsed.netloc, path_part), path=path
|
||||
)
|
||||
uri = urlunparse(parsed).replace("git+ssh://", "git+", 1)
|
||||
return uri
|
||||
|
||||
|
||||
def add_ssh_scheme_to_git_uri(uri):
|
||||
# type: (str) -> str
|
||||
"""Cleans VCS uris from pipenv.patched.notpip format"""
|
||||
if isinstance(uri, six.string_types):
|
||||
# Add scheme for parsing purposes, this is also what pip does
|
||||
if uri.startswith("git+") and "://" not in uri:
|
||||
uri = uri.replace("git+", "git+ssh://", 1)
|
||||
parsed = urlparse(uri)
|
||||
if ":" in parsed.netloc:
|
||||
netloc, _, path_start = parsed.netloc.rpartition(":")
|
||||
path = "/{0}{1}".format(path_start, parsed.path)
|
||||
uri = urlunparse(parsed._replace(netloc=netloc, path=path))
|
||||
return uri
|
||||
|
||||
|
||||
@@ -113,6 +130,8 @@ def is_vcs(pipfile_entry):
|
||||
def is_editable(pipfile_entry):
|
||||
if isinstance(pipfile_entry, Mapping):
|
||||
return pipfile_entry.get("editable", False) is True
|
||||
if isinstance(pipfile_entry, six.string_types):
|
||||
return pipfile_entry.startswith("-e ")
|
||||
return False
|
||||
|
||||
|
||||
@@ -129,16 +148,30 @@ def is_star(val):
|
||||
)
|
||||
|
||||
|
||||
def convert_entry_to_path(path):
|
||||
# type: (Dict[str, Any]) -> str
|
||||
"""Convert a pipfile entry to a string"""
|
||||
|
||||
if not isinstance(path, Mapping):
|
||||
raise TypeError("expecting a mapping, received {0!r}".format(path))
|
||||
|
||||
if not any(key in path for key in ["file", "path"]):
|
||||
raise ValueError("missing path-like entry in supplied mapping {0!r}".format(path))
|
||||
|
||||
if "file" in path:
|
||||
path = vistir.path.url_to_path(path["file"])
|
||||
|
||||
elif "path" in path:
|
||||
path = path["path"]
|
||||
return path
|
||||
|
||||
|
||||
def is_installable_file(path):
|
||||
"""Determine if a path can potentially be installed"""
|
||||
from packaging import specifiers
|
||||
|
||||
if hasattr(path, "keys") and any(
|
||||
key for key in path.keys() if key in ["file", "path"]
|
||||
):
|
||||
path = urlparse(path["file"]).path if "file" in path else path["path"]
|
||||
if not isinstance(path, six.string_types) or path == "*":
|
||||
return False
|
||||
if isinstance(path, Mapping):
|
||||
path = convert_entry_to_path(path)
|
||||
|
||||
# If the string starts with a valid specifier operator, test if it is a valid
|
||||
# specifier set before making a path object (to avoid breaking windows)
|
||||
@@ -152,23 +185,65 @@ def is_installable_file(path):
|
||||
return False
|
||||
|
||||
parsed = urlparse(path)
|
||||
if parsed.scheme == "file":
|
||||
path = parsed.path
|
||||
|
||||
if not os.path.exists(os.path.abspath(path)):
|
||||
is_local = (not parsed.scheme or parsed.scheme == "file" or (len(parsed.scheme) == 1 and os.name == "nt"))
|
||||
if parsed.scheme and parsed.scheme == "file":
|
||||
path = vistir.path.url_to_path(path)
|
||||
normalized_path = vistir.path.normalize_path(path)
|
||||
if is_local and not os.path.exists(normalized_path):
|
||||
return False
|
||||
|
||||
lookup_path = Path(path)
|
||||
absolute_path = "{0}".format(lookup_path.absolute())
|
||||
if lookup_path.is_dir() and is_installable_dir(absolute_path):
|
||||
is_archive = pip_shims.shims.is_archive_file(normalized_path)
|
||||
is_local_project = os.path.isdir(normalized_path) and is_installable_dir(normalized_path)
|
||||
if is_local and is_local_project or is_archive:
|
||||
return True
|
||||
|
||||
elif lookup_path.is_file() and pip_shims.shims.is_archive_file(absolute_path):
|
||||
if not is_local and pip_shims.shims.is_archive_file(parsed.path):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def get_dist_metadata(dist):
|
||||
import pkg_resources
|
||||
from email.parser import FeedParser
|
||||
if (isinstance(dist, pkg_resources.DistInfoDistribution) and
|
||||
dist.has_metadata('METADATA')):
|
||||
metadata = dist.get_metadata('METADATA')
|
||||
elif dist.has_metadata('PKG-INFO'):
|
||||
metadata = dist.get_metadata('PKG-INFO')
|
||||
else:
|
||||
metadata = ""
|
||||
|
||||
feed_parser = FeedParser()
|
||||
feed_parser.feed(metadata)
|
||||
return feed_parser.close()
|
||||
|
||||
|
||||
def get_setup_paths(base_path, subdirectory=None):
|
||||
# type: (str, Optional[str]) -> Dict[str, Optional[str]]
|
||||
if base_path is None:
|
||||
raise TypeError("must provide a path to derive setup paths from")
|
||||
setup_py = os.path.join(base_path, "setup.py")
|
||||
setup_cfg = os.path.join(base_path, "setup.cfg")
|
||||
pyproject_toml = os.path.join(base_path, "pyproject.toml")
|
||||
if subdirectory is not None:
|
||||
base_path = os.path.join(base_path, subdirectory)
|
||||
subdir_setup_py = os.path.join(subdirectory, "setup.py")
|
||||
subdir_setup_cfg = os.path.join(subdirectory, "setup.cfg")
|
||||
subdir_pyproject_toml = os.path.join(subdirectory, "pyproject.toml")
|
||||
if subdirectory and os.path.exists(subdir_setup_py):
|
||||
setup_py = subdir_setup_py
|
||||
if subdirectory and os.path.exists(subdir_setup_cfg):
|
||||
setup_cfg = subdir_setup_cfg
|
||||
if subdirectory and os.path.exists(subdir_pyproject_toml):
|
||||
pyproject_toml = subdir_pyproject_toml
|
||||
return {
|
||||
"setup_py": setup_py if os.path.exists(setup_py) else None,
|
||||
"setup_cfg": setup_cfg if os.path.exists(setup_cfg) else None,
|
||||
"pyproject_toml": pyproject_toml if os.path.exists(pyproject_toml) else None
|
||||
}
|
||||
|
||||
|
||||
def prepare_pip_source_args(sources, pip_args=None):
|
||||
if pip_args is None:
|
||||
pip_args = []
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import os
|
||||
from ._core import ShellDetectionFailure
|
||||
|
||||
|
||||
__version__ = '1.2.7'
|
||||
__version__ = '1.2.8'
|
||||
|
||||
|
||||
def detect_shell(pid=None, max_depth=6):
|
||||
|
||||
Vendored
+12
-1
@@ -75,7 +75,18 @@ def _iter_process():
|
||||
# looking for. We can fix this when it actually matters. (#8)
|
||||
continue
|
||||
raise WinError()
|
||||
info = {'executable': str(pe.szExeFile.decode('utf-8'))}
|
||||
|
||||
# The executable name would be encoded with the current code page if
|
||||
# we're in ANSI mode (usually). Try to decode it into str/unicode,
|
||||
# replacing invalid characters to be safe (not thoeratically necessary,
|
||||
# I think). Note that we need to use 'mbcs' instead of encoding
|
||||
# settings from sys because this is from the Windows API, not Python
|
||||
# internals (which those settings reflect). (pypa/pipenv#3382)
|
||||
executable = pe.szExeFile
|
||||
if isinstance(executable, bytes):
|
||||
executable = executable.decode('mbcs', 'replace')
|
||||
|
||||
info = {'executable': executable}
|
||||
if pe.th32ParentProcessID:
|
||||
info['parent_pid'] = pe.th32ParentProcessID
|
||||
yield pe.th32ProcessID, info
|
||||
|
||||
Vendored
+1
-1
@@ -21,7 +21,7 @@ def _get_process_mapping():
|
||||
processes = {}
|
||||
for line in output.split('\n'):
|
||||
try:
|
||||
pid, ppid, args = line.strip().split(None, 2)
|
||||
pid, ppid, args = line.strip().split(maxsplit=2)
|
||||
except ValueError:
|
||||
continue
|
||||
processes[pid] = Process(
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
import collections
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
Process = collections.namedtuple('Process', 'args pid ppid')
|
||||
|
||||
|
||||
def get_process_mapping():
|
||||
"""Try to look up the process tree via the output of `ps`.
|
||||
"""
|
||||
output = subprocess.check_output([
|
||||
'ps', '-ww', '-o', 'pid=', '-o', 'ppid=', '-o', 'args=',
|
||||
])
|
||||
if not isinstance(output, str):
|
||||
output = output.decode(sys.stdout.encoding)
|
||||
processes = {}
|
||||
for line in output.split('\n'):
|
||||
try:
|
||||
pid, ppid, args = line.strip().split(None, 2)
|
||||
except ValueError:
|
||||
continue
|
||||
processes[pid] = Process(
|
||||
args=tuple(shlex.split(args)), pid=pid, ppid=ppid,
|
||||
)
|
||||
return processes
|
||||
+20
-14
@@ -1,34 +1,40 @@
|
||||
import os
|
||||
import re
|
||||
|
||||
from ._default import Process
|
||||
from ._core import Process
|
||||
|
||||
|
||||
STAT_PPID = 3
|
||||
STAT_TTY = 6
|
||||
|
||||
STAT_PATTERN = re.compile(r'\(.+\)|\S+')
|
||||
|
||||
|
||||
def _get_stat(pid):
|
||||
with open(os.path.join('/proc', str(pid), 'stat')) as f:
|
||||
parts = STAT_PATTERN.findall(f.read())
|
||||
return parts[STAT_TTY], parts[STAT_PPID]
|
||||
|
||||
|
||||
def _get_cmdline(pid):
|
||||
with open(os.path.join('/proc', str(pid), 'cmdline')) as f:
|
||||
return tuple(f.read().split('\0')[:-1])
|
||||
|
||||
|
||||
def get_process_mapping():
|
||||
"""Try to look up the process tree via the /proc interface.
|
||||
"""
|
||||
with open('/proc/{0}/stat'.format(os.getpid())) as f:
|
||||
self_tty = f.read().split()[STAT_TTY]
|
||||
self_tty = _get_stat(os.getpid())[0]
|
||||
processes = {}
|
||||
for pid in os.listdir('/proc'):
|
||||
if not pid.isdigit():
|
||||
continue
|
||||
try:
|
||||
stat = '/proc/{0}/stat'.format(pid)
|
||||
cmdline = '/proc/{0}/cmdline'.format(pid)
|
||||
with open(stat) as fstat, open(cmdline) as fcmdline:
|
||||
stat = re.findall(r'\(.+\)|\S+', fstat.read())
|
||||
cmd = fcmdline.read().split('\x00')[:-1]
|
||||
ppid = stat[STAT_PPID]
|
||||
tty = stat[STAT_TTY]
|
||||
if tty == self_tty:
|
||||
processes[pid] = Process(
|
||||
args=tuple(cmd), pid=pid, ppid=ppid,
|
||||
)
|
||||
tty, ppid = _get_stat(pid)
|
||||
if tty != self_tty:
|
||||
continue
|
||||
args = _get_cmdline(pid)
|
||||
processes[pid] = Process(args=args, pid=pid, ppid=ppid)
|
||||
except IOError:
|
||||
# Process has disappeared - just ignore it.
|
||||
continue
|
||||
|
||||
+1
-3
@@ -1,10 +1,8 @@
|
||||
import collections
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
Process = collections.namedtuple('Process', 'args pid ppid')
|
||||
from ._core import Process
|
||||
|
||||
|
||||
def get_process_mapping():
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
import os
|
||||
import re
|
||||
|
||||
from ._default import Process
|
||||
|
||||
|
||||
STAT_PPID = 3
|
||||
STAT_TTY = 6
|
||||
|
||||
|
||||
def get_process_mapping():
|
||||
"""Try to look up the process tree via Linux's /proc
|
||||
"""
|
||||
with open('/proc/{0}/stat'.format(os.getpid())) as f:
|
||||
self_tty = f.read().split()[STAT_TTY]
|
||||
processes = {}
|
||||
for pid in os.listdir('/proc'):
|
||||
if not pid.isdigit():
|
||||
continue
|
||||
try:
|
||||
stat = '/proc/{0}/stat'.format(pid)
|
||||
cmdline = '/proc/{0}/cmdline'.format(pid)
|
||||
with open(stat) as fstat, open(cmdline) as fcmdline:
|
||||
stat = re.findall(r'\(.+\)|\S+', fstat.read())
|
||||
cmd = fcmdline.read().split('\x00')[:-1]
|
||||
ppid = stat[STAT_PPID]
|
||||
tty = stat[STAT_TTY]
|
||||
if tty == self_tty:
|
||||
processes[pid] = Process(
|
||||
args=tuple(cmd), pid=pid, ppid=ppid,
|
||||
)
|
||||
except IOError:
|
||||
# Process has disappeared - just ignore it.
|
||||
continue
|
||||
return processes
|
||||
+7
-4
@@ -1,5 +1,4 @@
|
||||
import errno
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
@@ -34,9 +33,13 @@ def get_process_mapping():
|
||||
for line in output.split('\n'):
|
||||
try:
|
||||
pid, ppid, args = line.strip().split(None, 2)
|
||||
processes[pid] = Process(
|
||||
args=tuple(shlex.split(args)), pid=pid, ppid=ppid,
|
||||
)
|
||||
# XXX: This is not right, but we are really out of options.
|
||||
# ps does not offer a sane way to decode the argument display,
|
||||
# and this is "Good Enough" for obtaining shell names. Hopefully
|
||||
# people don't name their shell with a space, or have something
|
||||
# like "/usr/bin/xonsh is uber". (sarugaku/shellingham#14)
|
||||
args = tuple(a.strip() for a in args.split(' '))
|
||||
except ValueError:
|
||||
continue
|
||||
processes[pid] = Process(args=args, pid=pid, ppid=ppid)
|
||||
return processes
|
||||
|
||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2010-2017 Benjamin Peterson
|
||||
Copyright (c) 2010-2018 Benjamin Peterson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
|
||||
Vendored
+63
-2
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2010-2017 Benjamin Peterson
|
||||
# Copyright (c) 2010-2018 Benjamin Peterson
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -29,7 +29,7 @@ import sys
|
||||
import types
|
||||
|
||||
__author__ = "Benjamin Peterson <benjamin@python.org>"
|
||||
__version__ = "1.11.0"
|
||||
__version__ = "1.12.0"
|
||||
|
||||
|
||||
# Useful for very coarse version differentiation.
|
||||
@@ -844,10 +844,71 @@ def add_metaclass(metaclass):
|
||||
orig_vars.pop(slots_var)
|
||||
orig_vars.pop('__dict__', None)
|
||||
orig_vars.pop('__weakref__', None)
|
||||
if hasattr(cls, '__qualname__'):
|
||||
orig_vars['__qualname__'] = cls.__qualname__
|
||||
return metaclass(cls.__name__, cls.__bases__, orig_vars)
|
||||
return wrapper
|
||||
|
||||
|
||||
def ensure_binary(s, encoding='utf-8', errors='strict'):
|
||||
"""Coerce **s** to six.binary_type.
|
||||
|
||||
For Python 2:
|
||||
- `unicode` -> encoded to `str`
|
||||
- `str` -> `str`
|
||||
|
||||
For Python 3:
|
||||
- `str` -> encoded to `bytes`
|
||||
- `bytes` -> `bytes`
|
||||
"""
|
||||
if isinstance(s, text_type):
|
||||
return s.encode(encoding, errors)
|
||||
elif isinstance(s, binary_type):
|
||||
return s
|
||||
else:
|
||||
raise TypeError("not expecting type '%s'" % type(s))
|
||||
|
||||
|
||||
def ensure_str(s, encoding='utf-8', errors='strict'):
|
||||
"""Coerce *s* to `str`.
|
||||
|
||||
For Python 2:
|
||||
- `unicode` -> encoded to `str`
|
||||
- `str` -> `str`
|
||||
|
||||
For Python 3:
|
||||
- `str` -> `str`
|
||||
- `bytes` -> decoded to `str`
|
||||
"""
|
||||
if not isinstance(s, (text_type, binary_type)):
|
||||
raise TypeError("not expecting type '%s'" % type(s))
|
||||
if PY2 and isinstance(s, text_type):
|
||||
s = s.encode(encoding, errors)
|
||||
elif PY3 and isinstance(s, binary_type):
|
||||
s = s.decode(encoding, errors)
|
||||
return s
|
||||
|
||||
|
||||
def ensure_text(s, encoding='utf-8', errors='strict'):
|
||||
"""Coerce *s* to six.text_type.
|
||||
|
||||
For Python 2:
|
||||
- `unicode` -> `unicode`
|
||||
- `str` -> `unicode`
|
||||
|
||||
For Python 3:
|
||||
- `str` -> `str`
|
||||
- `bytes` -> decoded to `str`
|
||||
"""
|
||||
if isinstance(s, binary_type):
|
||||
return s.decode(encoding, errors)
|
||||
elif isinstance(s, text_type):
|
||||
return s
|
||||
else:
|
||||
raise TypeError("not expecting type '%s'" % type(s))
|
||||
|
||||
|
||||
|
||||
def python_2_unicode_compatible(klass):
|
||||
"""
|
||||
A decorator that defines __unicode__ and __str__ methods under Python 2.
|
||||
|
||||
Vendored
+1
-1
@@ -22,4 +22,4 @@ from .api import value
|
||||
from .api import ws
|
||||
|
||||
|
||||
__version__ = "0.5.2"
|
||||
__version__ = "0.5.3"
|
||||
|
||||
Vendored
+15
@@ -1,5 +1,7 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import copy
|
||||
|
||||
from ._compat import decode
|
||||
from .exceptions import KeyAlreadyPresent
|
||||
from .exceptions import NonExistentKey
|
||||
@@ -600,3 +602,16 @@ class Container(dict):
|
||||
self._map = state[0]
|
||||
self._body = state[1]
|
||||
self._parsed = state[2]
|
||||
|
||||
def copy(self): # type: () -> Container
|
||||
return copy.copy(self)
|
||||
|
||||
def __copy__(self): # type: () -> Container
|
||||
c = self.__class__(self._parsed)
|
||||
for k, v in super(Container, self).copy().items():
|
||||
super(Container, c).__setitem__(k, v)
|
||||
|
||||
c._body += self.body
|
||||
c._map.update(self._map)
|
||||
|
||||
return c
|
||||
|
||||
Vendored
+4
-1
@@ -527,7 +527,10 @@ class DateTime(Item, datetime):
|
||||
def __sub__(self, other):
|
||||
result = super(DateTime, self).__sub__(other)
|
||||
|
||||
return self._new(result)
|
||||
if isinstance(result, datetime):
|
||||
result = self._new(result)
|
||||
|
||||
return result
|
||||
|
||||
def _new(self, result):
|
||||
raw = result.isoformat()
|
||||
|
||||
Vendored
-4
@@ -45,10 +45,6 @@ class _State:
|
||||
if self._save_marker:
|
||||
self._source._marker = self._marker
|
||||
|
||||
# Restore exceptions are silently consumed, other exceptions need to
|
||||
# propagate
|
||||
return exception_type is None
|
||||
|
||||
|
||||
class _StateHandler:
|
||||
"""
|
||||
|
||||
Vendored
+1
-1
@@ -27,7 +27,7 @@ from logging import NullHandler
|
||||
|
||||
__author__ = 'Andrey Petrov (andrey.petrov@shazow.net)'
|
||||
__license__ = 'MIT'
|
||||
__version__ = '1.24'
|
||||
__version__ = '1.24.1'
|
||||
|
||||
__all__ = (
|
||||
'HTTPConnectionPool',
|
||||
|
||||
Vendored
+4
-4
@@ -69,9 +69,9 @@ class GzipDecoder(object):
|
||||
return getattr(self._obj, name)
|
||||
|
||||
def decompress(self, data):
|
||||
ret = b''
|
||||
ret = bytearray()
|
||||
if self._state == GzipDecoderState.SWALLOW_DATA or not data:
|
||||
return ret
|
||||
return bytes(ret)
|
||||
while True:
|
||||
try:
|
||||
ret += self._obj.decompress(data)
|
||||
@@ -81,11 +81,11 @@ class GzipDecoder(object):
|
||||
self._state = GzipDecoderState.SWALLOW_DATA
|
||||
if previous_state == GzipDecoderState.OTHER_MEMBERS:
|
||||
# Allow trailing garbage acceptable in other gzip clients
|
||||
return ret
|
||||
return bytes(ret)
|
||||
raise
|
||||
data = self._obj.unused_data
|
||||
if not data:
|
||||
return ret
|
||||
return bytes(ret)
|
||||
self._state = GzipDecoderState.OTHER_MEMBERS
|
||||
self._obj = zlib.decompressobj(16 + zlib.MAX_WBITS)
|
||||
|
||||
|
||||
Vendored
+2
@@ -263,6 +263,8 @@ def create_urllib3_context(ssl_version=None, cert_reqs=None,
|
||||
"""
|
||||
context = SSLContext(ssl_version or ssl.PROTOCOL_SSLv23)
|
||||
|
||||
context.set_ciphers(ciphers or DEFAULT_CIPHERS)
|
||||
|
||||
# Setting the default here, as we may have no ssl module on import
|
||||
cert_reqs = ssl.CERT_REQUIRED if cert_reqs is None else cert_reqs
|
||||
|
||||
|
||||
Vendored
+10
-3
@@ -6,6 +6,7 @@ from .compat import (
|
||||
TemporaryDirectory,
|
||||
partialmethod,
|
||||
to_native_string,
|
||||
StringIO,
|
||||
)
|
||||
from .contextmanagers import (
|
||||
atomic_open_for_write,
|
||||
@@ -14,6 +15,7 @@ from .contextmanagers import (
|
||||
temp_environ,
|
||||
temp_path,
|
||||
spinner,
|
||||
replaced_stream
|
||||
)
|
||||
from .misc import (
|
||||
load_path,
|
||||
@@ -26,12 +28,14 @@ from .misc import (
|
||||
take,
|
||||
chunked,
|
||||
divide,
|
||||
get_wrapped_stream,
|
||||
StreamWrapper
|
||||
)
|
||||
from .path import mkdir_p, rmtree, create_tracked_tempdir, create_tracked_tempfile
|
||||
from .spin import VistirSpinner, create_spinner
|
||||
from .spin import create_spinner
|
||||
|
||||
|
||||
__version__ = '0.2.5'
|
||||
__version__ = '0.3.0'
|
||||
|
||||
|
||||
__all__ = [
|
||||
@@ -50,7 +54,6 @@ __all__ = [
|
||||
"NamedTemporaryFile",
|
||||
"partialmethod",
|
||||
"spinner",
|
||||
"VistirSpinner",
|
||||
"create_spinner",
|
||||
"create_tracked_tempdir",
|
||||
"create_tracked_tempfile",
|
||||
@@ -61,4 +64,8 @@ __all__ = [
|
||||
"take",
|
||||
"chunked",
|
||||
"divide",
|
||||
"StringIO",
|
||||
"get_wrapped_stream",
|
||||
"StreamWrapper",
|
||||
"replaced_stream"
|
||||
]
|
||||
|
||||
Vendored
+102
-10
@@ -1,6 +1,7 @@
|
||||
# -*- coding=utf-8 -*-
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
import codecs
|
||||
import errno
|
||||
import os
|
||||
import sys
|
||||
@@ -19,7 +20,6 @@ __all__ = [
|
||||
"JSONDecodeError",
|
||||
"FileNotFoundError",
|
||||
"ResourceWarning",
|
||||
"FileNotFoundError",
|
||||
"PermissionError",
|
||||
"IsADirectoryError",
|
||||
"fs_str",
|
||||
@@ -27,6 +27,15 @@ __all__ = [
|
||||
"TemporaryDirectory",
|
||||
"NamedTemporaryFile",
|
||||
"to_native_string",
|
||||
"Iterable",
|
||||
"Mapping",
|
||||
"Sequence",
|
||||
"Set",
|
||||
"ItemsView",
|
||||
"fs_encode",
|
||||
"fs_decode",
|
||||
"_fs_encode_errors",
|
||||
"_fs_decode_errors"
|
||||
]
|
||||
|
||||
if sys.version_info >= (3, 5):
|
||||
@@ -47,20 +56,22 @@ else:
|
||||
try:
|
||||
from weakref import finalize
|
||||
except ImportError:
|
||||
from pipenv.vendor.backports.weakref import finalize
|
||||
from pipenv.vendor.backports.weakref import finalize # type: ignore
|
||||
|
||||
try:
|
||||
from functools import partialmethod
|
||||
except Exception:
|
||||
from .backports.functools import partialmethod
|
||||
from .backports.functools import partialmethod # type: ignore
|
||||
|
||||
try:
|
||||
from json import JSONDecodeError
|
||||
except ImportError: # Old Pythons.
|
||||
JSONDecodeError = ValueError
|
||||
JSONDecodeError = ValueError # type: ignore
|
||||
|
||||
if six.PY2:
|
||||
|
||||
from io import BytesIO as StringIO
|
||||
|
||||
class ResourceWarning(Warning):
|
||||
pass
|
||||
|
||||
@@ -80,12 +91,24 @@ if six.PY2:
|
||||
"""The command does not work on directories"""
|
||||
pass
|
||||
|
||||
class FileExistsError(OSError):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.errno = errno.EEXIST
|
||||
super(FileExistsError, self).__init__(*args, **kwargs)
|
||||
|
||||
else:
|
||||
from builtins import ResourceWarning, FileNotFoundError, PermissionError, IsADirectoryError
|
||||
|
||||
six.add_move(six.MovedAttribute("Iterable", "collections", "collections.abc"))
|
||||
from six.moves import Iterable
|
||||
from builtins import (
|
||||
ResourceWarning, FileNotFoundError, PermissionError, IsADirectoryError,
|
||||
FileExistsError
|
||||
)
|
||||
from io import StringIO
|
||||
|
||||
six.add_move(six.MovedAttribute("Iterable", "collections", "collections.abc")) # type: ignore
|
||||
six.add_move(six.MovedAttribute("Mapping", "collections", "collections.abc")) # type: ignore
|
||||
six.add_move(six.MovedAttribute("Sequence", "collections", "collections.abc")) # type: ignore
|
||||
six.add_move(six.MovedAttribute("Set", "collections", "collections.abc")) # type: ignore
|
||||
six.add_move(six.MovedAttribute("ItemsView", "collections", "collections.abc")) # type: ignore
|
||||
from six.moves import Iterable, Mapping, Sequence, Set, ItemsView # type: ignore # noqa
|
||||
|
||||
if not sys.warnoptions:
|
||||
warnings.simplefilter("default", ResourceWarning)
|
||||
@@ -179,13 +202,82 @@ def fs_str(string):
|
||||
|
||||
Borrowed from pip-tools
|
||||
"""
|
||||
|
||||
if isinstance(string, str):
|
||||
return string
|
||||
assert not isinstance(string, bytes)
|
||||
return string.encode(_fs_encoding)
|
||||
|
||||
|
||||
_fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
|
||||
def _get_path(path):
|
||||
"""
|
||||
Fetch the string value from a path-like object
|
||||
|
||||
Returns **None** if there is no string value.
|
||||
"""
|
||||
|
||||
if isinstance(path, (six.string_types, bytes)):
|
||||
return path
|
||||
path_type = type(path)
|
||||
try:
|
||||
path_repr = path_type.__fspath__(path)
|
||||
except AttributeError:
|
||||
return
|
||||
if isinstance(path_repr, (six.string_types, bytes)):
|
||||
return path_repr
|
||||
return
|
||||
|
||||
|
||||
def fs_encode(path):
|
||||
"""
|
||||
Encode a filesystem path to the proper filesystem encoding
|
||||
|
||||
:param Union[str, bytes] path: A string-like path
|
||||
:returns: A bytes-encoded filesystem path representation
|
||||
"""
|
||||
|
||||
path = _get_path(path)
|
||||
if path is None:
|
||||
raise TypeError("expected a valid path to encode")
|
||||
if isinstance(path, six.text_type):
|
||||
path = path.encode(_fs_encoding, _fs_encode_errors)
|
||||
return path
|
||||
|
||||
|
||||
def fs_decode(path):
|
||||
"""
|
||||
Decode a filesystem path using the proper filesystem encoding
|
||||
|
||||
:param path: The filesystem path to decode from bytes or string
|
||||
:return: [description]
|
||||
:rtype: [type]
|
||||
"""
|
||||
|
||||
path = _get_path(path)
|
||||
if path is None:
|
||||
raise TypeError("expected a valid path to decode")
|
||||
if isinstance(path, six.binary_type):
|
||||
path = path.decode(_fs_encoding, _fs_decode_errors)
|
||||
return path
|
||||
|
||||
|
||||
if sys.version_info >= (3, 3) and os.name != "nt":
|
||||
_fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
|
||||
else:
|
||||
_fs_encoding = "utf-8"
|
||||
|
||||
if six.PY3:
|
||||
if os.name == "nt":
|
||||
_fs_error_fn = None
|
||||
alt_strategy = "surrogatepass"
|
||||
else:
|
||||
alt_strategy = "surrogateescape"
|
||||
_fs_error_fn = getattr(sys, "getfilesystemencodeerrors", None)
|
||||
_fs_encode_errors = _fs_error_fn() if _fs_error_fn is not None else alt_strategy
|
||||
_fs_decode_errors = _fs_error_fn() if _fs_error_fn is not None else alt_strategy
|
||||
else:
|
||||
_fs_encode_errors = "backslashreplace"
|
||||
_fs_decode_errors = "replace"
|
||||
|
||||
|
||||
def to_native_string(string):
|
||||
|
||||
+56
-2
@@ -1,5 +1,5 @@
|
||||
# -*- coding=utf-8 -*-
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
from __future__ import absolute_import, unicode_literals, print_function
|
||||
|
||||
import io
|
||||
import os
|
||||
@@ -15,7 +15,8 @@ from .path import is_file_url, is_valid_url, path_to_url, url_to_path
|
||||
|
||||
|
||||
__all__ = [
|
||||
"temp_environ", "temp_path", "cd", "atomic_open_for_write", "open_file", "spinner"
|
||||
"temp_environ", "temp_path", "cd", "atomic_open_for_write", "open_file", "spinner",
|
||||
"dummy_spinner", "replaced_stream"
|
||||
]
|
||||
|
||||
|
||||
@@ -286,3 +287,56 @@ def open_file(link, session=None, stream=True):
|
||||
if conn is not None:
|
||||
conn.close()
|
||||
result.close()
|
||||
|
||||
|
||||
@contextmanager
|
||||
def replaced_stream(stream_name):
|
||||
"""
|
||||
Context manager to temporarily swap out *stream_name* with a stream wrapper.
|
||||
|
||||
:param str stream_name: The name of a sys stream to wrap
|
||||
:returns: A ``StreamWrapper`` replacement, temporarily
|
||||
|
||||
>>> orig_stdout = sys.stdout
|
||||
>>> with replaced_stream("stdout") as stdout:
|
||||
... sys.stdout.write("hello")
|
||||
... assert stdout.getvalue() == "hello"
|
||||
|
||||
>>> sys.stdout.write("hello")
|
||||
'hello'
|
||||
"""
|
||||
orig_stream = getattr(sys, stream_name)
|
||||
new_stream = six.StringIO()
|
||||
try:
|
||||
setattr(sys, stream_name, new_stream)
|
||||
yield getattr(sys, stream_name)
|
||||
finally:
|
||||
setattr(sys, stream_name, orig_stream)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def replaced_streams():
|
||||
"""
|
||||
Context manager to replace both ``sys.stdout`` and ``sys.stderr`` using
|
||||
``replaced_stream``
|
||||
|
||||
returns: *(stdout, stderr)*
|
||||
|
||||
>>> import sys
|
||||
>>> with vistir.contextmanagers.replaced_streams() as streams:
|
||||
>>> stdout, stderr = streams
|
||||
>>> sys.stderr.write("test")
|
||||
>>> sys.stdout.write("hello")
|
||||
>>> assert stdout.getvalue() == "hello"
|
||||
>>> assert stderr.getvalue() == "test"
|
||||
|
||||
>>> stdout.getvalue()
|
||||
'hello'
|
||||
|
||||
>>> stderr.getvalue()
|
||||
'test'
|
||||
"""
|
||||
|
||||
with replaced_stream("stdout") as stdout:
|
||||
with replaced_stream("stderr") as stderr:
|
||||
yield (stdout, stderr)
|
||||
|
||||
Vendored
+184
-16
@@ -1,6 +1,7 @@
|
||||
# -*- coding=utf-8 -*-
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
from __future__ import absolute_import, unicode_literals, print_function
|
||||
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
import locale
|
||||
@@ -15,7 +16,7 @@ from itertools import islice, tee
|
||||
import six
|
||||
|
||||
from .cmdparse import Script
|
||||
from .compat import Path, fs_str, partialmethod, to_native_string, Iterable
|
||||
from .compat import Path, fs_str, partialmethod, to_native_string, Iterable, StringIO
|
||||
from .contextmanagers import spinner as spinner
|
||||
|
||||
if os.name != "nt":
|
||||
@@ -38,6 +39,9 @@ __all__ = [
|
||||
"divide",
|
||||
"getpreferredencoding",
|
||||
"decode_for_output",
|
||||
"get_canonical_encoding_name",
|
||||
"get_wrapped_stream",
|
||||
"StreamWrapper",
|
||||
]
|
||||
|
||||
|
||||
@@ -159,7 +163,10 @@ def _create_subprocess(
|
||||
c = _spawn_subprocess(cmd, env=env, block=block, cwd=cwd,
|
||||
combine_stderr=combine_stderr)
|
||||
except Exception as exc:
|
||||
sys.stderr.write("Error %s while executing command %s", exc, " ".join(cmd._parts))
|
||||
import traceback
|
||||
formatted_tb = "".join(traceback.format_exception(*sys.exc_info()))
|
||||
sys.stderr.write("Error while executing command %s:" % " ".join(cmd._parts))
|
||||
sys.stderr.write(formatted_tb)
|
||||
raise
|
||||
if not block:
|
||||
c.stdin.close()
|
||||
@@ -279,14 +286,11 @@ def run(
|
||||
_env = os.environ.copy()
|
||||
if env:
|
||||
_env.update(env)
|
||||
env = _env
|
||||
if six.PY2:
|
||||
fs_encode = partial(to_bytes, encoding=locale_encoding)
|
||||
_env = {fs_encode(k): fs_encode(v) for k, v in os.environ.items()}
|
||||
for key, val in env.items():
|
||||
_env[fs_encode(key)] = fs_encode(val)
|
||||
_env = {fs_encode(k): fs_encode(v) for k, v in _env.items()}
|
||||
else:
|
||||
_env = {k: fs_str(v) for k, v in os.environ.items()}
|
||||
_env = {k: fs_str(v) for k, v in _env.items()}
|
||||
if not spinner_name:
|
||||
spinner_name = "bouncingBar"
|
||||
if six.PY2:
|
||||
@@ -315,7 +319,6 @@ def run(
|
||||
)
|
||||
|
||||
|
||||
|
||||
def load_path(python):
|
||||
"""Load the :mod:`sys.path` from the given python executable's environment as json
|
||||
|
||||
@@ -329,7 +332,7 @@ def load_path(python):
|
||||
|
||||
python = Path(python).as_posix()
|
||||
out, err = run([python, "-c", "import json, sys; print(json.dumps(sys.path))"],
|
||||
nospin=True)
|
||||
nospin=True)
|
||||
if out:
|
||||
return json.loads(out)
|
||||
else:
|
||||
@@ -515,19 +518,184 @@ def getpreferredencoding():
|
||||
PREFERRED_ENCODING = getpreferredencoding()
|
||||
|
||||
|
||||
def decode_for_output(output):
|
||||
def get_output_encoding(source_encoding):
|
||||
"""
|
||||
Given a source encoding, determine the preferred output encoding.
|
||||
|
||||
:param str source_encoding: The encoding of the source material.
|
||||
:returns: The output encoding to decode to.
|
||||
:rtype: str
|
||||
"""
|
||||
|
||||
if source_encoding is not None:
|
||||
if get_canonical_encoding_name(source_encoding) == 'ascii':
|
||||
return 'utf-8'
|
||||
return get_canonical_encoding_name(source_encoding)
|
||||
return get_canonical_encoding_name(PREFERRED_ENCODING)
|
||||
|
||||
|
||||
def _encode(output, encoding=None, errors=None, translation_map=None):
|
||||
if encoding is None:
|
||||
encoding = PREFERRED_ENCODING
|
||||
try:
|
||||
output = output.encode(encoding)
|
||||
except (UnicodeDecodeError, UnicodeEncodeError):
|
||||
if translation_map is not None:
|
||||
if six.PY2:
|
||||
output = unicode.translate(
|
||||
to_text(output, encoding=encoding, errors=errors), translation_map
|
||||
)
|
||||
else:
|
||||
output = output.translate(translation_map)
|
||||
else:
|
||||
output = to_text(output, encoding=encoding, errors=errors)
|
||||
except AttributeError:
|
||||
pass
|
||||
return output
|
||||
|
||||
|
||||
def decode_for_output(output, target_stream=None, translation_map=None):
|
||||
"""Given a string, decode it for output to a terminal
|
||||
|
||||
:param str output: A string to print to a terminal
|
||||
:param target_stream: A stream to write to, we will encode to target this stream if possible.
|
||||
:param dict translation_map: A mapping of unicode character ordinals to replacement strings.
|
||||
:return: A re-encoded string using the preferred encoding
|
||||
:rtype: str
|
||||
"""
|
||||
|
||||
if not isinstance(output, six.string_types):
|
||||
return output
|
||||
encoding = None
|
||||
if target_stream is not None:
|
||||
encoding = getattr(target_stream, "encoding", None)
|
||||
encoding = get_output_encoding(encoding)
|
||||
try:
|
||||
output = output.encode(PREFERRED_ENCODING)
|
||||
except AttributeError:
|
||||
pass
|
||||
output = output.decode(PREFERRED_ENCODING)
|
||||
return output
|
||||
output = _encode(output, encoding=encoding, translation_map=translation_map)
|
||||
except (UnicodeDecodeError, UnicodeEncodeError):
|
||||
output = _encode(output, encoding=encoding, errors="replace",
|
||||
translation_map=translation_map)
|
||||
return to_text(output, encoding=encoding, errors="replace")
|
||||
|
||||
|
||||
def get_canonical_encoding_name(name):
|
||||
# type: (str) -> str
|
||||
"""
|
||||
Given an encoding name, get the canonical name from a codec lookup.
|
||||
|
||||
:param str name: The name of the codec to lookup
|
||||
:return: The canonical version of the codec name
|
||||
:rtype: str
|
||||
"""
|
||||
|
||||
import codecs
|
||||
try:
|
||||
codec = codecs.lookup(name)
|
||||
except LookupError:
|
||||
return name
|
||||
else:
|
||||
return codec.name
|
||||
|
||||
|
||||
def get_wrapped_stream(stream):
|
||||
"""
|
||||
Given a stream, wrap it in a `StreamWrapper` instance and return the wrapped stream.
|
||||
|
||||
:param stream: A stream instance to wrap
|
||||
:returns: A new, wrapped stream
|
||||
:rtype: :class:`StreamWrapper`
|
||||
"""
|
||||
|
||||
if stream is None:
|
||||
raise TypeError("must provide a stream to wrap")
|
||||
encoding = getattr(stream, "encoding", None)
|
||||
encoding = get_output_encoding(encoding)
|
||||
return StreamWrapper(stream, encoding, "replace", line_buffering=True)
|
||||
|
||||
|
||||
class StreamWrapper(io.TextIOWrapper):
|
||||
|
||||
"""
|
||||
This wrapper class will wrap a provided stream and supply an interface
|
||||
for compatibility.
|
||||
"""
|
||||
|
||||
def __init__(self, stream, encoding, errors, line_buffering=True, **kwargs):
|
||||
self._stream = stream = _StreamProvider(stream)
|
||||
io.TextIOWrapper.__init__(
|
||||
self, stream, encoding, errors, line_buffering=line_buffering, **kwargs
|
||||
)
|
||||
|
||||
# borrowed from click's implementation of stream wrappers, see
|
||||
# https://github.com/pallets/click/blob/6cafd32/click/_compat.py#L64
|
||||
if six.PY2:
|
||||
def write(self, x):
|
||||
if isinstance(x, (str, buffer, bytearray)):
|
||||
try:
|
||||
self.flush()
|
||||
except Exception:
|
||||
pass
|
||||
return self.buffer.write(str(x))
|
||||
return io.TextIOWrapper.write(self, x)
|
||||
|
||||
def writelines(self, lines):
|
||||
for line in lines:
|
||||
self.write(line)
|
||||
|
||||
def __del__(self):
|
||||
try:
|
||||
self.detach()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def isatty(self):
|
||||
return self._stream.isatty()
|
||||
|
||||
|
||||
# More things borrowed from click, this is because we are using `TextIOWrapper` instead of
|
||||
# just a normal StringIO
|
||||
class _StreamProvider(object):
|
||||
def __init__(self, stream):
|
||||
self._stream = stream
|
||||
super(_StreamProvider, self).__init__()
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self._stream, name)
|
||||
|
||||
def read1(self, size):
|
||||
fn = getattr(self._stream, "read1", None)
|
||||
if fn is not None:
|
||||
return fn(size)
|
||||
if six.PY2:
|
||||
return self._stream.readline(size)
|
||||
return self._stream.read(size)
|
||||
|
||||
def readable(self):
|
||||
fn = getattr(self._stream, "readable", None)
|
||||
if fn is not None:
|
||||
return fn()
|
||||
try:
|
||||
self._stream.read(0)
|
||||
except Exception:
|
||||
return False
|
||||
return True
|
||||
|
||||
def writable(self):
|
||||
fn = getattr(self._stream, "writable", None)
|
||||
if fn is not None:
|
||||
return fn()
|
||||
try:
|
||||
self._stream.write(b"")
|
||||
except Exception:
|
||||
return False
|
||||
return True
|
||||
|
||||
def seekable(self):
|
||||
fn = getattr(self._stream, "seekable", None)
|
||||
if fn is not None:
|
||||
return fn()
|
||||
try:
|
||||
self._stream.seek(self._stream.tell())
|
||||
except Exception:
|
||||
return False
|
||||
return True
|
||||
|
||||
Vendored
+13
-19
@@ -23,6 +23,8 @@ from .compat import (
|
||||
TemporaryDirectory,
|
||||
_fs_encoding,
|
||||
finalize,
|
||||
fs_decode,
|
||||
fs_encode
|
||||
)
|
||||
|
||||
|
||||
@@ -195,9 +197,8 @@ def is_readonly_path(fn):
|
||||
|
||||
Permissions check is `bool(path.stat & stat.S_IREAD)` or `not os.access(path, os.W_OK)`
|
||||
"""
|
||||
from .compat import to_native_string
|
||||
|
||||
fn = to_native_string(fn)
|
||||
fn = fs_encode(fn)
|
||||
if os.path.exists(fn):
|
||||
file_stat = os.stat(fn).st_mode
|
||||
return not bool(file_stat & stat.S_IWRITE) or not os.access(fn, os.W_OK)
|
||||
@@ -212,20 +213,19 @@ def mkdir_p(newdir, mode=0o777):
|
||||
:raises: OSError if a file is encountered along the way
|
||||
"""
|
||||
# http://code.activestate.com/recipes/82465-a-friendly-mkdir/
|
||||
from .misc import to_bytes, to_text
|
||||
|
||||
newdir = to_bytes(newdir, "utf-8")
|
||||
newdir = fs_encode(newdir)
|
||||
if os.path.exists(newdir):
|
||||
if not os.path.isdir(newdir):
|
||||
raise OSError(
|
||||
"a file with the same name as the desired dir, '{0}', already exists.".format(
|
||||
newdir
|
||||
fs_decode(newdir)
|
||||
)
|
||||
)
|
||||
else:
|
||||
head, tail = os.path.split(to_bytes(newdir, encoding="utf-8"))
|
||||
head, tail = os.path.split(newdir)
|
||||
# Make sure the tail doesn't point to the asame place as the head
|
||||
curdir = to_bytes(".", encoding="utf-8")
|
||||
curdir = fs_encode(".")
|
||||
tail_and_head_match = (
|
||||
os.path.relpath(tail, start=os.path.basename(head)) == curdir
|
||||
)
|
||||
@@ -234,7 +234,7 @@ def mkdir_p(newdir, mode=0o777):
|
||||
if os.path.exists(target) and os.path.isfile(target):
|
||||
raise OSError(
|
||||
"A file with the same name as the desired dir, '{0}', already exists.".format(
|
||||
to_text(newdir, encoding="utf-8")
|
||||
fs_decode(newdir)
|
||||
)
|
||||
)
|
||||
os.makedirs(os.path.join(head, tail), mode)
|
||||
@@ -296,9 +296,7 @@ def set_write_bit(fn):
|
||||
:param str fn: The target filename or path
|
||||
"""
|
||||
|
||||
from .compat import to_native_string
|
||||
|
||||
fn = to_native_string(fn)
|
||||
fn = fs_encode(fn)
|
||||
if not os.path.exists(fn):
|
||||
return
|
||||
file_stat = os.stat(fn).st_mode
|
||||
@@ -330,9 +328,7 @@ def rmtree(directory, ignore_errors=False, onerror=None):
|
||||
Setting `ignore_errors=True` may cause this to silently fail to delete the path
|
||||
"""
|
||||
|
||||
from .compat import to_native_string
|
||||
|
||||
directory = to_native_string(directory)
|
||||
directory = fs_encode(directory)
|
||||
if onerror is None:
|
||||
onerror = handle_remove_readonly
|
||||
try:
|
||||
@@ -341,9 +337,8 @@ def rmtree(directory, ignore_errors=False, onerror=None):
|
||||
)
|
||||
except (IOError, OSError, FileNotFoundError) as exc:
|
||||
# Ignore removal failures where the file doesn't exist
|
||||
if exc.errno == errno.ENOENT:
|
||||
pass
|
||||
raise
|
||||
if exc.errno != errno.ENOENT:
|
||||
raise
|
||||
|
||||
|
||||
def handle_remove_readonly(func, path, exc):
|
||||
@@ -361,7 +356,7 @@ def handle_remove_readonly(func, path, exc):
|
||||
"""
|
||||
# Check for read-only attribute
|
||||
from .compat import (
|
||||
ResourceWarning, FileNotFoundError, PermissionError, to_native_string
|
||||
ResourceWarning, FileNotFoundError, PermissionError
|
||||
)
|
||||
|
||||
PERM_ERRORS = (errno.EACCES, errno.EPERM, errno.ENOENT)
|
||||
@@ -370,7 +365,6 @@ def handle_remove_readonly(func, path, exc):
|
||||
)
|
||||
# split the initial exception out into its type, exception, and traceback
|
||||
exc_type, exc_exception, exc_tb = exc
|
||||
path = to_native_string(path)
|
||||
if is_readonly_path(path):
|
||||
# Apply write permission and call original function
|
||||
set_write_bit(path)
|
||||
|
||||
Vendored
+105
-65
@@ -1,4 +1,5 @@
|
||||
# -*- coding=utf-8 -*-
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import functools
|
||||
import os
|
||||
@@ -8,41 +9,72 @@ import threading
|
||||
import time
|
||||
|
||||
import colorama
|
||||
import cursor
|
||||
import six
|
||||
|
||||
from .compat import to_native_string
|
||||
from .termcolors import COLOR_MAP, COLORS, colored, DISABLE_COLORS
|
||||
from .misc import decode_for_output
|
||||
from io import StringIO
|
||||
|
||||
try:
|
||||
import yaspin
|
||||
import cursor
|
||||
except ImportError:
|
||||
yaspin = None
|
||||
Spinners = None
|
||||
SpinBase = None
|
||||
cursor = None
|
||||
else:
|
||||
from yaspin.spinners import Spinners
|
||||
import yaspin.spinners
|
||||
import yaspin.core
|
||||
Spinners = yaspin.spinners.Spinners
|
||||
SpinBase = yaspin.core.Yaspin
|
||||
|
||||
handler = None
|
||||
if yaspin and os.name == "nt":
|
||||
handler = yaspin.signal_handlers.default_handler
|
||||
elif yaspin and os.name != "nt":
|
||||
handler = yaspin.signal_handlers.fancy_handler
|
||||
if os.name == "nt":
|
||||
def handler(signum, frame, spinner):
|
||||
"""Signal handler, used to gracefully shut down the ``spinner`` instance
|
||||
when specified signal is received by the process running the ``spinner``.
|
||||
|
||||
``signum`` and ``frame`` are mandatory arguments. Check ``signal.signal``
|
||||
function for more details.
|
||||
"""
|
||||
spinner.fail()
|
||||
spinner.stop()
|
||||
sys.exit(0)
|
||||
|
||||
else:
|
||||
def handler(signum, frame, spinner):
|
||||
"""Signal handler, used to gracefully shut down the ``spinner`` instance
|
||||
when specified signal is received by the process running the ``spinner``.
|
||||
|
||||
``signum`` and ``frame`` are mandatory arguments. Check ``signal.signal``
|
||||
function for more details.
|
||||
"""
|
||||
spinner.red.fail("✘")
|
||||
spinner.stop()
|
||||
sys.exit(0)
|
||||
|
||||
CLEAR_LINE = chr(27) + "[K"
|
||||
|
||||
TRANSLATION_MAP = {
|
||||
10004: u"OK",
|
||||
10008: u"x",
|
||||
}
|
||||
|
||||
|
||||
decode_output = functools.partial(decode_for_output, translation_map=TRANSLATION_MAP)
|
||||
|
||||
|
||||
class DummySpinner(object):
|
||||
def __init__(self, text="", **kwargs):
|
||||
super(DummySpinner, self).__init__()
|
||||
if DISABLE_COLORS:
|
||||
colorama.init()
|
||||
from .misc import decode_for_output
|
||||
self.text = to_native_string(decode_for_output(text)) if text else ""
|
||||
self.text = to_native_string(decode_output(text)) if text else ""
|
||||
self.stdout = kwargs.get("stdout", sys.stdout)
|
||||
self.stderr = kwargs.get("stderr", sys.stderr)
|
||||
self.out_buff = StringIO()
|
||||
self.write_to_stdout = kwargs.get("write_to_stdout", False)
|
||||
super(DummySpinner, self).__init__()
|
||||
|
||||
def __enter__(self):
|
||||
if self.text and self.text != "None":
|
||||
@@ -50,11 +82,11 @@ class DummySpinner(object):
|
||||
self.write(self.text)
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, traceback):
|
||||
def __exit__(self, exc_type, exc_val, tb):
|
||||
if exc_type:
|
||||
import traceback
|
||||
from .misc import decode_for_output
|
||||
self.write_err(decode_for_output(traceback.format_exception(*sys.exc_info())))
|
||||
formatted_tb = traceback.format_exception(exc_type, exc_val, tb)
|
||||
self.write_err("".join(formatted_tb))
|
||||
self._close_output_buffer()
|
||||
return False
|
||||
|
||||
@@ -76,56 +108,63 @@ class DummySpinner(object):
|
||||
pass
|
||||
|
||||
def fail(self, exitcode=1, text="FAIL"):
|
||||
from .misc import decode_for_output
|
||||
if text and text != "None":
|
||||
if text is not None and text != "None":
|
||||
if self.write_to_stdout:
|
||||
self.write(decode_for_output(text))
|
||||
self.write(text)
|
||||
else:
|
||||
self.write_err(decode_for_output(text))
|
||||
self.write_err(text)
|
||||
self._close_output_buffer()
|
||||
|
||||
def ok(self, text="OK"):
|
||||
if text and text != "None":
|
||||
if text is not None and text != "None":
|
||||
if self.write_to_stdout:
|
||||
self.stdout.write(self.text)
|
||||
self.write(text)
|
||||
else:
|
||||
self.stderr.write(self.text)
|
||||
self.write_err(text)
|
||||
self._close_output_buffer()
|
||||
return 0
|
||||
|
||||
def hide_and_write(self, text, target=None):
|
||||
if not target:
|
||||
target = self.stdout
|
||||
from .misc import decode_for_output
|
||||
if text is None or isinstance(text, six.string_types) and text == "None":
|
||||
pass
|
||||
target.write(decode_for_output("\r"))
|
||||
target.write(decode_output("\r", target_stream=target))
|
||||
self._hide_cursor(target=target)
|
||||
target.write(decode_for_output("{0}\n".format(text)))
|
||||
target.write(decode_output("{0}\n".format(text), target_stream=target))
|
||||
target.write(CLEAR_LINE)
|
||||
self._show_cursor(target=target)
|
||||
|
||||
def write(self, text=None):
|
||||
if not self.write_to_stdout:
|
||||
return self.write_err(text)
|
||||
from .misc import decode_for_output
|
||||
if text is None or isinstance(text, six.string_types) and text == "None":
|
||||
pass
|
||||
text = decode_for_output(text)
|
||||
self.stdout.write(decode_for_output("\r"))
|
||||
line = decode_for_output("{0}\n".format(text))
|
||||
self.stdout.write(line)
|
||||
self.stdout.write(CLEAR_LINE)
|
||||
if not self.stdout.closed:
|
||||
stdout = self.stdout
|
||||
else:
|
||||
stdout = sys.stdout
|
||||
text = decode_output(text, target_stream=stdout)
|
||||
stdout.write(decode_output("\r", target_stream=stdout))
|
||||
line = decode_output("{0}\n".format(text), target_stream=stdout)
|
||||
stdout.write(line)
|
||||
stdout.write(CLEAR_LINE)
|
||||
|
||||
def write_err(self, text=None):
|
||||
from .misc import decode_for_output
|
||||
if text is None or isinstance(text, six.string_types) and text == "None":
|
||||
pass
|
||||
text = decode_for_output(text)
|
||||
self.stderr.write(decode_for_output("\r"))
|
||||
line = decode_for_output("{0}\n".format(text))
|
||||
self.stderr.write(line)
|
||||
self.stderr.write(CLEAR_LINE)
|
||||
if not self.stderr.closed:
|
||||
stderr = self.stderr
|
||||
else:
|
||||
if sys.stderr.closed:
|
||||
print(text)
|
||||
return
|
||||
stderr = sys.stderr
|
||||
text = decode_output(text, target_stream=stderr)
|
||||
stderr.write(decode_output("\r", target_stream=stderr))
|
||||
line = decode_output("{0}\n".format(text), target_stream=stderr)
|
||||
stderr.write(line)
|
||||
stderr.write(CLEAR_LINE)
|
||||
|
||||
@staticmethod
|
||||
def _hide_cursor(target=None):
|
||||
@@ -136,10 +175,11 @@ class DummySpinner(object):
|
||||
pass
|
||||
|
||||
|
||||
base_obj = yaspin.core.Yaspin if yaspin is not None else DummySpinner
|
||||
if SpinBase is None:
|
||||
SpinBase = DummySpinner
|
||||
|
||||
|
||||
class VistirSpinner(base_obj):
|
||||
class VistirSpinner(SpinBase):
|
||||
"A spinner class for handling spinners on windows and posix."
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -182,6 +222,8 @@ class VistirSpinner(base_obj):
|
||||
self.write_to_stdout = write_to_stdout
|
||||
self.is_dummy = bool(yaspin is None)
|
||||
super(VistirSpinner, self).__init__(*args, **kwargs)
|
||||
if DISABLE_COLORS:
|
||||
colorama.deinit()
|
||||
|
||||
def ok(self, text="OK", err=False):
|
||||
"""Set Ok (success) finalizer to a spinner."""
|
||||
@@ -204,38 +246,40 @@ class VistirSpinner(base_obj):
|
||||
def hide_and_write(self, text, target=None):
|
||||
if not target:
|
||||
target = self.stdout
|
||||
from .misc import decode_for_output
|
||||
if text is None or isinstance(text, six.string_types) and text == "None":
|
||||
pass
|
||||
target.write(decode_for_output("\r"))
|
||||
target.write(decode_output("\r"))
|
||||
self._hide_cursor(target=target)
|
||||
target.write(decode_for_output("{0}\n".format(text)))
|
||||
target.write(decode_output("{0}\n".format(text)))
|
||||
target.write(CLEAR_LINE)
|
||||
self._show_cursor(target=target)
|
||||
|
||||
def write(self, text):
|
||||
if not self.write_to_stdout:
|
||||
return self.write_err(text)
|
||||
from .misc import to_text
|
||||
sys.stdout.write("\r")
|
||||
self.stdout.write(CLEAR_LINE)
|
||||
stdout = self.stdout
|
||||
if self.stdout.closed:
|
||||
stdout = sys.stdout
|
||||
stdout.write(decode_output("\r", target_stream=stdout))
|
||||
stdout.write(decode_output(CLEAR_LINE, target_stream=stdout))
|
||||
if text is None:
|
||||
text = ""
|
||||
text = to_native_string("{0}\n".format(text))
|
||||
self.stdout.write(text)
|
||||
self.out_buff.write(to_text(text))
|
||||
text = decode_output("{0}\n".format(text), target_stream=stdout)
|
||||
stdout.write(text)
|
||||
self.out_buff.write(decode_output(text, target_stream=self.out_buff))
|
||||
|
||||
def write_err(self, text):
|
||||
"""Write error text in the terminal without breaking the spinner."""
|
||||
from .misc import to_text
|
||||
|
||||
self.stderr.write("\r")
|
||||
self.stderr.write(CLEAR_LINE)
|
||||
stderr = self.stderr
|
||||
if self.stderr.closed:
|
||||
stderr = sys.stderr
|
||||
stderr.write(decode_output("\r", target_stream=stderr))
|
||||
stderr.write(decode_output(CLEAR_LINE, target_stream=stderr))
|
||||
if text is None:
|
||||
text = ""
|
||||
text = to_native_string("{0}\n".format(text))
|
||||
text = decode_output("{0}\n".format(text), target_stream=stderr)
|
||||
self.stderr.write(text)
|
||||
self.out_buff.write(to_text(text))
|
||||
self.out_buff.write(decode_output(text, target_stream=self.out_buff))
|
||||
|
||||
def start(self):
|
||||
if self._sigmap:
|
||||
@@ -270,26 +314,22 @@ class VistirSpinner(base_obj):
|
||||
|
||||
if target.isatty():
|
||||
self._show_cursor(target=target)
|
||||
if self.stderr and self.stderr != sys.stderr:
|
||||
self.stderr.close()
|
||||
if self.stdout and self.stdout != sys.stdout:
|
||||
self.stdout.close()
|
||||
self.out_buff.close()
|
||||
|
||||
def _freeze(self, final_text, err=False):
|
||||
"""Stop spinner, compose last frame and 'freeze' it."""
|
||||
if not final_text:
|
||||
final_text = ""
|
||||
text = to_native_string(final_text)
|
||||
target = self.stderr if err else self.stdout
|
||||
if target.closed:
|
||||
target = sys.stderr if err else sys.stdout
|
||||
text = decode_output(final_text, target_stream=target)
|
||||
self._last_frame = self._compose_out(text, mode="last")
|
||||
|
||||
# Should be stopped here, otherwise prints after
|
||||
# self._freeze call will mess up the spinner
|
||||
self.stop()
|
||||
if err or not self.write_to_stdout:
|
||||
self.stderr.write(self._last_frame)
|
||||
else:
|
||||
self.stdout.write(self._last_frame)
|
||||
target.write(self._last_frame)
|
||||
|
||||
def _compose_color_func(self):
|
||||
fn = functools.partial(
|
||||
@@ -303,19 +343,19 @@ class VistirSpinner(base_obj):
|
||||
def _compose_out(self, frame, mode=None):
|
||||
# Ensure Unicode input
|
||||
|
||||
frame = to_native_string(frame)
|
||||
frame = decode_output(frame)
|
||||
if self._text is None:
|
||||
self._text = ""
|
||||
text = to_native_string(self._text)
|
||||
text = decode_output(self._text)
|
||||
if self._color_func is not None:
|
||||
frame = self._color_func(frame)
|
||||
if self._side == "right":
|
||||
frame, text = text, frame
|
||||
# Mode
|
||||
if not mode:
|
||||
out = to_native_string("\r{0} {1}".format(frame, text))
|
||||
out = decode_output("\r{0} {1}".format(frame, text))
|
||||
else:
|
||||
out = to_native_string("{0} {1}\n".format(frame, text))
|
||||
out = decode_output("{0} {1}\n".format(frame, text))
|
||||
return out
|
||||
|
||||
def _spin(self):
|
||||
|
||||
@@ -6,11 +6,11 @@ import warnings
|
||||
import pytest
|
||||
|
||||
from vistir.compat import ResourceWarning, fs_str
|
||||
from vistir.contextmanagers import temp_environ
|
||||
from vistir.path import mkdir_p
|
||||
|
||||
from pipenv._compat import Path, TemporaryDirectory
|
||||
from pipenv.exceptions import VirtualenvActivationException
|
||||
from pipenv.utils import temp_environ
|
||||
from pipenv.vendor import delegator, requests, toml, tomlkit
|
||||
from pytest_pypi.app import prepare_fixtures
|
||||
from pytest_pypi.app import prepare_packages as prepare_pypi_packages
|
||||
@@ -187,14 +187,21 @@ class _Pipfile(object):
|
||||
|
||||
class _PipenvInstance(object):
|
||||
"""An instance of a Pipenv Project..."""
|
||||
def __init__(self, pypi=None, pipfile=True, chdir=False, path=None, home_dir=None):
|
||||
def __init__(
|
||||
self, pypi=None, pipfile=True, chdir=False, path=None, home_dir=None,
|
||||
venv_root=None, ignore_virtualenvs=True, venv_in_project=True
|
||||
):
|
||||
self.pypi = pypi
|
||||
self.original_umask = os.umask(0o007)
|
||||
if ignore_virtualenvs:
|
||||
os.environ["PIPENV_IGNORE_VIRTUALENVS"] = fs_str("1")
|
||||
if venv_root:
|
||||
os.environ["VIRTUAL_ENV"] = venv_root
|
||||
if venv_in_project:
|
||||
os.environ["PIPENV_VENV_IN_PROJECT"] = fs_str("1")
|
||||
else:
|
||||
os.environ.pop("PIPENV_VENV_IN_PROJECT", None)
|
||||
|
||||
self.original_dir = os.path.abspath(os.curdir)
|
||||
os.environ["PIPENV_NOSPIN"] = fs_str("1")
|
||||
os.environ["CI"] = fs_str("1")
|
||||
warnings.simplefilter("ignore", category=ResourceWarning)
|
||||
warnings.filterwarnings("ignore", category=ResourceWarning, message="unclosed.*<ssl.SSLSocket.*>")
|
||||
path = path if path else os.environ.get("PIPENV_PROJECT_DIR", None)
|
||||
if not path:
|
||||
self._path = TemporaryDirectory(suffix='-project', prefix='pipenv-')
|
||||
@@ -224,10 +231,6 @@ class _PipenvInstance(object):
|
||||
self._pipfile = _Pipfile(Path(p_path))
|
||||
|
||||
def __enter__(self):
|
||||
os.environ['PIPENV_DONT_USE_PYENV'] = fs_str('1')
|
||||
os.environ['PIPENV_IGNORE_VIRTUALENVS'] = fs_str('1')
|
||||
os.environ['PIPENV_VENV_IN_PROJECT'] = fs_str('1')
|
||||
os.environ['PIPENV_NOSPIN'] = fs_str('1')
|
||||
if self.chdir:
|
||||
os.chdir(self.path)
|
||||
return self
|
||||
@@ -243,7 +246,6 @@ class _PipenvInstance(object):
|
||||
except OSError as e:
|
||||
_warn_msg = warn_msg.format(e)
|
||||
warnings.warn(_warn_msg, ResourceWarning)
|
||||
os.umask(self.original_umask)
|
||||
|
||||
def pipenv(self, cmd, block=True):
|
||||
if self.pipfile_path and os.path.isfile(self.pipfile_path):
|
||||
@@ -290,8 +292,18 @@ class _PipenvInstance(object):
|
||||
|
||||
@pytest.fixture()
|
||||
def PipenvInstance():
|
||||
yield _PipenvInstance
|
||||
|
||||
with temp_environ():
|
||||
original_umask = os.umask(0o007)
|
||||
os.environ["PIPENV_NOSPIN"] = fs_str("1")
|
||||
os.environ["CI"] = fs_str("1")
|
||||
os.environ['PIPENV_DONT_USE_PYENV'] = fs_str('1')
|
||||
os.environ['PIPENV_NOSPIN'] = fs_str('1')
|
||||
warnings.simplefilter("ignore", category=ResourceWarning)
|
||||
warnings.filterwarnings("ignore", category=ResourceWarning, message="unclosed.*<ssl.SSLSocket.*>")
|
||||
try:
|
||||
yield _PipenvInstance
|
||||
finally:
|
||||
os.umask(original_umask)
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def pip_src_dir(request, pathlib_tmpdir):
|
||||
|
||||
@@ -8,6 +8,7 @@ import pytest
|
||||
from pipenv.patched import pipfile
|
||||
from pipenv.project import Project
|
||||
from pipenv.utils import temp_environ
|
||||
import pipenv.environments
|
||||
|
||||
|
||||
@pytest.mark.project
|
||||
@@ -168,25 +169,41 @@ def test_include_editable_packages(PipenvInstance, pypi, testsroot, pathlib_tmpd
|
||||
|
||||
@pytest.mark.project
|
||||
@pytest.mark.virtualenv
|
||||
def test_run_in_virtualenv(PipenvInstance, pypi, virtualenv):
|
||||
with PipenvInstance(chdir=True, pypi=pypi) as p:
|
||||
os.environ['PIPENV_IGNORE_VIRTUALENVS'] = '1'
|
||||
def test_run_in_virtualenv_with_global_context(PipenvInstance, pypi, virtualenv):
|
||||
with PipenvInstance(chdir=True, pypi=pypi, venv_root=virtualenv.as_posix(), ignore_virtualenvs=False, venv_in_project=False) as p:
|
||||
c = p.pipenv('run which pip')
|
||||
assert c.return_code == 0
|
||||
assert 'virtualenv' not in c.out
|
||||
|
||||
os.environ.pop("PIPENV_IGNORE_VIRTUALENVS", None)
|
||||
c = p.pipenv('run which pip')
|
||||
assert c.return_code == 0
|
||||
assert 'virtualenv' in c.out
|
||||
assert 'Creating a virtualenv' not in c.err
|
||||
project = Project()
|
||||
assert project.virtualenv_location == str(virtualenv)
|
||||
c = p.pipenv("run pip install click")
|
||||
assert c.return_code == 0
|
||||
assert "Courtesy Notice" in c.err
|
||||
c = p.pipenv("install six")
|
||||
assert c.return_code == 0
|
||||
c = p.pipenv('run python -c "import click;print(click.__file__)"')
|
||||
assert c.return_code == 0
|
||||
assert c.out.strip().startswith(str(virtualenv))
|
||||
c = p.pipenv("clean --dry-run")
|
||||
assert c.return_code == 0
|
||||
assert "click" in c.out
|
||||
|
||||
|
||||
@pytest.mark.project
|
||||
@pytest.mark.virtualenv
|
||||
def test_run_in_virtualenv(PipenvInstance, pypi):
|
||||
with PipenvInstance(chdir=True, pypi=pypi) as p:
|
||||
c = p.pipenv('run which pip')
|
||||
assert c.return_code == 0
|
||||
assert 'Creating a virtualenv' in c.err
|
||||
project = Project()
|
||||
c = p.pipenv("run pip install click")
|
||||
assert c.return_code == 0
|
||||
c = p.pipenv("install six")
|
||||
assert c.return_code == 0
|
||||
c = p.pipenv('run python -c "import click;print(click.__file__)"')
|
||||
assert c.return_code == 0
|
||||
assert c.out.strip().startswith(str(project.virtualenv_location))
|
||||
c = p.pipenv("clean --dry-run")
|
||||
assert c.return_code == 0
|
||||
assert "click" in c.out
|
||||
|
||||
Reference in New Issue
Block a user