Issue 4993 Add standard pre commit hooks and apply linting. (#4994)

* Add .pre-commit-config.yaml to the project and exclude tests (for now).  This does not include the MyPy linting that pip does but does include everything else.
This commit is contained in:
Matt Davis
2022-03-29 23:01:50 -04:00
committed by GitHub
parent 3387881a6d
commit 9a3b3ce706
48 changed files with 2330 additions and 1350 deletions
+8 -1
View File
@@ -72,6 +72,14 @@ jobs:
git submodule update --init --recursive
python -m pip install -e . --upgrade
pipenv install --deploy --dev --python=${{ steps.python-path.outputs.path }}
- name: Lint check of the code
env:
PIPENV_DEFAULT_PYTHON_VERSION: ${{ matrix.python-version }}
PYTHONWARNINGS: ignore:DEPRECATION
PYTHONIOENCODING: "utf-8"
GIT_ASK_YESNO: "false"
run: |
pipenv run pre-commit run --all-files --verbose
- name: Run tests
env:
PIPENV_DEFAULT_PYTHON_VERSION: ${{ matrix.python-version }}
@@ -109,4 +117,3 @@ jobs:
- run: |
python -m pip install --upgrade wheel invoke parver bs4 vistir towncrier
python -m invoke vendoring.update
+2
View File
@@ -0,0 +1,2 @@
[settings]
profile = black
+64
View File
@@ -0,0 +1,64 @@
exclude: '^(pipenv/patched/|pipenv/vendor/|tests/)'
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.1.0
hooks:
- id: check-builtin-literals
- id: check-added-large-files
- id: check-case-conflict
- id: check-ast
- id: check-toml
- id: check-yaml
- id: debug-statements
- id: end-of-file-fixer
exclude: WHEEL
- id: forbid-new-submodules
- id: trailing-whitespace
exclude: .patch
- repo: https://github.com/psf/black
rev: 22.3.0
hooks:
- id: black
- repo: https://gitlab.com/pycqa/flake8
rev: 4.0.1
hooks:
- id: flake8
additional_dependencies: [
'flake8-bugbear==20.1.4',
'flake8-logging-format==0.6.0',
'flake8-implicit-str-concat==0.2.0',
]
exclude: tests/data
- repo: https://github.com/PyCQA/isort
rev: 5.10.1
hooks:
- id: isort
files: \.py$
- repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.7.0
hooks:
- id: python-no-log-warn
- id: python-no-eval
- id: rst-backticks
files: .*\.rst$
types: [file]
- repo: local
hooks:
- id: news-fragment-filenames
name: NEWS fragment
language: fail
entry: NEWS fragment files must be named *.(feature|behavior|bugfix|vendor|doc|trivial|removal|process).rst
exclude: ^news/(towncrier_template.rst|.*\.(feature|behavior|bugfix|vendor|doc|trivial|removal|process).rst)
files: ^news/
- repo: https://github.com/mgedmin/check-manifest
rev: '0.46'
hooks:
- id: check-manifest
stages: [manual]
+16 -16
View File
@@ -5,7 +5,7 @@
Bug Fixes
---------
- Environment variables were not being loaded when the `--quiet` flag was set `#5010 <https://github.com/pypa/pipenv/issues/5010>`_
- Environment variables were not being loaded when the ``--quiet`` flag was set `#5010 <https://github.com/pypa/pipenv/issues/5010>`_
- It would appear that ``requirementslib`` was not fully specifying the subdirectory to ``build_pep517`` and
and when a new version of ``setuptools`` was released, the test ``test_lock_nested_vcs_direct_url``
broke indicating the Pipfile.lock no longer contained the extra dependencies that should have been resolved.
@@ -25,7 +25,7 @@ Features & Improvements
-----------------------
- It is now possible to silence the ``Loading .env environment variables`` message on ``pipenv run``
with the ``--quiet`` flag or the `PIPENV_QUIET` environment variable. `#4027 <https://github.com/pypa/pipenv/issues/4027>`_
with the ``--quiet`` flag or the ``PIPENV_QUIET`` environment variable. `#4027 <https://github.com/pypa/pipenv/issues/4027>`_
Bug Fixes
---------
@@ -43,7 +43,7 @@ Bug Fixes
Features & Improvements
-----------------------
- Use environment variable `PIPENV_SKIP_LOCK` to control the behaviour of lock skipping. `#4797 <https://github.com/pypa/pipenv/issues/4797>`_
- Use environment variable ``PIPENV_SKIP_LOCK`` to control the behaviour of lock skipping. `#4797 <https://github.com/pypa/pipenv/issues/4797>`_
- New CLI command ``verify``, checks the Pipfile.lock is up-to-date `#4893 <https://github.com/pypa/pipenv/issues/4893>`_
Behavior Changes
@@ -56,15 +56,15 @@ Bug Fixes
- Python versions on Windows can now be installed automatically through pyenv-win `#4525 <https://github.com/pypa/pipenv/issues/4525>`_
- Patched our vendored Pip to fix: Pipenv Lock (Or Install) Does Not Respect Index Specified For A Package. `#4637 <https://github.com/pypa/pipenv/issues/4637>`_
- If `PIP_TARGET` is set to environment variables, Refer specified directory for calculate delta, instead default directory `#4775 <https://github.com/pypa/pipenv/issues/4775>`_
- If ``PIP_TARGET`` is set to environment variables, Refer specified directory for calculate delta, instead default directory `#4775 <https://github.com/pypa/pipenv/issues/4775>`_
- Remove remaining mention of python2 and --two flag from codebase. `#4938 <https://github.com/pypa/pipenv/issues/4938>`_
- Use `CI` environment value, over mere existence of name `#4944 <https://github.com/pypa/pipenv/issues/4944>`_
- Use ``CI`` environment value, over mere existence of name `#4944 <https://github.com/pypa/pipenv/issues/4944>`_
- Environment variables from dot env files are now properly expanded when included in scripts. `#4975 <https://github.com/pypa/pipenv/issues/4975>`_
Vendored Libraries
------------------
- Updated vendor version of `pythonfinder` from `1.2.9` to `1.2.10` which fixes a bug with WSL
- Updated vendor version of ``pythonfinder`` from ``1.2.9`` to ``1.2.10`` which fixes a bug with WSL
(Windows Subsystem for Linux) when a path can not be read and Permission Denied error is encountered. `#4976 <https://github.com/pypa/pipenv/issues/4976>`_
Removals and Deprecations
@@ -207,7 +207,7 @@ Vendored Libraries
- ``tomli 1.1.0``
- ``wheel 0.36.2`` `#4747 <https://github.com/pypa/pipenv/issues/4747>`_
- Drop the dependencies for Python 2.7 compatibility purpose. `#4751 <https://github.com/pypa/pipenv/issues/4751>`_
- Switch the dependency resolver from ``pip-tools`` to `pip`.
- Switch the dependency resolver from ``pip-tools`` to ``pip``.
Update vendor libraries:
- Update ``requirementslib`` from ``1.5.16`` to ``1.6.1``
@@ -416,7 +416,7 @@ Features & Improvements
- Allow overriding PIPENV_INSTALL_TIMEOUT environment variable (in seconds). `#3652 <https://github.com/pypa/pipenv/issues/3652>`_
- Allow overriding PIP_EXISTS_ACTION evironment variable (value is passed to pip install).
Possible values here: https://pip.pypa.io/en/stable/reference/pip/#exists-action-option
Useful when you need to `PIP_EXISTS_ACTION=i` (ignore existing packages) - great for CI environments, where you need really fast setup. `#3738 <https://github.com/pypa/pipenv/issues/3738>`_
Useful when you need to ``PIP_EXISTS_ACTION=i`` (ignore existing packages) - great for CI environments, where you need really fast setup. `#3738 <https://github.com/pypa/pipenv/issues/3738>`_
- Pipenv will no longer forcibly override ``PIP_NO_DEPS`` on all vcs and file dependencies as resolution happens on these in a pre-lock step. `#3763 <https://github.com/pypa/pipenv/issues/3763>`_
- Improved verbose logging output during ``pipenv lock`` will now stream output to the console while maintaining a spinner. `#3810 <https://github.com/pypa/pipenv/issues/3810>`_
- Added support for automatic python installs via ``asdf`` and associated ``PIPENV_DONT_USE_ASDF`` environment variable. `#4018 <https://github.com/pypa/pipenv/issues/4018>`_
@@ -434,7 +434,7 @@ Behavior Changes
Bug Fixes
---------
- Raise `PipenvUsageError` when [[source]] does not contain url field. `#2373 <https://github.com/pypa/pipenv/issues/2373>`_
- Raise ``PipenvUsageError`` when [[source]] does not contain url field. `#2373 <https://github.com/pypa/pipenv/issues/2373>`_
- Fixed a bug which caused editable package resolution to sometimes fail with an unhelpful setuptools-related error message. `#2722 <https://github.com/pypa/pipenv/issues/2722>`_
- Fixed an issue which caused errors due to reliance on the system utilities ``which`` and ``where`` which may not always exist on some systems.
- Fixed a bug which caused periodic failures in python discovery when executables named ``python`` were not present on the target ``$PATH``. `#2783 <https://github.com/pypa/pipenv/issues/2783>`_
@@ -697,13 +697,13 @@ Bug Fixes
- Fixed an issue in ``delegator.py`` related to subprocess calls when using ``PopenSpawn`` to stream output, which sometimes threw unexpected ``EOF`` errors. `#3102 <https://github.com/pypa/pipenv/issues/3102>`_,
`#3114 <https://github.com/pypa/pipenv/issues/3114>`_,
`#3117 <https://github.com/pypa/pipenv/issues/3117>`_
- Fix the path casing issue that makes `pipenv clean` fail on Windows `#3104 <https://github.com/pypa/pipenv/issues/3104>`_
- Fix the path casing issue that makes ``pipenv clean`` fail on Windows `#3104 <https://github.com/pypa/pipenv/issues/3104>`_
- Pipenv will avoid leaving build artifacts in the current working directory. `#3106 <https://github.com/pypa/pipenv/issues/3106>`_
- Fixed issues with broken subprocess calls leaking resource handles and causing random and sporadic failures. `#3109 <https://github.com/pypa/pipenv/issues/3109>`_
- Fixed an issue which caused ``pipenv clean`` to sometimes clean packages from the base ``site-packages`` folder or fail entirely. `#3113 <https://github.com/pypa/pipenv/issues/3113>`_
- Updated ``pythonfinder`` to correct an issue with unnesting of nested paths when searching for python versions. `#3121 <https://github.com/pypa/pipenv/issues/3121>`_
- Added additional logic for ignoring and replacing non-ascii characters when formatting console output on non-UTF-8 systems. `#3131 <https://github.com/pypa/pipenv/issues/3131>`_
- Fix virtual environment discovery when ``PIPENV_VENV_IN_PROJECT`` is set, but the in-project `.venv` is a file. `#3134 <https://github.com/pypa/pipenv/issues/3134>`_
- Fix virtual environment discovery when ``PIPENV_VENV_IN_PROJECT`` is set, but the in-project ``.venv`` is a file. `#3134 <https://github.com/pypa/pipenv/issues/3134>`_
- Hashes for remote and local non-PyPI artifacts will now be included in ``Pipfile.lock`` during resolution. `#3145 <https://github.com/pypa/pipenv/issues/3145>`_
- Fix project path hashing logic in purpose to prevent collisions of virtual environments. `#3151 <https://github.com/pypa/pipenv/issues/3151>`_
- Fix package installation when the virtual environment path contains parentheses. `#3158 <https://github.com/pypa/pipenv/issues/3158>`_
@@ -787,7 +787,7 @@ Vendored Libraries
Features & Improvements
-----------------------
- Added environment variables `PIPENV_VERBOSE` and `PIPENV_QUIET` to control
- Added environment variables ``PIPENV_VERBOSE`` and ``PIPENV_QUIET`` to control
output verbosity without needing to pass options. `#2527 <https://github.com/pypa/pipenv/issues/2527>`_
- Updated test-PyPI add-on to better support json-API access (forward compatibility).
@@ -820,7 +820,7 @@ Behavior Changes
- Add ``COMSPEC`` to fallback option (along with ``SHELL`` and ``PYENV_SHELL``)
if shell detection fails, improving robustness on Windows. `#2651 <https://github.com/pypa/pipenv/issues/2651>`_
- Fallback to shell mode if `run` fails with Windows error 193 to handle non-executable commands. This should improve usability on Windows, where some users run non-executable files without specifying a command, relying on Windows file association to choose the current command. `#2718 <https://github.com/pypa/pipenv/issues/2718>`_
- Fallback to shell mode if ``run`` fails with Windows error 193 to handle non-executable commands. This should improve usability on Windows, where some users run non-executable files without specifying a command, relying on Windows file association to choose the current command. `#2718 <https://github.com/pypa/pipenv/issues/2718>`_
Bug Fixes
@@ -918,7 +918,7 @@ Bug Fixes
`#2867 <https://github.com/pypa/pipenv/issues/2867>`_,
`#2880 <https://github.com/pypa/pipenv/issues/2880>`_
- Fixed a bug where `pipenv` crashes when the `WORKON_HOME` directory does not exist. `#2877 <https://github.com/pypa/pipenv/issues/2877>`_
- Fixed a bug where ``pipenv`` crashes when the ``WORKON_HOME`` directory does not exist. `#2877 <https://github.com/pypa/pipenv/issues/2877>`_
- Fixed pip is not loaded from pipenv's patched one but the system one `#2912 <https://github.com/pypa/pipenv/issues/2912>`_
@@ -985,7 +985,7 @@ Improved Documentation
- Added simple example to README.md for installing from git. `#2685 <https://github.com/pypa/pipenv/issues/2685>`_
- Stopped recommending `--system` for Docker contexts. `#2762 <https://github.com/pypa/pipenv/issues/2762>`_
- Stopped recommending ``--system`` for Docker contexts. `#2762 <https://github.com/pypa/pipenv/issues/2762>`_
- Fixed the example url for doing "pipenv install -e
some-repository-url#egg=something", it was missing the "egg=" in the fragment
@@ -993,7 +993,7 @@ Improved Documentation
- Fixed link to the "be cordial" essay in the contribution documentation. `#2793 <https://github.com/pypa/pipenv/issues/2793>`_
- Clarify `pipenv install` documentation `#2844 <https://github.com/pypa/pipenv/issues/2844>`_
- Clarify ``pipenv install`` documentation `#2844 <https://github.com/pypa/pipenv/issues/2844>`_
- Replace reference to uservoice with PEEP-000 `#2909 <https://github.com/pypa/pipenv/issues/2909>`_
+3
View File
@@ -5,7 +5,10 @@ click = "*"
pytest_pypi = {path = "./tests/pytest-pypi", editable = true}
stdeb = {version="*", markers="sys_platform == 'linux'"}
dataclasses = {version="*", markers="python_version < '3.7'"}
importlib-resources = {version = "*", markers = "python_version < '3.7'"}
sphinxcontrib-spelling = "<4.3.0"
pre-commit = "*"
atomicwrites = {version = "*", markers="sys_platform == 'win32'"}
[packages]
Generated
+292 -180
View File
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "b6632ccfba082244f188747d88665264be87621552d2c1bbebaf36174bc24e8a"
"sha256": "0d25587c8b692005c51421eb35b08c878ca1d6e14e175d3c9ed74fd4d637476d"
},
"pipfile-spec": 6,
"requires": {},
@@ -39,11 +39,11 @@
},
"attrs": {
"hashes": [
"sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
"sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"
"sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4",
"sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==21.2.0"
"version": "==21.4.0"
},
"babel": {
"hashes": [
@@ -53,14 +53,6 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.9.1"
},
"backports.entry-points-selectable": {
"hashes": [
"sha256:7fceed9532a7aa2bd888654a7314f864a3c16a4e710b34a58cfc0f08114c663b",
"sha256:914b21a479fde881635f7af5adc7f6e38d6b274be32269070c53b698c60d5386"
],
"markers": "python_version >= '2.7'",
"version": "==1.1.1"
},
"beautifulsoup4": {
"hashes": [
"sha256:9a315ce70049920ea4572a4055bc4bd700c940521d36fc858205ad4fcde149bf",
@@ -71,11 +63,11 @@
},
"black": {
"hashes": [
"sha256:0b1f66cbfadcd332ceeaeecf6373d9991d451868d2e2219ad0ac1213fb701117",
"sha256:83f3852301c8dcb229e9c444dd79f573c8d31c7c2dad9bbaaa94c808630e32aa"
"sha256:77b80f693a569e2e527958459634f18df9b0ba2625ba4e0c2d5da5be42e6f2b3",
"sha256:a615e69ae185e08fdd73e4715e260e2479c861b5740057fde6e8b4e3b7dd589f"
],
"markers": "python_full_version >= '3.6.2'",
"version": "==21.11b0"
"version": "==21.12b0"
},
"bleach": {
"hashes": [
@@ -98,13 +90,76 @@
],
"version": "==2021.10.8"
},
"cffi": {
"hashes": [
"sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3",
"sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2",
"sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636",
"sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20",
"sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728",
"sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27",
"sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66",
"sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443",
"sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0",
"sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7",
"sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39",
"sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605",
"sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a",
"sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37",
"sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029",
"sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139",
"sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc",
"sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df",
"sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14",
"sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880",
"sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2",
"sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a",
"sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e",
"sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474",
"sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024",
"sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8",
"sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0",
"sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e",
"sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a",
"sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e",
"sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032",
"sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6",
"sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e",
"sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b",
"sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e",
"sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954",
"sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962",
"sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c",
"sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4",
"sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55",
"sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962",
"sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023",
"sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c",
"sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6",
"sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8",
"sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382",
"sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7",
"sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc",
"sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997",
"sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"
],
"version": "==1.15.0"
},
"cfgv": {
"hashes": [
"sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426",
"sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"
],
"markers": "python_full_version >= '3.6.1'",
"version": "==3.3.1"
},
"charset-normalizer": {
"hashes": [
"sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0",
"sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b"
"sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597",
"sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"
],
"markers": "python_version >= '3.0'",
"version": "==2.0.7"
"version": "==2.0.12"
},
"click": {
"hashes": [
@@ -125,9 +180,35 @@
"sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b",
"sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"
],
"markers": "sys_platform == 'win32'",
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==0.4.4"
},
"cryptography": {
"hashes": [
"sha256:0a3bf09bb0b7a2c93ce7b98cb107e9170a90c51a0162a20af1c61c765b90e60b",
"sha256:1f64a62b3b75e4005df19d3b5235abd43fa6358d5516cfc43d87aeba8d08dd51",
"sha256:32db5cc49c73f39aac27574522cecd0a4bb7384e71198bc65a0d23f901e89bb7",
"sha256:4881d09298cd0b669bb15b9cfe6166f16fc1277b4ed0d04a22f3d6430cb30f1d",
"sha256:4e2dddd38a5ba733be6a025a1475a9f45e4e41139d1321f412c6b360b19070b6",
"sha256:53e0285b49fd0ab6e604f4c5d9c5ddd98de77018542e88366923f152dbeb3c29",
"sha256:70f8f4f7bb2ac9f340655cbac89d68c527af5bb4387522a8413e841e3e6628c9",
"sha256:7b2d54e787a884ffc6e187262823b6feb06c338084bbe80d45166a1cb1c6c5bf",
"sha256:7be666cc4599b415f320839e36367b273db8501127b38316f3b9f22f17a0b815",
"sha256:8241cac0aae90b82d6b5c443b853723bcc66963970c67e56e71a2609dc4b5eaf",
"sha256:82740818f2f240a5da8dfb8943b360e4f24022b093207160c77cadade47d7c85",
"sha256:8897b7b7ec077c819187a123174b645eb680c13df68354ed99f9b40a50898f77",
"sha256:c2c5250ff0d36fd58550252f54915776940e4e866f38f3a7866d92b32a654b86",
"sha256:ca9f686517ec2c4a4ce930207f75c00bf03d94e5063cbc00a1dc42531511b7eb",
"sha256:d2b3d199647468d410994dbeb8cec5816fb74feb9368aedf300af709ef507e3e",
"sha256:da73d095f8590ad437cd5e9faf6628a218aa7c387e1fdf67b888b47ba56a17f0",
"sha256:e167b6b710c7f7bc54e67ef593f8731e1f45aa35f8a8a7b72d6e42ec76afd4b3",
"sha256:ea634401ca02367c1567f012317502ef3437522e2fc44a3ea1844de028fa4b84",
"sha256:ec6597aa85ce03f3e507566b8bcdf9da2227ec86c4266bd5e6ab4d9e0cc8dab2",
"sha256:f64b232348ee82f13aac22856515ce0195837f6968aeaa94a3d0353ea2ec06a6"
],
"markers": "python_version >= '3.6'",
"version": "==36.0.2"
},
"dataclasses": {
"hashes": [
"sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf",
@@ -139,10 +220,10 @@
},
"distlib": {
"hashes": [
"sha256:c8b54e8454e5bf6237cc84c20e8264c3e991e824ef27e8f1e81049867d861e31",
"sha256:d982d0751ff6eaaab5e2ec8e691d949ee80eddf01a62eaa96ddb11531fe16b05"
"sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b",
"sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"
],
"version": "==0.3.3"
"version": "==0.3.4"
},
"docutils": {
"hashes": [
@@ -162,11 +243,11 @@
},
"filelock": {
"hashes": [
"sha256:2e139a228bcf56dd8b2274a65174d005c4a6b68540ee0bdbb92c76f43f29f7e8",
"sha256:93d512b32a23baf4cac44ffd72ccf70732aeff7b8050fcaf6d3ec406d954baf4"
"sha256:0f12f552b42b5bf60dba233710bf71337d35494fc8bdd4fd6d9f6d082ad45e06",
"sha256:a4bc51381e01502a30e9f06dd4fa19a1712eab852b6fb0f84fd7cce0793d8ca3"
],
"markers": "python_version >= '3.6'",
"version": "==3.4.0"
"version": "==3.4.1"
},
"flake8": {
"hashes": [
@@ -186,11 +267,19 @@
},
"flask": {
"hashes": [
"sha256:7b2fb8e934ddd50731893bdcdb00fc8c0315916f9fcd50d22c7cc1a95ab634e2",
"sha256:cb90f62f1d8e4dc4621f52106613488b5ba826b2e1e10a33eac92f723093ab6a"
"sha256:59da8a3170004800a2837844bfa84d49b022550616070f7cb1a659682b2e7c9f",
"sha256:e1120c228ca2f553b470df4a5fa927ab66258467526069981b3eb0a91902687d"
],
"markers": "python_version >= '3.6'",
"version": "==2.0.2"
"version": "==2.0.3"
},
"identify": {
"hashes": [
"sha256:6b4b5031f69c48bf93a646b90de9b381c6b5f560df4cbe0ed3cf7650ae741e4d",
"sha256:aa68609c7454dbcaae60a01ff6b8df1de9b39fe6e50b1f6107ec81dcda624aa6"
],
"markers": "python_full_version >= '3.6.1'",
"version": "==2.4.4"
},
"idna": {
"hashes": [
@@ -210,19 +299,20 @@
},
"importlib-metadata": {
"hashes": [
"sha256:53ccfd5c134223e497627b9815d5030edf77d2ed573922f7a0b8f8bb81a1c100",
"sha256:75bdec14c397f528724c1bfd9709d660b33a4d2e77387a3358f20b848bb5e5fb"
"sha256:65a9576a5b2d58ca44d133c42a241905cc45e34d2c06fd5ba2bafa221e5d7b5e",
"sha256:766abffff765960fcc18003801f7044eb6755ffae4521c8e8ce8e83b9c9b0668"
],
"markers": "python_version < '3.8'",
"version": "==4.8.2"
"version": "==4.8.3"
},
"importlib-resources": {
"hashes": [
"sha256:33a95faed5fc19b4bc16b29a6eeae248a3fe69dd55d4d229d2b480e23eeaad45",
"sha256:d756e2f85dd4de2ba89be0b21dba2a3bbec2e871a42a3a16719258a11f87506b"
"sha256:203d70dda34cfbfbb42324a8d4211196e7d3e858de21a5eb68c6d1cdd99e4e98",
"sha256:ae35ed1cfe8c0d6c1a53ecd168167f01fa93b893d51a62cdf23aea044c67211b"
],
"index": "pypi",
"markers": "python_version < '3.7'",
"version": "==5.4.0"
"version": "==5.2.3"
},
"incremental": {
"hashes": [
@@ -240,11 +330,10 @@
},
"invoke": {
"hashes": [
"sha256:374d1e2ecf78981da94bfaf95366216aaec27c2d6a7b7d5818d92da55aa258d3",
"sha256:769e90caeb1bd07d484821732f931f1ad8916a38e3f3e618644687fc09cb6317",
"sha256:e6c9917a1e3e73e7ea91fdf82d5f151ccfe85bf30cc65cdb892444c02dbb5f74"
"sha256:a5159fc63dba6ca2a87a1e33d282b99cea69711b03c64a35bb4e1c53c6c4afa0",
"sha256:e332e49de40463f2016315f51df42313855772be86435686156bc18f45b5cc6c"
],
"version": "==1.6.0"
"version": "==1.7.0"
},
"itsdangerous": {
"hashes": [
@@ -254,6 +343,14 @@
"markers": "python_version >= '3.6'",
"version": "==2.0.1"
},
"jeepney": {
"hashes": [
"sha256:1b5a0ea5c0e7b166b2f5895b91a08c14de8915afda4407fb5022a195224958ac",
"sha256:fa9e232dfa0c498bd0b8a3a73b8d8a31978304dcef0515adc859d4e096f96f4f"
],
"markers": "sys_platform == 'linux'",
"version": "==0.7.1"
},
"jinja2": {
"hashes": [
"sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8",
@@ -264,11 +361,11 @@
},
"keyring": {
"hashes": [
"sha256:6334aee6073db2fb1f30892697b1730105b5e9a77ce7e61fca6b435225493efe",
"sha256:bd2145a237ed70c8ce72978b497619ddfcae640b6dcf494402d5143e37755c6e"
"sha256:17e49fb0d6883c2b4445359434dba95aad84aabb29bbff044ad0ed7100232eca",
"sha256:89cbd74d4683ed164c8082fb38619341097741323b3786905c6dac04d6915a55"
],
"markers": "python_version >= '3.6'",
"version": "==23.2.1"
"version": "==23.4.1"
},
"markupsafe": {
"hashes": [
@@ -367,13 +464,20 @@
],
"version": "==0.4.3"
},
"nodeenv": {
"hashes": [
"sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b",
"sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7"
],
"version": "==1.6.0"
},
"packaging": {
"hashes": [
"sha256:096d689d78ca690e4cd8a89568ba06d07ca097e3306a4381635073ca91479966",
"sha256:14317396d1e8cdb122989b916fa2c7e9ca8e2be9e8060a6eff75b6b7b4d8a7e0"
"sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb",
"sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"
],
"markers": "python_version >= '3.6'",
"version": "==21.2"
"version": "==21.3"
},
"parver": {
"hashes": [
@@ -408,10 +512,10 @@
},
"pkginfo": {
"hashes": [
"sha256:37ecd857b47e5f55949c41ed061eb51a0bee97a87c969219d144c0e023982779",
"sha256:e7432f81d08adec7297633191bbf0bd47faf13cd8724c3a13250e51d542635bd"
"sha256:542e0d0b6750e2e21c20179803e40ab50598d8066d51097a0e382cba9eb02bff",
"sha256:c24c487c6a7f72c66e816ab1796b96ac6c3d14d49338293d2141664330b55ffc"
],
"version": "==1.7.1"
"version": "==1.8.2"
},
"platformdirs": {
"hashes": [
@@ -429,6 +533,14 @@
"markers": "python_version >= '3.6'",
"version": "==1.0.0"
},
"pre-commit": {
"hashes": [
"sha256:725fa7459782d7bec5ead072810e47351de01709be838c2ce1726b9591dad616",
"sha256:c1a8040ff15ad3d648c70cc3e55b93e4d2d5b687320955505587fd79bbaed06a"
],
"index": "pypi",
"version": "==2.17.0"
},
"py": {
"hashes": [
"sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719",
@@ -445,6 +557,13 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.7.0"
},
"pycparser": {
"hashes": [
"sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9",
"sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"
],
"version": "==2.21"
},
"pyenchant": {
"hashes": [
"sha256:1cf830c6614362a78aab78d50eaf7c6c93831369c52e1bb64ffae1df0341e637",
@@ -465,35 +584,35 @@
},
"pygments": {
"hashes": [
"sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380",
"sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6"
"sha256:44238f1b60a76d78fc8ca0528ee429702aae011c265fe6a8dd8b63049ae41c65",
"sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a"
],
"markers": "python_version >= '3.5'",
"version": "==2.10.0"
"version": "==2.11.2"
},
"pyparsing": {
"hashes": [
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
"sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea",
"sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.4.7"
"markers": "python_version >= '3.6'",
"version": "==3.0.7"
},
"pytest": {
"hashes": [
"sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89",
"sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"
"sha256:9ce3ff477af913ecf6321fe337b93a2c0dcf2a0a1439c43f5452112c1e4280db",
"sha256:e30905a0c131d3d94b89624a1cc5afec3e0ba2fbdb151867d8e0ebd49850f171"
],
"markers": "python_version >= '3.6'",
"version": "==6.2.5"
"version": "==7.0.1"
},
"pytest-forked": {
"hashes": [
"sha256:6aa9ac7e00ad1a539c41bec6d21011332de671e938c7637378ec9710204e37ca",
"sha256:dc4147784048e70ef5d437951728825a131b81714b398d5d52f17c7c144d8815"
"sha256:8b67587c8f98cbbadfdd804539ed5455b6ed03802203485dd2f53c1422d7440e",
"sha256:bbbb6717efc886b9d64537b41fb1497cfaf3c9601276be8da2cccfea5a3c8ad8"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==1.3.0"
"markers": "python_version >= '3.6'",
"version": "==1.4.0"
},
"pytest-pypi": {
"editable": true,
@@ -501,19 +620,19 @@
},
"pytest-timeout": {
"hashes": [
"sha256:329bdea323d3e5bea4737070dd85a0d1021dbecb2da5342dc25284fdb929dff0",
"sha256:a5ec4eceddb8ea726911848593d668594107e797621e97f93a1d1dbc6fbb9080"
"sha256:c07ca07404c612f8abbe22294b23c368e2e5104b521c1790195561f37e1ac3d9",
"sha256:f6f50101443ce70ad325ceb4473c4255e9d74e3c7cd0ef827309dfa4c0d975c6"
],
"markers": "python_version >= '3.6'",
"version": "==2.0.1"
"version": "==2.1.0"
},
"pytest-xdist": {
"hashes": [
"sha256:7b61ebb46997a0820a263553179d6d1e25a8c50d8a8620cd1aa1e20e3be99168",
"sha256:89b330316f7fc475f999c81b577c2b926c9569f3d397ae432c0c2e2496d61ff9"
"sha256:4580deca3ff04ddb2ac53eba39d76cb5dd5edeac050cb6fbc768b0dd712b4edf",
"sha256:6fe5c74fec98906deb8f2d2b616b5c782022744978e7bd4695d39c8f42d0ce65"
],
"markers": "python_version >= '3.6'",
"version": "==2.4.0"
"version": "==2.5.0"
},
"pytz": {
"hashes": [
@@ -522,82 +641,60 @@
],
"version": "==2021.3"
},
"pywin32-ctypes": {
"pyyaml": {
"hashes": [
"sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942",
"sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"
"sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293",
"sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b",
"sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57",
"sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b",
"sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4",
"sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07",
"sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba",
"sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9",
"sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287",
"sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513",
"sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0",
"sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0",
"sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92",
"sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f",
"sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2",
"sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc",
"sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c",
"sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86",
"sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4",
"sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c",
"sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34",
"sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b",
"sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c",
"sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb",
"sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737",
"sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3",
"sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d",
"sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53",
"sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78",
"sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803",
"sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a",
"sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174",
"sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"
],
"markers": "sys_platform == 'win32'",
"version": "==0.2.0"
"markers": "python_version >= '3.6'",
"version": "==6.0"
},
"readme-renderer": {
"hashes": [
"sha256:3286806450d9961d6e3b5f8a59f77e61503799aca5155c8d8d40359b4e1e1adc",
"sha256:8299700d7a910c304072a7601eafada6712a5b011a20139417e1b1e9f04645d8"
"sha256:262510fe6aae81ed4e94d8b169077f325614c0b1a45916a80442c6576264a9c2",
"sha256:dfb4d17f21706d145f7473e0b61ca245ba58e810cf9b2209a48239677f82e5b0"
],
"version": "==30.0"
},
"regex": {
"hashes": [
"sha256:05b7d6d7e64efe309972adab77fc2af8907bb93217ec60aa9fe12a0dad35874f",
"sha256:0617383e2fe465732af4509e61648b77cbe3aee68b6ac8c0b6fe934db90be5cc",
"sha256:07856afef5ffcc052e7eccf3213317fbb94e4a5cd8177a2caa69c980657b3cb4",
"sha256:162abfd74e88001d20cb73ceaffbfe601469923e875caf9118333b1a4aaafdc4",
"sha256:2207ae4f64ad3af399e2d30dde66f0b36ae5c3129b52885f1bffc2f05ec505c8",
"sha256:30ab804ea73972049b7a2a5c62d97687d69b5a60a67adca07eb73a0ddbc9e29f",
"sha256:3b5df18db1fccd66de15aa59c41e4f853b5df7550723d26aa6cb7f40e5d9da5a",
"sha256:3c5fb32cc6077abad3bbf0323067636d93307c9fa93e072771cf9a64d1c0f3ef",
"sha256:416c5f1a188c91e3eb41e9c8787288e707f7d2ebe66e0a6563af280d9b68478f",
"sha256:432bd15d40ed835a51617521d60d0125867f7b88acf653e4ed994a1f8e4995dc",
"sha256:4aaa4e0705ef2b73dd8e36eeb4c868f80f8393f5f4d855e94025ce7ad8525f50",
"sha256:537ca6a3586931b16a85ac38c08cc48f10fc870a5b25e51794c74df843e9966d",
"sha256:53db2c6be8a2710b359bfd3d3aa17ba38f8aa72a82309a12ae99d3c0c3dcd74d",
"sha256:5537f71b6d646f7f5f340562ec4c77b6e1c915f8baae822ea0b7e46c1f09b733",
"sha256:6650f16365f1924d6014d2ea770bde8555b4a39dc9576abb95e3cd1ff0263b36",
"sha256:666abff54e474d28ff42756d94544cdfd42e2ee97065857413b72e8a2d6a6345",
"sha256:68a067c11463de2a37157930d8b153005085e42bcb7ad9ca562d77ba7d1404e0",
"sha256:780b48456a0f0ba4d390e8b5f7c661fdd218934388cde1a974010a965e200e12",
"sha256:788aef3549f1924d5c38263104dae7395bf020a42776d5ec5ea2b0d3d85d6646",
"sha256:7ee1227cf08b6716c85504aebc49ac827eb88fcc6e51564f010f11a406c0a667",
"sha256:7f301b11b9d214f83ddaf689181051e7f48905568b0c7017c04c06dfd065e244",
"sha256:83ee89483672b11f8952b158640d0c0ff02dc43d9cb1b70c1564b49abe92ce29",
"sha256:85bfa6a5413be0ee6c5c4a663668a2cad2cbecdee367630d097d7823041bdeec",
"sha256:9345b6f7ee578bad8e475129ed40123d265464c4cfead6c261fd60fc9de00bcf",
"sha256:93a5051fcf5fad72de73b96f07d30bc29665697fb8ecdfbc474f3452c78adcf4",
"sha256:962b9a917dd7ceacbe5cd424556914cb0d636001e393b43dc886ba31d2a1e449",
"sha256:98ba568e8ae26beb726aeea2273053c717641933836568c2a0278a84987b2a1a",
"sha256:a3feefd5e95871872673b08636f96b61ebef62971eab044f5124fb4dea39919d",
"sha256:b43c2b8a330a490daaef5a47ab114935002b13b3f9dc5da56d5322ff218eeadb",
"sha256:b483c9d00a565633c87abd0aaf27eb5016de23fed952e054ecc19ce32f6a9e7e",
"sha256:ba05430e819e58544e840a68b03b28b6d328aff2e41579037e8bab7653b37d83",
"sha256:ca5f18a75e1256ce07494e245cdb146f5a9267d3c702ebf9b65c7f8bd843431e",
"sha256:d5ca078bb666c4a9d1287a379fe617a6dccd18c3e8a7e6c7e1eb8974330c626a",
"sha256:da1a90c1ddb7531b1d5ff1e171b4ee61f6345119be7351104b67ff413843fe94",
"sha256:dba70f30fd81f8ce6d32ddeef37d91c8948e5d5a4c63242d16a2b2df8143aafc",
"sha256:dd33eb9bdcfbabab3459c9ee651d94c842bc8a05fabc95edf4ee0c15a072495e",
"sha256:e0538c43565ee6e703d3a7c3bdfe4037a5209250e8502c98f20fea6f5fdf2965",
"sha256:e1f54b9b4b6c53369f40028d2dd07a8c374583417ee6ec0ea304e710a20f80a0",
"sha256:e32d2a2b02ccbef10145df9135751abea1f9f076e67a4e261b05f24b94219e36",
"sha256:e71255ba42567d34a13c03968736c5d39bb4a97ce98188fafb27ce981115beec",
"sha256:ed2e07c6a26ed4bea91b897ee2b0835c21716d9a469a96c3e878dc5f8c55bb23",
"sha256:eef2afb0fd1747f33f1ee3e209bce1ed582d1896b240ccc5e2697e3275f037c7",
"sha256:f23222527b307970e383433daec128d769ff778d9b29343fb3496472dc20dabe",
"sha256:f341ee2df0999bfdf7a95e448075effe0db212a59387de1a70690e4acb03d4c6",
"sha256:f7f325be2804246a75a4f45c72d4ce80d2443ab815063cdf70ee8fb2ca59ee1b",
"sha256:f8af619e3be812a2059b212064ea7a640aff0568d972cd1b9e920837469eb3cb",
"sha256:fa8c626d6441e2d04b6ee703ef2d1e17608ad44c7cb75258c09dd42bacdfc64b",
"sha256:fbb9dc00e39f3e6c0ef48edee202f9520dafb233e8b51b06b8428cfcb92abd30",
"sha256:fff55f3ce50a3ff63ec8e2a8d3dd924f1941b250b0aac3d3d42b687eeff07a8e"
],
"version": "==2021.11.10"
"markers": "python_version >= '3.6'",
"version": "==34.0"
},
"requests": {
"hashes": [
"sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24",
"sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"
"sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61",
"sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==2.26.0"
"version": "==2.27.1"
},
"requests-toolbelt": {
"hashes": [
@@ -611,15 +708,24 @@
"sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835",
"sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"
],
"markers": "python_version >= '3.7'",
"version": "==1.5.0"
},
"secretstorage": {
"hashes": [
"sha256:422d82c36172d88d6a0ed5afdec956514b189ddbfb72fefab0c8a1cee4eaf71f",
"sha256:fd666c51a6bf200643495a04abb261f83229dcb6fd8472ec393df7ffc8b6f195"
],
"markers": "sys_platform == 'linux'",
"version": "==3.3.1"
},
"setuptools": {
"hashes": [
"sha256:94ee891f4759150cded601a6beb6b08400413aefd0267b692f3f8c6e0bb238e7",
"sha256:fb537610c2dfe77b5896e3ee53dd53fbdd9adc48076c8f28cee3a30fb59a5038"
"sha256:22c7348c6d2976a52632c67f7ab0cdf40147db7789f9aed18734643fe9cf3373",
"sha256:4ce92f1e1f8f01233ee9952c04f6b81d1e02939d6e1b488428154974a4d0783e"
],
"markers": "python_version >= '3.6'",
"version": "==59.1.1"
"version": "==59.6.0"
},
"six": {
"hashes": [
@@ -720,6 +826,7 @@
"hashes": [
"sha256:08c22c9c03b28a140fe3ec5064b53a5288279f22e596ca06b0be698d50c93cf2"
],
"index": "pypi",
"markers": "sys_platform == 'linux'",
"version": "==0.10.0"
},
@@ -733,83 +840,88 @@
},
"tomli": {
"hashes": [
"sha256:c6ce0015eb38820eaf32b5db832dbc26deb3dd427bd5f6556cf0acac2c214fee",
"sha256:f04066f68f5554911363063a30b108d2b5a5b1a010aa8b6132af78489fe3aade"
"sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f",
"sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"
],
"markers": "python_version >= '3.6'",
"version": "==1.2.2"
"version": "==1.2.3"
},
"towncrier": {
"hashes": [
"sha256:930454ab86da25aae01bf0f37927e565d9366b12b948d5f533c7c7641dba7b16",
"sha256:bfc86ad9dd28c53dfefbb6d7d33875a8f459c5491e18857be4bcff096b3192c3"
"sha256:9cb6f45c16e1a1eec9d0e7651165e7be60cd0ab81d13a5c96ca97a498ae87f48",
"sha256:fc5a88a2a54988e3a8ed2b60d553599da8330f65722cc607c839614ed87e0f92"
],
"version": "==21.9.0rc1"
"version": "==21.9.0"
},
"tqdm": {
"hashes": [
"sha256:8dd278a422499cd6b727e6ae4061c40b48fce8b76d1ccbf5d34fca9b7f925b0c",
"sha256:d359de7217506c9851b7869f3708d8ee53ed70a1b8edbba4dbcb47442592920d"
"sha256:1d9835ede8e394bb8c9dcbffbca02d717217113adc679236873eeaac5bc0b3cd",
"sha256:e643e071046f17139dea55b880dc9b33822ce21613b4a4f5ea57f202833dbc29"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==4.62.3"
"version": "==4.63.0"
},
"twine": {
"hashes": [
"sha256:4caad5ef4722e127b3749052fcbffaaf71719b19d4fd4973b29c469957adeba2",
"sha256:916070f8ecbd1985ebed5dbb02b9bda9a092882a96d7069d542d4fc0bb5c673c"
"sha256:8efa52658e0ae770686a13b675569328f1fba9837e5de1867bfe5f46a9aefe19",
"sha256:d0550fca9dc19f3d5e8eadfce0c227294df0a2a951251a4385797c8a6198b7c8"
],
"markers": "python_version >= '3.6'",
"version": "==3.6.0"
"version": "==3.8.0"
},
"typed-ast": {
"hashes": [
"sha256:14fed8820114a389a2b7e91624db5f85f3f6682fda09fe0268a59aabd28fe5f5",
"sha256:155b74b078be842d2eb630dd30a280025eca0a5383c7d45853c27afee65f278f",
"sha256:224afecb8b39739f5c9562794a7c98325cb9d972712e1a98b6989a4720219541",
"sha256:361b9e5d27bd8e3ccb6ea6ad6c4f3c0be322a1a0f8177db6d56264fa0ae40410",
"sha256:37ba2ab65a0028b1a4f2b61a8fe77f12d242731977d274a03d68ebb751271508",
"sha256:49af5b8f6f03ed1eb89ee06c1d7c2e7c8e743d720c3746a5857609a1abc94c94",
"sha256:51040bf45aacefa44fa67fb9ebcd1f2bec73182b99a532c2394eea7dabd18e24",
"sha256:52ca2b2b524d770bed7a393371a38e91943f9160a190141e0df911586066ecda",
"sha256:618912cbc7e17b4aeba86ffe071698c6e2d292acbd6d1d5ec1ee724b8c4ae450",
"sha256:65c81abbabda7d760df7304d843cc9dbe7ef5d485504ca59a46ae2d1731d2428",
"sha256:7b310a207ee9fde3f46ba327989e6cba4195bc0c8c70a158456e7b10233e6bed",
"sha256:7e6731044f748340ef68dcadb5172a4b1f40847a2983fe3983b2a66445fbc8e6",
"sha256:806e0c7346b9b4af8c62d9a29053f484599921a4448c37fbbcbbf15c25138570",
"sha256:a67fd5914603e2165e075f1b12f5a8356bfb9557e8bfb74511108cfbab0f51ed",
"sha256:e4374a76e61399a173137e7984a1d7e356038cf844f24fd8aea46c8029a2f712",
"sha256:e8a9b9c87801cecaad3b4c2b8876387115d1a14caa602c1618cedbb0cb2a14e6",
"sha256:ea517c2bb11c5e4ba7a83a91482a2837041181d57d3ed0749a6c382a2b6b7086",
"sha256:ec184dfb5d3d11e82841dbb973e7092b75f306b625fad7b2e665b64c5d60ab3f",
"sha256:ff4ad88271aa7a55f19b6a161ed44e088c393846d954729549e3cde8257747bb"
"sha256:0eb77764ea470f14fcbb89d51bc6bbf5e7623446ac4ed06cbd9ca9495b62e36e",
"sha256:1098df9a0592dd4c8c0ccfc2e98931278a6c6c53cb3a3e2cf7e9ee3b06153344",
"sha256:183b183b7771a508395d2cbffd6db67d6ad52958a5fdc99f450d954003900266",
"sha256:18fe320f354d6f9ad3147859b6e16649a0781425268c4dde596093177660e71a",
"sha256:26a432dc219c6b6f38be20a958cbe1abffcc5492821d7e27f08606ef99e0dffd",
"sha256:294a6903a4d087db805a7656989f613371915fc45c8cc0ddc5c5a0a8ad9bea4d",
"sha256:31d8c6b2df19a777bc8826770b872a45a1f30cfefcfd729491baa5237faae837",
"sha256:33b4a19ddc9fc551ebabca9765d54d04600c4a50eda13893dadf67ed81d9a098",
"sha256:42c47c3b43fe3a39ddf8de1d40dbbfca60ac8530a36c9b198ea5b9efac75c09e",
"sha256:525a2d4088e70a9f75b08b3f87a51acc9cde640e19cc523c7e41aa355564ae27",
"sha256:58ae097a325e9bb7a684572d20eb3e1809802c5c9ec7108e85da1eb6c1a3331b",
"sha256:676d051b1da67a852c0447621fdd11c4e104827417bf216092ec3e286f7da596",
"sha256:74cac86cc586db8dfda0ce65d8bcd2bf17b58668dfcc3652762f3ef0e6677e76",
"sha256:8c08d6625bb258179b6e512f55ad20f9dfef019bbfbe3095247401e053a3ea30",
"sha256:90904d889ab8e81a956f2c0935a523cc4e077c7847a836abee832f868d5c26a4",
"sha256:963a0ccc9a4188524e6e6d39b12c9ca24cc2d45a71cfdd04a26d883c922b4b78",
"sha256:bbebc31bf11762b63bf61aaae232becb41c5bf6b3461b80a4df7e791fabb3aca",
"sha256:bc2542e83ac8399752bc16e0b35e038bdb659ba237f4222616b4e83fb9654985",
"sha256:c29dd9a3a9d259c9fa19d19738d021632d673f6ed9b35a739f48e5f807f264fb",
"sha256:c7407cfcad702f0b6c0e0f3e7ab876cd1d2c13b14ce770e412c0c4b9728a0f88",
"sha256:da0a98d458010bf4fe535f2d1e367a2e2060e105978873c04c04212fb20543f7",
"sha256:df05aa5b241e2e8045f5f4367a9f6187b09c4cdf8578bb219861c4e27c443db5",
"sha256:f290617f74a610849bd8f5514e34ae3d09eafd521dceaa6cf68b3f4414266d4e",
"sha256:f30ddd110634c2d7534b2d4e0e22967e88366b0d356b24de87419cc4410c41b7"
],
"markers": "python_version < '3.8' and implementation_name == 'cpython'",
"version": "==1.5.0"
"version": "==1.5.2"
},
"typing-extensions": {
"hashes": [
"sha256:2cdf80e4e04866a9b3689a51869016d36db0814d84b8d8a568d22781d45d27ed",
"sha256:829704698b22e13ec9eaf959122315eabb370b0884400e9818334d8b677023d9"
"sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42",
"sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"
],
"markers": "python_version >= '3.6'",
"version": "==4.0.0"
"version": "==4.1.1"
},
"urllib3": {
"hashes": [
"sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece",
"sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"
"sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14",
"sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
"version": "==1.26.7"
"version": "==1.26.9"
},
"virtualenv": {
"hashes": [
"sha256:4b02e52a624336eece99c96e3ab7111f469c24ba226a53ec474e8e787b365814",
"sha256:576d05b46eace16a9c348085f7d0dc8ef28713a2cabaa1cf0aea41e8f12c9218"
"sha256:c3e01300fb8495bc00ed70741f5271fc95fed067eb7106297be73d30879af60c",
"sha256:ce8901d3bbf3b90393498187f2d56797a8a452fb2d0d7efc6fd837554d6f679c"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==20.10.0"
"version": "==20.13.4"
},
"virtualenv-clone": {
"hashes": [
@@ -828,18 +940,18 @@
},
"werkzeug": {
"hashes": [
"sha256:63d3dc1cf60e7b7e35e97fa9861f7397283b75d765afcaefd993d6046899de8f",
"sha256:aa2bb6fc8dee8d6c504c0ac1e7f5f7dc5810a9903e793b6f715a9f015bdadb9a"
"sha256:1421ebfc7648a39a5c58c601b154165d05cf47a3cd0ccb70857cbdacf6c8f2b8",
"sha256:b863f8ff057c522164b6067c9e28b041161b4be5ba4d0daceeaa50a163822d3c"
],
"markers": "python_version >= '3.6'",
"version": "==2.0.2"
"version": "==2.0.3"
},
"zipp": {
"hashes": [
"sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832",
"sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"
],
"markers": "python_version >= '3.6'",
"markers": "python_version < '3.10'",
"version": "==3.6.0"
}
}
+7 -7
View File
@@ -274,7 +274,7 @@ Example::
.. note::
Each month, `PyUp.io` updates the ``safety`` database of
Each month, `PyUp.io`_ updates the ``safety`` database of
insecure Python packages and `makes it available to the
community for free <https://pyup.io/safety/>`__. Pipenv
makes an API call to retrieve those results and use them
@@ -380,7 +380,7 @@ If a ``.env`` file is present in your project, ``$ pipenv shell`` and ``$ pipenv
>>> os.environ['HELLO']
'WORLD'
Shell like variable expansion is available in ``.env`` files using `${VARNAME}` syntax.::
Shell like variable expansion is available in ``.env`` files using ``${VARNAME}`` syntax.::
$ cat .env
CONFIG_PATH=${HOME}/.config/foo
@@ -591,15 +591,15 @@ Magic shell completions are now enabled!
It's reasonably common for platform specific Python bindings for
operating system interfaces to only be available through the system
package manager, and hence unavailable for installation into virtual
environments with `pip`. In these cases, the virtual environment can
be created with access to the system `site-packages` directory::
environments with ``pip``. In these cases, the virtual environment can
be created with access to the system ``site-packages`` directory::
$ pipenv --three --site-packages
To ensure that all `pip`-installable components actually are installed
To ensure that all ``pip``-installable components actually are installed
into the virtual environment and system packages are only used for
interfaces that don't participate in Python-level dependency resolution
at all, use the `PIP_IGNORE_INSTALLED` setting::
at all, use the ``PIP_IGNORE_INSTALLED`` setting::
$ PIP_IGNORE_INSTALLED=1 pipenv install --dev
@@ -618,7 +618,7 @@ Libraries are ultimately meant to be used in some **application**. Applications
To summarize:
- For libraries, define **abstract dependencies** via ``install_requires`` in ``setup.py``. The decision of which version exactly to be installed and where to obtain that dependency is not yours to make!
- For applications, define **dependencies and where to get them** in the `Pipfile` and use this file to update the set of **concrete dependencies** in ``Pipfile.lock``. This file defines a specific idempotent environment that is known to work for your project. The ``Pipfile.lock`` is your source of truth. The ``Pipfile`` is a convenience for you to create that lock-file, in that it allows you to still remain somewhat vague about the exact version of a dependency to be used. Pipenv is there to help you define a working conflict-free set of specific dependency-versions, which would otherwise be a very tedious task.
- For applications, define **dependencies and where to get them** in the ``Pipfile`` and use this file to update the set of **concrete dependencies** in ``Pipfile.lock``. This file defines a specific idempotent environment that is known to work for your project. The ``Pipfile.lock`` is your source of truth. The ``Pipfile`` is a convenience for you to create that lock-file, in that it allows you to still remain somewhat vague about the exact version of a dependency to be used. Pipenv is there to help you define a working conflict-free set of specific dependency-versions, which would otherwise be a very tedious task.
- Of course, ``Pipfile`` and Pipenv are still useful for library developers, as they can be used to define a development or test environment.
- And, of course, there are projects for which the distinction between library and application isn't that clear. In that case, use ``install_requires`` alongside Pipenv and ``Pipfile``.
+1 -1
View File
@@ -132,7 +132,7 @@ Example Pipfile.lock
- Generally, keep both ``Pipfile`` and ``Pipfile.lock`` in version control.
- Do not keep ``Pipfile.lock`` in version control if multiple versions of Python are being targeted.
- Specify your target Python version in your `Pipfile`'s ``[requires]`` section. Ideally, you should only have one target Python version, as this is a deployment tool. ``python_version`` should be in the format ``X.Y`` (or ``X``) and ``python_full_version`` should be in ``X.Y.Z`` format.
- Specify your target Python version in your ``Pipfile``'s ``[requires]`` section. Ideally, you should only have one target Python version, as this is a deployment tool. ``python_version`` should be in the format ``X.Y`` (or ``X``) and ``python_full_version`` should be in ``X.Y.Z`` format.
- ``pipenv install`` is fully compatible with ``pip install`` syntax, for which the full documentation can be found `here <https://pip.pypa.io/en/stable/user_guide/#installing-packages>`_.
- Note that the ``Pipfile`` uses the `TOML Spec <https://github.com/toml-lang/toml#user-content-spec>`_.
+44 -40
View File
@@ -34,38 +34,38 @@ with open(os.path.join(here, "..", "pipenv", "__version__.py")) as f:
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.todo',
'sphinx.ext.coverage',
'sphinx.ext.viewcode',
'sphinx_click.ext',
"sphinx.ext.autodoc",
"sphinx.ext.todo",
"sphinx.ext.coverage",
"sphinx.ext.viewcode",
"sphinx_click.ext",
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
source_suffix = ".rst"
# The master toctree document.
master_doc = 'index'
master_doc = "index"
# General information about the project.
project = 'pipenv'
project = "pipenv"
copyright = '2020. A project founded by <a href="http://kennethreitz.com/pages/open-projects.html">Kenneth Reitz</a>'
author = 'Python Packaging Authority'
author = "Python Packaging Authority"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = about['__version__']
version = about["__version__"]
# The full version, including alpha/beta/rc tags.
release = about['__version__']
release = about["__version__"]
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -77,10 +77,10 @@ language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = "sphinx"
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True
@@ -90,42 +90,47 @@ todo_include_todos = True
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
html_theme = "alabaster"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
html_theme_options = {
'show_powered_by': False,
'github_user': 'pypa',
'github_repo': 'pipenv',
'github_banner': False,
'show_related': False
"show_powered_by": False,
"github_user": "pypa",
"github_repo": "pipenv",
"github_banner": False,
"show_related": False,
}
html_sidebars = {
'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html',
'hacks.html'],
'**': ['sidebarlogo.html', 'localtoc.html', 'relations.html',
'sourcelink.html', 'searchbox.html', 'hacks.html']
"index": ["sidebarintro.html", "sourcelink.html", "searchbox.html", "hacks.html"],
"**": [
"sidebarlogo.html",
"localtoc.html",
"relations.html",
"sourcelink.html",
"searchbox.html",
"hacks.html",
],
}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_static_path = ["_static"]
def setup(app):
app.add_stylesheet('custom.css')
app.add_stylesheet("custom.css")
# -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'pipenvdoc'
htmlhelp_basename = "pipenvdoc"
# -- Options for LaTeX output ---------------------------------------------
@@ -134,15 +139,12 @@ latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
@@ -152,8 +154,7 @@ latex_elements = {
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'pipenv.tex', 'pipenv Documentation',
'Kenneth Reitz', 'manual'),
(master_doc, "pipenv.tex", "pipenv Documentation", "Kenneth Reitz", "manual"),
]
@@ -161,10 +162,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'pipenv', 'pipenv Documentation',
[author], 1)
]
man_pages = [(master_doc, "pipenv", "pipenv Documentation", [author], 1)]
# -- Options for Texinfo output -------------------------------------------
@@ -173,9 +171,15 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'pipenv', 'pipenv Documentation',
author, 'pipenv', 'One line description of project.',
'Miscellaneous'),
(
master_doc,
"pipenv",
"pipenv Documentation",
author,
"pipenv",
"One line description of project.",
"Miscellaneous",
),
]
@@ -197,4 +201,4 @@ epub_copyright = copyright
# epub_uid = ''
# A list of files that should not be packed into the epub file.
epub_exclude_files = ['search.html']
epub_exclude_files = ["search.html"]
+16 -3
View File
@@ -8,7 +8,7 @@ contributing to the Pipenv project is *very* generous of you.
This document lays out guidelines and advice for contributing to this project.
If you're thinking of contributing, please start by reading this document and
getting a feel for how contributing to this project works.
getting a feel for how contributing to this project works.
The guide is split into sections based on the type of contribution you're
thinking of making, with a section that covers general guidelines for all
@@ -106,6 +106,18 @@ See `pypa/pipenv#2557`_ for more details.
.. _pypa/pipenv#2557: https://github.com/pypa/pipenv/issues/2557
Pipenv now uses pre-commit hooks similar to Pip in order to apply linting and
code formatting automatically! The build now also checks that these linting rules
have been applied to the code before running the tests.
The build will fail when linting changes are detected so be sure to sync dev requirements
and install the pre-commit hooks locally:
$ ``pipenv install --dev``
# This will configure running the pre-commit checks at start of each commit
$ ``pre-commit install``
# Should you want to check the pre-commit configuration against all configured project files
$ ``pre-commit run --all-files --verbose``
.. _testing:
@@ -241,7 +253,7 @@ you run it before you open a PR. Taking this second approach,
will allow you, for example, to run a single test case, or
``fail fast`` if you need it.
2. Manually, which repeat the steps of the scripts above:
2. Manually, which repeat the steps of the scripts above:
.. code-block:: console
@@ -272,10 +284,11 @@ It is important that your environment is setup correctly, and
this may take some work, for example, on a specific Mac installation, the following
steps may be needed::
.. code-block:: bash
# Make sure the tests can access github
if [ "$SSH_AGENT_PID" = "" ]
then
eval `ssh-agent`
eval ``ssh-agent``
ssh-add
fi
+1 -1
View File
@@ -1,7 +1,7 @@
.. pipenv documentation master file, created by
sphinx-quickstart on Mon Jan 30 13:28:36 2017.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
contain the root ``toctree`` directive.
Pipenv: Python Dev Workflow for Humans
======================================
+24 -21
View File
@@ -23,44 +23,44 @@
# Don't manually edit this script! Check ths instructions in the link above to
# create get-pip.py and patch the following:
#+++ ./get-pip/public/get-pip.py 2022-01-12 16:52:11.920161471 +0100
#--- ./pipenv/get-pipenv.py 2022-01-12 20:11:35.816906142 +0100
#@@ -55,7 +28,7 @@
# +++ ./get-pip/public/get-pip.py 2022-01-12 16:52:11.920161471 +0100
# --- ./pipenv/get-pipenv.py 2022-01-12 20:11:35.816906142 +0100
# @@ -55,7 +28,7 @@
# message_parts = [
# "This script does not work on Python {}.{}".format(*this_python),
# "The minimum supported Python version is {}.{}.".format(*min_version),
#- "Please use an alternative installation https://pipenv.pypa.io/en/latest/install/",
#+ "Please use https://bootstrap.pypa.io/pip/{}.{}/get-pip.py instead.".format(*this_python),
# - "Please use an alternative installation https://pipenv.pypa.io/en/latest/install/",
# + "Please use https://bootstrap.pypa.io/pip/{}.{}/get-pip.py instead.".format(*this_python),
# ]
# print("ERROR: " + " ".join(message_parts))
# sys.exit(1)
#@@ -70,7 +43,7 @@
#
# @@ -70,7 +43,7 @@
#
# def determine_pip_install_arguments():
# implicit_pip = True
#+ implicit_setuptools = False
#- implicit_setuptools = True
# + implicit_setuptools = False
# - implicit_setuptools = True
# implicit_wheel = True
#
#
# # Check if the user has requested us not to install setuptools
#@@ -87,8 +60,6 @@
#
# @@ -87,8 +60,6 @@
#
# # We only want to implicitly install setuptools and wheel if they don't
# # already exist on the target platform.
#+ # No need for doing this, since pipenv already has setuptools as
#+ # a dependency in setup.py
# + # No need for doing this, since pipenv already has setuptools as
# + # a dependency in setup.py
# if implicit_setuptools:
# try:
# import setuptools # noqa
#@@ -109,8 +80,6 @@
# @@ -109,8 +80,6 @@
# args += ["setuptools"]
# if implicit_wheel:
# args += ["wheel"]
#+
#+ args += ["pipenv"]
#
# +
# + args += ["pipenv"]
#
# return ["install", "--upgrade", "--force-reinstall"] + args
# YMMV, so dig a bit to find how to add pipenv to the args passed to pip.
import sys
@@ -103,17 +103,19 @@ def determine_pip_install_arguments():
# We only want to implicitly install setuptools and wheel if they don't
# already exist on the target platform.
# No need for doing this, since pipenv already has setuptools as
# No need for doing this, since pipenv already has setuptools as
# a dependency in setup.py
if implicit_setuptools:
try:
import setuptools # noqa
implicit_setuptools = False
except ImportError:
pass
if implicit_wheel:
try:
import wheel # noqa
implicit_wheel = False
except ImportError:
pass
@@ -125,7 +127,7 @@ def determine_pip_install_arguments():
args += ["setuptools"]
if implicit_wheel:
args += ["wheel"]
args += ["pipenv"]
return ["install", "--upgrade", "--force-reinstall"] + args
@@ -164,6 +166,7 @@ def bootstrap(tmpdir):
# Execute the included pip and use it to install the latest pip and
# setuptools from PyPI
from pip._internal.cli.main import main as pip_entry_point
args = determine_pip_install_arguments()
sys.exit(pip_entry_point(args))
+1
View File
@@ -0,0 +1 @@
Internal to pipenv, the utils.py was split into a utils module with unused code removed.
+2
View File
@@ -0,0 +1,2 @@
Added code linting using pre-commit-hooks, black, flake8, isort, pygrep-hooks, news-fragments and check-manifest.
Very similar to pip's configuration; adds a towncrier new's type ``process`` for change to Development processes.
+2 -3
View File
@@ -7,8 +7,7 @@ import os
import sys
import warnings
from pipenv.__version__ import __version__ # noqa
from pipenv.__version__ import __version__ # noqa
PIPENV_ROOT = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
PIPENV_VENDOR = os.sep.join([PIPENV_ROOT, "vendor"])
@@ -54,8 +53,8 @@ if os.name == "nt":
sys.stdout = stdout
sys.stderr = stderr
from .cli import cli
from . import resolver # noqa
from .cli import cli
if __name__ == "__main__":
cli()
-1
View File
@@ -1,5 +1,4 @@
from pipenv.cli import cli
if __name__ == "__main__":
cli()
+10 -3
View File
@@ -12,13 +12,19 @@ warnings.filterwarnings("ignore", category=ResourceWarning)
__all__ = [
"getpreferredencoding", "DEFAULT_ENCODING", "canonical_encoding_name",
"force_encoding", "UNICODE_TO_ASCII_TRANSLATION_MAP", "decode_output", "fix_utf8"
"getpreferredencoding",
"DEFAULT_ENCODING",
"canonical_encoding_name",
"force_encoding",
"UNICODE_TO_ASCII_TRANSLATION_MAP",
"decode_output",
"fix_utf8",
]
def getpreferredencoding():
import locale
# Borrowed from Invoke
# (see https://github.com/pyinvoke/invoke/blob/93af29d/invoke/runners.py#L881)
return locale.getpreferredencoding(False)
@@ -29,6 +35,7 @@ DEFAULT_ENCODING = getpreferredencoding()
def canonical_encoding_name(name):
import codecs
try:
codec = codecs.lookup(name)
except LookupError:
@@ -55,7 +62,7 @@ def force_encoding():
if stdout_encoding != "utf-8" or stderr_encoding != "utf-8":
try:
from ctypes import pythonapi, py_object, c_char_p
from ctypes import c_char_p, py_object, pythonapi
except ImportError:
return DEFAULT_ENCODING, DEFAULT_ENCODING
try:
+1 -1
View File
@@ -1 +1 @@
from .command import cli # noqa
from .command import cli # noqa
+125 -88
View File
@@ -5,25 +5,41 @@ from pipenv import environments
from pipenv.__version__ import __version__
from pipenv._compat import fix_utf8
from pipenv.cli.options import (
CONTEXT_SETTINGS, PipenvGroup, common_options, deploy_option,
general_options, install_options, lock_options, pass_state,
pypi_mirror_option, python_option, site_packages_option, skip_lock_option,
sync_options, system_option, three_option, uninstall_options, verbose_option
CONTEXT_SETTINGS,
PipenvGroup,
common_options,
deploy_option,
general_options,
install_options,
lock_options,
pass_state,
pypi_mirror_option,
python_option,
site_packages_option,
skip_lock_option,
sync_options,
system_option,
three_option,
uninstall_options,
verbose_option,
)
from pipenv.exceptions import PipenvOptionsError
from pipenv.patched import crayons
from pipenv.utils.processes import subprocess_run
from pipenv.vendor.click import (
Choice, argument, echo, edit, group, option, pass_context, secho, types,
version_option
Choice,
argument,
echo,
edit,
group,
option,
pass_context,
secho,
version_option,
)
subcommand_context = CONTEXT_SETTINGS.copy()
subcommand_context.update({
"ignore_unknown_options": True,
"allow_extra_args": True
})
subcommand_context.update({"ignore_unknown_options": True, "allow_extra_args": True})
subcommand_context_no_interspersion = subcommand_context.copy()
subcommand_context_no_interspersion["allow_interspersed_args"] = False
@@ -31,8 +47,12 @@ subcommand_context_no_interspersion["allow_interspersed_args"] = False
@group(cls=PipenvGroup, invoke_without_command=True, context_settings=CONTEXT_SETTINGS)
@option("--where", is_flag=True, default=False, help="Output project home information.")
@option("--venv", is_flag=True, default=False, help="Output virtualenv information.")
@option("--py", is_flag=True, default=False, help="Output Python interpreter information.")
@option("--envs", is_flag=True, default=False, help="Output Environment Variable options.")
@option(
"--py", is_flag=True, default=False, help="Output Python interpreter information."
)
@option(
"--envs", is_flag=True, default=False, help="Output Environment Variable options."
)
@option("--rm", is_flag=True, default=False, help="Remove the virtualenv.")
@option("--bare", is_flag=True, default=False, help="Minimal output.")
@option("--man", is_flag=True, default=False, help="Display manpage.")
@@ -58,21 +78,33 @@ def cli(
support=None,
help=False,
site_packages=None,
**kwargs
**kwargs,
):
from ..core import (
cleanup_virtualenv, do_clear, do_py, do_where, ensure_project,
format_help, system_which, warn_in_virtualenv
)
from pipenv.utils.spinner import create_spinner
from ..core import (
cleanup_virtualenv,
do_clear,
do_py,
do_where,
ensure_project,
format_help,
system_which,
warn_in_virtualenv,
)
if man:
if system_which("man"):
path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "pipenv.1")
os.execle(system_which("man"), "man", path, os.environ)
return 0
else:
secho("man does not appear to be available on your system.", fg="yellow", bold=True, err=True)
secho(
"man does not appear to be available on your system.",
fg="yellow",
bold=True,
err=True,
)
return 1
if envs:
echo("The following environment variables can be set, to do various things:\n")
@@ -114,7 +146,7 @@ def cli(
"{}({}){}".format(
crayons.red("No virtualenv has been created for this project"),
crayons.normal(state.project.project_directory, bold=True),
crayons.red(" yet!")
crayons.red(" yet!"),
),
err=True,
)
@@ -183,10 +215,7 @@ def cli(
@skip_lock_option
@install_options
@pass_state
def install(
state,
**kwargs
):
def install(state, **kwargs):
"""Installs provided packages and adds them to Pipfile, or (if no packages are given), installs all packages from Pipfile."""
from ..core import do_install
@@ -210,13 +239,13 @@ def install(
extra_index_url=state.extra_index_urls,
packages=state.installstate.packages,
editable_packages=state.installstate.editables,
site_packages=state.site_packages
site_packages=state.site_packages,
)
@cli.command(
short_help="Uninstalls a provided package and removes it from Pipfile.",
context_settings=subcommand_context
context_settings=subcommand_context,
)
@option(
"--all-dev",
@@ -233,15 +262,10 @@ def install(
@uninstall_options
@pass_state
@pass_context
def uninstall(
ctx,
state,
all_dev=False,
all=False,
**kwargs
):
def uninstall(ctx, state, all_dev=False, all=False, **kwargs):
"""Uninstalls a provided package and removes it from Pipfile."""
from ..core import do_uninstall
retcode = do_uninstall(
state.project,
packages=state.installstate.packages,
@@ -254,7 +278,7 @@ def uninstall(
all=all,
keep_outdated=state.installstate.keep_outdated,
pypi_mirror=state.pypi_mirror,
ctx=ctx
ctx=ctx,
)
if retcode:
sys.exit(retcode)
@@ -280,11 +304,7 @@ LOCK_DEV_NOTE = """\
@lock_options
@pass_state
@pass_context
def lock(
ctx,
state,
**kwargs
):
def lock(ctx, state, **kwargs):
"""Generates Pipfile.lock."""
from ..core import do_init, do_lock, ensure_project
@@ -292,8 +312,12 @@ def lock(
# Note that we don't pass clear on to ensure_project as it is also
# handled in do_lock
ensure_project(
state.project, three=state.three, python=state.python, pypi_mirror=state.pypi_mirror,
warn=(not state.quiet), site_packages=state.site_packages,
state.project,
three=state.three,
python=state.python,
pypi_mirror=state.pypi_mirror,
warn=(not state.quiet),
site_packages=state.site_packages,
)
emit_requirements = state.lockoptions.emit_requirements
dev = state.installstate.dev
@@ -325,7 +349,7 @@ def lock(
raise PipenvOptionsError(
"--dev-only",
"--dev-only is only permitted in combination with --requirements. "
"Aborting."
"Aborting.",
)
do_lock(
state.project,
@@ -347,7 +371,7 @@ def lock(
is_flag=True,
default=False,
help="Run in shell in fancy mode. Make sure the shell have no path manipulating"
" scripts. Run $pipenv shell for issues with compatibility mode.",
" scripts. Run $pipenv shell for issues with compatibility mode.",
)
@option(
"--anyway",
@@ -409,6 +433,7 @@ def shell(
def run(state, command, args):
"""Spawns a command installed into the virtualenv."""
from ..core import do_run
do_run(
state.project,
command=command,
@@ -416,21 +441,21 @@ def run(state, command, args):
three=state.three,
python=state.python,
pypi_mirror=state.pypi_mirror,
quiet=state.quiet
quiet=state.quiet,
)
@cli.command(
short_help="Checks for PyUp Safety security vulnerabilities and against"
" PEP 508 markers provided in Pipfile.",
context_settings=subcommand_context
" PEP 508 markers provided in Pipfile.",
context_settings=subcommand_context,
)
@option(
"--db",
nargs=1,
default=lambda: os.environ.get('PIPENV_SAFETY_DB'),
default=lambda: os.environ.get("PIPENV_SAFETY_DB"),
help="Path to a local PyUp Safety vulnerabilities database."
" Default: ENV PIPENV_SAFETY_DB or None.",
" Default: ENV PIPENV_SAFETY_DB or None.",
)
@option(
"--ignore",
@@ -447,8 +472,11 @@ def run(state, command, args):
@option(
"--key",
help="Safety API key from PyUp.io for scanning dependencies against a live"
" vulnerabilities database. Leave blank for scanning against a"
" database that only updates once a month.",
" vulnerabilities database. Leave blank for scanning against a"
" database that only updates once a month.",
)
@option(
"--quiet", is_flag=True, help="Quiet standard output, except vulnerability report."
)
@common_options
@system_option
@@ -461,7 +489,7 @@ def check(
output="default",
key=None,
quiet=False,
**kwargs
**kwargs,
):
"""Checks for PyUp Safety security vulnerabilities and against PEP 508 markers provided in Pipfile."""
from ..core import do_check
@@ -482,31 +510,33 @@ def check(
@cli.command(short_help="Runs lock, then sync.", context_settings=CONTEXT_SETTINGS)
@option("--bare", is_flag=True, default=False, help="Minimal output.")
@option(
"--outdated", is_flag=True, default=False, help="List out-of-date dependencies."
)
@option("--outdated", is_flag=True, default=False, help="List out-of-date dependencies.")
@option("--dry-run", is_flag=True, default=None, help="List out-of-date dependencies.")
@install_options
@pass_state
@pass_context
def update(
ctx,
state,
bare=False,
dry_run=None,
outdated=False,
**kwargs
):
def update(ctx, state, bare=False, dry_run=None, outdated=False, **kwargs):
"""Runs lock, then sync."""
from ..core import do_lock, do_outdated, do_sync, ensure_project
ensure_project(
state.project, three=state.three, python=state.python, pypi_mirror=state.pypi_mirror,
warn=(not state.quiet), site_packages=state.site_packages, clear=state.clear
state.project,
three=state.three,
python=state.python,
pypi_mirror=state.pypi_mirror,
warn=(not state.quiet),
site_packages=state.site_packages,
clear=state.clear,
)
if not outdated:
outdated = bool(dry_run)
if outdated:
do_outdated(state.project, clear=state.clear, pre=state.installstate.pre, pypi_mirror=state.pypi_mirror)
do_outdated(
state.project,
clear=state.clear,
pre=state.installstate.pre,
pypi_mirror=state.pypi_mirror,
)
packages = [p for p in state.installstate.packages if p]
editable = [p for p in state.installstate.editables if p]
if not packages:
@@ -557,7 +587,7 @@ def update(
@cli.command(
short_help="Displays currently-installed dependency graph information.",
context_settings=CONTEXT_SETTINGS
context_settings=CONTEXT_SETTINGS,
)
@option("--bare", is_flag=True, default=False, help="Minimal output.")
@option("--json", is_flag=True, default=False, help="Output JSON.")
@@ -572,8 +602,9 @@ def graph(state, bare=False, json=False, json_tree=False, reverse=False):
@cli.command(
short_help="View a given module in your editor.", name="open",
context_settings=CONTEXT_SETTINGS
short_help="View a given module in your editor.",
name="open",
context_settings=CONTEXT_SETTINGS,
)
@common_options
@argument("module", nargs=1)
@@ -590,11 +621,18 @@ def run_open(state, module, *args, **kwargs):
# Ensure that virtualenv is available.
ensure_project(
state.project, three=state.three, python=state.python,
validate=False, pypi_mirror=state.pypi_mirror,
state.project,
three=state.three,
python=state.python,
validate=False,
pypi_mirror=state.pypi_mirror,
)
c = subprocess_run(
[state.project._which("python"), "-c", "import {0}; print({0}.__file__)".format(module)]
[
state.project._which("python"),
"-c",
"import {0}; print({0}.__file__)".format(module),
]
)
if c.returncode:
echo(crayons.red("Module not found!"))
@@ -611,21 +649,14 @@ def run_open(state, module, *args, **kwargs):
@cli.command(
short_help="Installs all packages specified in Pipfile.lock.",
context_settings=CONTEXT_SETTINGS
context_settings=CONTEXT_SETTINGS,
)
@system_option
@option("--bare", is_flag=True, default=False, help="Minimal output.")
@sync_options
@pass_state
@pass_context
def sync(
ctx,
state,
bare=False,
user=False,
unused=False,
**kwargs
):
def sync(ctx, state, bare=False, user=False, unused=False, **kwargs):
"""Installs all packages specified in Pipfile.lock."""
from ..core import do_sync
@@ -641,7 +672,7 @@ def sync(
unused=unused,
sequential=state.installstate.sequential,
pypi_mirror=state.pypi_mirror,
system=state.system
system=state.system,
)
if retcode:
ctx.abort()
@@ -649,7 +680,7 @@ def sync(
@cli.command(
short_help="Uninstalls all packages not specified in Pipfile.lock.",
context_settings=CONTEXT_SETTINGS
context_settings=CONTEXT_SETTINGS,
)
@option("--bare", is_flag=True, default=False, help="Minimal output.")
@option("--dry-run", is_flag=True, default=False, help="Just output unneeded packages.")
@@ -660,8 +691,14 @@ def sync(
def clean(state, dry_run=False, bare=False, user=False):
"""Uninstalls all packages not specified in Pipfile.lock."""
from ..core import do_clean
do_clean(state.project, three=state.three, python=state.python, dry_run=dry_run,
system=state.system)
do_clean(
state.project,
three=state.three,
python=state.python,
dry_run=dry_run,
system=state.system,
)
@cli.command(
@@ -675,7 +712,7 @@ def scripts(state):
if not state.project.pipfile_exists:
echo("No Pipfile present at project home.", err=True)
sys.exit(1)
scripts = state.project.parsed_pipfile.get('scripts', {})
scripts = state.project.parsed_pipfile.get("scripts", {})
first_column_width = max(len(word) for word in ["Command"] + list(scripts))
second_column_width = max(len(word) for word in ["Script"] + list(scripts.values()))
lines = ["{0:<{width}} Script".format("Command", width=first_column_width)]
@@ -699,13 +736,13 @@ def verify(state):
sys.exit(1)
if state.project.get_lockfile_hash() != state.project.calculate_pipfile_hash():
echo(
'Pipfile.lock is out-of-date. Run {} to update.'.format(
"Pipfile.lock is out-of-date. Run {} to update.".format(
crayons.yellow("$ pipenv lock", bold=True)
),
err=True
err=True,
)
sys.exit(1)
echo(crayons.green('Pipfile.lock is up-to-date.'))
echo(crayons.green("Pipfile.lock is up-to-date."))
sys.exit(0)
+258 -78
View File
@@ -3,17 +3,19 @@ import os
from pipenv.project import Project
from pipenv.utils.internet import is_valid_url
from pipenv.vendor.click import (
BadArgumentUsage, BadParameter, Group, Option, argument, echo,
make_pass_decorator, option
BadArgumentUsage,
BadParameter,
Group,
Option,
argument,
echo,
make_pass_decorator,
option,
)
from pipenv.vendor.click import types as click_types
from pipenv.vendor.click_didyoumean import DYMMixin
CONTEXT_SETTINGS = {
"help_option_names": ["-h", "--help"],
"auto_envvar_prefix": "PIPENV"
}
CONTEXT_SETTINGS = {"help_option_names": ["-h", "--help"], "auto_envvar_prefix": "PIPENV"}
class PipenvGroup(DYMMixin, Group):
@@ -53,6 +55,7 @@ class PipenvGroup(DYMMixin, Group):
"""
return super().main(*args, **kwargs, windows_expand_args=False)
class State:
def __init__(self):
self.index = None
@@ -102,9 +105,16 @@ def index_option(f):
state = ctx.ensure_object(State)
state.index = value
return value
return option('-i', '--index', expose_value=False, envvar="PIP_INDEX_URL",
help='Target PyPI-compatible package index url.', nargs=1,
callback=callback)(f)
return option(
"-i",
"--index",
expose_value=False,
envvar="PIP_INDEX_URL",
help="Target PyPI-compatible package index url.",
nargs=1,
callback=callback,
)(f)
def extra_index_option(f):
@@ -112,9 +122,15 @@ def extra_index_option(f):
state = ctx.ensure_object(State)
state.extra_index_urls.extend(list(value))
return value
return option("--extra-index-url", multiple=True, expose_value=False,
help="URLs to the extra PyPI compatible indexes to query for package look-ups.",
callback=callback, envvar="PIP_EXTRA_INDEX_URL")(f)
return option(
"--extra-index-url",
multiple=True,
expose_value=False,
help="URLs to the extra PyPI compatible indexes to query for package look-ups.",
callback=callback,
envvar="PIP_EXTRA_INDEX_URL",
)(f)
def editable_option(f):
@@ -122,11 +138,16 @@ def editable_option(f):
state = ctx.ensure_object(State)
state.installstate.editables.extend(value)
return value
return option('-e', '--editable', expose_value=False, multiple=True,
callback=callback, type=click_types.STRING, help=(
"An editable Python package URL or path, often to a VCS "
"repository."
))(f)
return option(
"-e",
"--editable",
expose_value=False,
multiple=True,
callback=callback,
type=click_types.STRING,
help="An editable Python package URL or path, often to a VCS repository.",
)(f)
def sequential_option(f):
@@ -134,9 +155,17 @@ def sequential_option(f):
state = ctx.ensure_object(State)
state.installstate.sequential = value
return value
return option("--sequential", is_flag=True, default=False, expose_value=False,
help="Install dependencies one-at-a-time, instead of concurrently.",
callback=callback, type=click_types.BOOL, show_envvar=True)(f)
return option(
"--sequential",
is_flag=True,
default=False,
expose_value=False,
help="Install dependencies one-at-a-time, instead of concurrently.",
callback=callback,
type=click_types.BOOL,
show_envvar=True,
)(f)
def skip_lock_option(f):
@@ -144,10 +173,18 @@ def skip_lock_option(f):
state = ctx.ensure_object(State)
state.installstate.skip_lock = value
return value
return option("--skip-lock", is_flag=True, default=False, expose_value=False,
help="Skip locking mechanisms and use the Pipfile instead during operation.",
envvar="PIPENV_SKIP_LOCK", callback=callback, type=click_types.BOOL,
show_envvar=True)(f)
return option(
"--skip-lock",
is_flag=True,
default=False,
expose_value=False,
help="Skip locking mechanisms and use the Pipfile instead during operation.",
envvar="PIPENV_SKIP_LOCK",
callback=callback,
type=click_types.BOOL,
show_envvar=True,
)(f)
def keep_outdated_option(f):
@@ -155,9 +192,17 @@ def keep_outdated_option(f):
state = ctx.ensure_object(State)
state.installstate.keep_outdated = value
return value
return option("--keep-outdated", is_flag=True, default=False, expose_value=False,
help="Keep out-dated dependencies from being updated in Pipfile.lock.",
callback=callback, type=click_types.BOOL, show_envvar=True)(f)
return option(
"--keep-outdated",
is_flag=True,
default=False,
expose_value=False,
help="Keep out-dated dependencies from being updated in Pipfile.lock.",
callback=callback,
type=click_types.BOOL,
show_envvar=True,
)(f)
def selective_upgrade_option(f):
@@ -165,9 +210,16 @@ def selective_upgrade_option(f):
state = ctx.ensure_object(State)
state.installstate.selective_upgrade = value
return value
return option("--selective-upgrade", is_flag=True, default=False, type=click_types.BOOL,
help="Update specified packages.", callback=callback,
expose_value=False)(f)
return option(
"--selective-upgrade",
is_flag=True,
default=False,
type=click_types.BOOL,
help="Update specified packages.",
callback=callback,
expose_value=False,
)(f)
def ignore_pipfile_option(f):
@@ -175,9 +227,17 @@ def ignore_pipfile_option(f):
state = ctx.ensure_object(State)
state.installstate.ignore_pipfile = value
return value
return option("--ignore-pipfile", is_flag=True, default=False, expose_value=False,
help="Ignore Pipfile when installing, using the Pipfile.lock.",
callback=callback, type=click_types.BOOL, show_envvar=True)(f)
return option(
"--ignore-pipfile",
is_flag=True,
default=False,
expose_value=False,
help="Ignore Pipfile when installing, using the Pipfile.lock.",
callback=callback,
type=click_types.BOOL,
show_envvar=True,
)(f)
def _dev_option(f, help_text):
@@ -185,9 +245,18 @@ def _dev_option(f, help_text):
state = ctx.ensure_object(State)
state.installstate.dev = value
return value
return option("--dev", "-d", is_flag=True, default=False, type=click_types.BOOL,
help=help_text, callback=callback,
expose_value=False, show_envvar=True)(f)
return option(
"--dev",
"-d",
is_flag=True,
default=False,
type=click_types.BOOL,
help=help_text,
callback=callback,
expose_value=False,
show_envvar=True,
)(f)
def install_dev_option(f):
@@ -199,7 +268,9 @@ def lock_dev_option(f):
def uninstall_dev_option(f):
return _dev_option(f, "Deprecated (as it has no effect). May be removed in a future release.")
return _dev_option(
f, "Deprecated (as it has no effect). May be removed in a future release."
)
def pre_option(f):
@@ -207,8 +278,16 @@ def pre_option(f):
state = ctx.ensure_object(State)
state.installstate.pre = value
return value
return option("--pre", is_flag=True, default=False, help="Allow pre-releases.",
callback=callback, type=click_types.BOOL, expose_value=False)(f)
return option(
"--pre",
is_flag=True,
default=False,
help="Allow pre-releases.",
callback=callback,
type=click_types.BOOL,
expose_value=False,
)(f)
def package_arg(f):
@@ -216,8 +295,14 @@ def package_arg(f):
state = ctx.ensure_object(State)
state.installstate.packages.extend(value)
return value
return argument('packages', nargs=-1, callback=callback, expose_value=False,
type=click_types.STRING)(f)
return argument(
"packages",
nargs=-1,
callback=callback,
expose_value=False,
type=click_types.STRING,
)(f)
def three_option(f):
@@ -226,9 +311,15 @@ def three_option(f):
if value is not None:
state.three = value
return value
return option("--three", is_flag=True, default=None,
help="Use Python 3/2 when creating virtualenv.", callback=callback,
expose_value=False)(f)
return option(
"--three",
is_flag=True,
default=None,
help="Use Python 3 when creating virtualenv.",
callback=callback,
expose_value=False,
)(f)
def python_option(f):
@@ -237,10 +328,17 @@ def python_option(f):
if value is not None:
state.python = validate_python_path(ctx, param, value)
return value
return option("--python", default="", nargs=1, callback=callback,
help="Specify which version of Python virtualenv should use.",
expose_value=False, allow_from_autoenv=False,
type=click_types.STRING)(f)
return option(
"--python",
default="",
nargs=1,
callback=callback,
help="Specify which version of Python virtualenv should use.",
expose_value=False,
allow_from_autoenv=False,
type=click_types.STRING,
)(f)
def pypi_mirror_option(f):
@@ -250,8 +348,14 @@ def pypi_mirror_option(f):
if value is not None:
state.pypi_mirror = validate_pypi_mirror(ctx, param, value)
return value
return option("--pypi-mirror", nargs=1, callback=callback,
help="Specify a PyPI mirror.", expose_value=False)(f)
return option(
"--pypi-mirror",
nargs=1,
callback=callback,
help="Specify a PyPI mirror.",
expose_value=False,
)(f)
def verbose_option(f):
@@ -261,12 +365,20 @@ def verbose_option(f):
if state.quiet:
raise BadArgumentUsage(
"--verbose and --quiet are mutually exclusive! Please choose one!",
ctx=ctx
ctx=ctx,
)
state.verbose = True
setup_verbosity(ctx, param, 1)
return option("--verbose", "-v", is_flag=True, expose_value=False,
callback=callback, help="Verbose mode.", type=click_types.BOOL)(f)
return option(
"--verbose",
"-v",
is_flag=True,
expose_value=False,
callback=callback,
help="Verbose mode.",
type=click_types.BOOL,
)(f)
def quiet_option(f):
@@ -276,12 +388,20 @@ def quiet_option(f):
if state.verbose:
raise BadArgumentUsage(
"--verbose and --quiet are mutually exclusive! Please choose one!",
ctx=ctx
ctx=ctx,
)
state.quiet = True
setup_verbosity(ctx, param, -1)
return option("--quiet", "-q", is_flag=True, expose_value=False,
callback=callback, help="Quiet mode.", type=click_types.BOOL)(f)
return option(
"--quiet",
"-q",
is_flag=True,
expose_value=False,
callback=callback,
help="Quiet mode.",
type=click_types.BOOL,
)(f)
def site_packages_option(f):
@@ -290,9 +410,16 @@ def site_packages_option(f):
validate_bool_or_none(ctx, param, value)
state.site_packages = value
return value
return option("--site-packages/--no-site-packages", is_flag=True, default=None,
help="Enable site-packages for the virtualenv.", callback=callback,
expose_value=False, show_envvar=True)(f)
return option(
"--site-packages/--no-site-packages",
is_flag=True,
default=None,
help="Enable site-packages for the virtualenv.",
callback=callback,
expose_value=False,
show_envvar=True,
)(f)
def clear_option(f):
@@ -300,9 +427,16 @@ def clear_option(f):
state = ctx.ensure_object(State)
state.clear = value
return value
return option("--clear", is_flag=True, callback=callback, type=click_types.BOOL,
help="Clears caches (pipenv, pip).",
expose_value=False, show_envvar=True)(f)
return option(
"--clear",
is_flag=True,
callback=callback,
type=click_types.BOOL,
help="Clears caches (pipenv, pip).",
expose_value=False,
show_envvar=True,
)(f)
def system_option(f):
@@ -311,9 +445,17 @@ def system_option(f):
if value is not None:
state.system = value
return value
return option("--system", is_flag=True, default=False, help="System pip management.",
callback=callback, type=click_types.BOOL, expose_value=False,
show_envvar=True)(f)
return option(
"--system",
is_flag=True,
default=False,
help="System pip management.",
callback=callback,
type=click_types.BOOL,
expose_value=False,
show_envvar=True,
)(f)
def requirementstxt_option(f):
@@ -322,9 +464,17 @@ def requirementstxt_option(f):
if value:
state.installstate.requirementstxt = value
return value
return option("--requirements", "-r", nargs=1, default="", expose_value=False,
help="Import a requirements.txt file.", callback=callback,
type=click_types.STRING)(f)
return option(
"--requirements",
"-r",
nargs=1,
default="",
expose_value=False,
help="Import a requirements.txt file.",
callback=callback,
type=click_types.STRING,
)(f)
def emit_requirements_flag(f):
@@ -333,8 +483,16 @@ def emit_requirements_flag(f):
if value:
state.lockoptions.emit_requirements = value
return value
return option("--requirements", "-r", default=False, is_flag=True, expose_value=False,
help="Generate output in requirements.txt format.", callback=callback)(f)
return option(
"--requirements",
"-r",
default=False,
is_flag=True,
expose_value=False,
help="Generate output in requirements.txt format.",
callback=callback,
)(f)
def emit_requirements_header_flag(f):
@@ -343,8 +501,15 @@ def emit_requirements_header_flag(f):
if value:
state.lockoptions.emit_requirements_header = value
return value
return option("--header/--no-header", default=True, is_flag=True, expose_value=False,
help="Add header to generated requirements", callback=callback)(f)
return option(
"--header/--no-header",
default=True,
is_flag=True,
expose_value=False,
help="Add header to generated requirements",
callback=callback,
)(f)
def dev_only_flag(f):
@@ -353,8 +518,15 @@ def dev_only_flag(f):
if value:
state.lockoptions.dev_only = value
return value
return option("--dev-only", default=False, is_flag=True, expose_value=False,
help="Emit development dependencies *only* (overrides --dev)", callback=callback)(f)
return option(
"--dev-only",
default=False,
is_flag=True,
expose_value=False,
help="Emit development dependencies *only* (overrides --dev)",
callback=callback,
)(f)
def deploy_option(f):
@@ -362,15 +534,23 @@ def deploy_option(f):
state = ctx.ensure_object(State)
state.installstate.deploy = value
return value
return option("--deploy", is_flag=True, default=False, type=click_types.BOOL,
help="Abort if the Pipfile.lock is out-of-date, or Python version is"
" wrong.", callback=callback, expose_value=False)(f)
return option(
"--deploy",
is_flag=True,
default=False,
type=click_types.BOOL,
help="Abort if the Pipfile.lock is out-of-date, or Python version is wrong.",
callback=callback,
expose_value=False,
)(f)
def setup_verbosity(ctx, param, value):
if not value:
return
import logging
loggers = ("pip",)
if value == 1:
for logger in loggers:
+6 -4
View File
@@ -94,7 +94,9 @@ class Script(object):
See also: https://docs.python.org/3/library/subprocess.html#converting-argument-sequence
"""
return " ".join(itertools.chain(
[_quote_if_contains(self.command, r'[\s^()]')],
(_quote_if_contains(arg, r'[\s^]') for arg in self.args),
))
return " ".join(
itertools.chain(
[_quote_if_contains(self.command, r"[\s^()]")],
(_quote_if_contains(arg, r"[\s^]") for arg in self.args),
)
)
+414 -236
View File
File diff suppressed because it is too large Load Diff
+219 -115
View File
@@ -12,21 +12,17 @@ from sysconfig import get_paths, get_python_version
import pkg_resources
import pipenv
from pipenv.environments import is_type_checking
from pipenv.utils.shell import make_posix, normalize_path
from pipenv.utils.processes import subprocess_run
from pipenv.utils.indexes import prepare_pip_source_args
from pipenv.utils.processes import subprocess_run
from pipenv.utils.shell import make_posix, normalize_path
from pipenv.vendor import vistir
from pipenv.vendor.cached_property import cached_property
from pipenv.vendor.packaging.utils import canonicalize_name
if is_type_checking():
from types import ModuleType
from typing import (
ContextManager, Dict, Generator, List, Optional, Set, Union
)
from typing import ContextManager, Dict, Generator, List, Optional, Set, Union
import pip_shims.shims
import tomlkit
@@ -47,10 +43,10 @@ class Environment:
base_working_set=None, # type: pkg_resources.WorkingSet
pipfile=None, # type: Optional[Union[tomlkit.toml_document.TOMLDocument, TPipfile]]
sources=None, # type: Optional[List[TSource]]
project=None # type: Optional[Project]
project=None, # type: Optional[Project]
):
super().__init__()
self._modules = {'pkg_resources': pkg_resources, 'pipenv': pipenv}
self._modules = {"pkg_resources": pkg_resources, "pipenv": pipenv}
self.base_working_set = base_working_set if base_working_set else BASE_WORKING_SET
prefix = normalize_path(prefix)
self._python = None
@@ -82,9 +78,10 @@ class Environment:
self._modules[name] = importlib.import_module(name)
module = self._modules[name]
if not module:
dist = next(iter(
dist for dist in self.base_working_set if dist.project_name == name
), None)
dist = next(
iter(dist for dist in self.base_working_set if dist.project_name == name),
None,
)
if dist:
dist.activate()
module = importlib.import_module(name)
@@ -152,7 +149,9 @@ class Environment:
if not os.path.exists(include_dir):
include_dirs = self.get_include_path()
if include_dirs:
include_path = include_dirs.get("include", include_dirs.get("platinclude"))
include_path = include_dirs.get(
"include", include_dirs.get("platinclude")
)
if not include_path:
return {}
include_dir = Path(include_path)
@@ -169,7 +168,8 @@ class Environment:
base, leaf = os.path.split(path)
base, parent = os.path.split(base)
leaf = os.path.join(parent, leaf).replace(
replace_version, self.python_info.get("py_version_short", get_python_version())
replace_version,
self.python_info.get("py_version_short", get_python_version()),
)
return os.path.join(base, leaf)
return path
@@ -213,16 +213,21 @@ class Environment:
try:
paths = self.get_paths()
except Exception:
install_scheme = 'nt' if (os.name == 'nt') else 'posix_prefix'
paths = get_paths(install_scheme, vars={
'base': prefix,
'platbase': prefix,
})
install_scheme = "nt" if (os.name == "nt") else "posix_prefix"
paths = get_paths(
install_scheme,
vars={
"base": prefix,
"platbase": prefix,
},
)
current_version = get_python_version()
try:
for k in list(paths.keys()):
if not os.path.exists(paths[k]):
paths[k] = self._replace_parent_version(paths[k], current_version)
paths[k] = self._replace_parent_version(
paths[k], current_version
)
except OSError:
# Sometimes virtualenvs are made using virtualenv interpreters and there is no
# include directory, which will cause this approach to fail. This failsafe
@@ -231,11 +236,14 @@ class Environment:
paths.update(self.get_lib_paths())
paths["scripts"] = self.script_basedir
if not paths:
install_scheme = 'nt' if (os.name == 'nt') else 'posix_prefix'
paths = get_paths(install_scheme, vars={
'base': prefix,
'platbase': prefix,
})
install_scheme = "nt" if (os.name == "nt") else "posix_prefix"
paths = get_paths(
install_scheme,
vars={
"base": prefix,
"platbase": prefix,
},
)
if not os.path.exists(paths["purelib"]) and not os.path.exists(paths["platlib"]):
lib_paths = self.get_lib_paths()
paths.update(lib_paths)
@@ -249,7 +257,7 @@ class Environment:
else:
lib_dirs = purelib + os.pathsep + platlib
paths["libdir"] = purelib
paths['PYTHONPATH'] = os.pathsep.join(["", ".", lib_dirs])
paths["PYTHONPATH"] = os.pathsep.join(["", ".", lib_dirs])
paths["libdirs"] = lib_dirs
return paths
@@ -258,11 +266,14 @@ class Environment:
# type: () -> str
"""Path to the environment scripts dir"""
prefix = make_posix(self.prefix.as_posix())
install_scheme = 'nt' if (os.name == 'nt') else 'posix_prefix'
paths = get_paths(install_scheme, vars={
'base': prefix,
'platbase': prefix,
})
install_scheme = "nt" if (os.name == "nt") else "posix_prefix"
paths = get_paths(
install_scheme,
vars={
"base": prefix,
"platbase": prefix,
},
)
return paths["scripts"]
@property
@@ -291,20 +302,30 @@ class Environment:
"""
from .vendor.vistir.compat import JSONDecodeError
current_executable = Path(sys.executable).as_posix()
if not self.python or self.python == current_executable:
return sys.path
elif any([sys.prefix == self.prefix, not self.is_venv]):
return sys.path
cmd_args = [self.python, "-c", "import json, sys; print(json.dumps(sys.path))"]
path, _ = vistir.misc.run(cmd_args, return_object=False, nospin=True, block=True, combine_stderr=False, write_to_stdout=False)
path, _ = vistir.misc.run(
cmd_args,
return_object=False,
nospin=True,
block=True,
combine_stderr=False,
write_to_stdout=False,
)
try:
path = json.loads(path.strip())
except JSONDecodeError:
path = sys.path
return path
def build_command(self, python_lib=False, python_inc=False, scripts=False, py_version=False):
def build_command(
self, python_lib=False, python_inc=False, scripts=False, py_version=False
):
# type: (bool, bool, bool, bool) -> str
"""Build the text for running a command in the given environment
@@ -332,16 +353,26 @@ class Environment:
dist_prefix = f"{key}lib"
# XXX: We need to get 'stdlib' or 'platstdlib'
sys_prefix = "{}stdlib".format("" if key == "pure" else key)
pylib_lines.append(f"u'{dist_prefix}': u'{{{{0}}}}'.format({distutils_line.format(var, val)})")
pylib_lines.append(f"u'{sys_prefix}': u'{{{{0}}}}'.format({sysconfig_line.format(sys_prefix)})")
pylib_lines.append(
f"u'{dist_prefix}': u'{{{{0}}}}'.format({distutils_line.format(var, val)})"
)
pylib_lines.append(
f"u'{sys_prefix}': u'{{{{0}}}}'.format({sysconfig_line.format(sys_prefix)})"
)
if python_inc:
for key, var, val in (("include", "inc", "0"), ("platinclude", "inc", "1")):
pylib_lines.append(f"u'{key}': u'{{{{0}}}}'.format({distutils_line.format(var, val)})")
pylib_lines.append(
f"u'{key}': u'{{{{0}}}}'.format({distutils_line.format(var, val)})"
)
lines = pylib_lines + pyinc_lines
if scripts:
lines.append("u'scripts': u'{{0}}'.format(%s)" % sysconfig_line.format("scripts"))
lines.append(
"u'scripts': u'{{0}}'.format(%s)" % sysconfig_line.format("scripts")
)
if py_version:
lines.append("u'py_version_short': u'{{0}}'.format(distutils.sysconfig.get_python_version()),")
lines.append(
"u'py_version_short': u'{{0}}'.format(distutils.sysconfig.get_python_version()),"
)
lines_as_str = ",".join(lines)
py_command = py_command % lines_as_str
return py_command
@@ -357,7 +388,9 @@ class Environment:
tmpfile = vistir.path.create_tracked_tempfile(suffix=".json")
tmpfile.close()
tmpfile_path = make_posix(tmpfile.name)
py_command = self.build_command(python_lib=True, python_inc=True, scripts=True, py_version=True)
py_command = self.build_command(
python_lib=True, python_inc=True, scripts=True, py_version=True
)
command = [self.python, "-c", py_command.format(tmpfile_path)]
c = subprocess_run(command)
if c.returncode == 0:
@@ -366,7 +399,14 @@ class Environment:
paths = json.load(fh)
if "purelib" in paths:
paths["libdir"] = paths["purelib"] = make_posix(paths["purelib"])
for key in ("platlib", "scripts", "platstdlib", "stdlib", "include", "platinclude"):
for key in (
"platlib",
"scripts",
"platstdlib",
"stdlib",
"include",
"platinclude",
):
if key in paths:
paths[key] = make_posix(paths[key])
return paths
@@ -405,16 +445,27 @@ class Environment:
if not paths:
if not self.prefix.joinpath("lib").exists():
return {}
stdlib_path = next(iter([
p for p in self.prefix.joinpath("lib").iterdir()
if p.name.startswith("python")
]), None)
stdlib_path = next(
iter(
[
p
for p in self.prefix.joinpath("lib").iterdir()
if p.name.startswith("python")
]
),
None,
)
lib_path = None
if stdlib_path:
lib_path = next(iter([
p.as_posix() for p in stdlib_path.iterdir()
if p.name == "site-packages"
]))
lib_path = next(
iter(
[
p.as_posix()
for p in stdlib_path.iterdir()
if p.name == "site-packages"
]
)
)
paths = {"stdlib": stdlib_path.as_posix()}
if lib_path:
paths["purelib"] = lib_path
@@ -503,9 +554,10 @@ class Environment:
when installing.
"""
from .vendor.packaging.version import parse as parse_version
pip = next(iter(
pkg for pkg in self.get_installed_packages() if pkg.key == "pip"
), None)
pip = next(
iter(pkg for pkg in self.get_installed_packages() if pkg.key == "pip"), None
)
if pip is not None:
return parse_version(pip.version)
return parse_version("20.2")
@@ -542,8 +594,12 @@ class Environment:
:rtype: iterator
"""
pip_target_dir = os.environ.get('PIP_TARGET')
libdirs = [pip_target_dir] if pip_target_dir else self.base_paths["libdirs"].split(os.pathsep)
pip_target_dir = os.environ.get("PIP_TARGET")
libdirs = (
[pip_target_dir]
if pip_target_dir
else self.base_paths["libdirs"].split(os.pathsep)
)
dists = (pkg_resources.find_distributions(libdir) for libdir in libdirs)
yield from itertools.chain.from_iterable(dists)
@@ -575,8 +631,10 @@ class Environment:
# type: (pkg_resources.Distribution) -> bool
"""Determine whether the supplied distribution is in the environment."""
from .environments import normalize_pipfile_path as _normalized
prefixes = [
_normalized(prefix) for prefix in self.base_paths["libdirs"].split(os.pathsep)
_normalized(prefix)
for prefix in self.base_paths["libdirs"].split(os.pathsep)
if _normalized(prefix).startswith(_normalized(self.prefix.as_posix()))
]
location = self.locate_dist(dist)
@@ -590,7 +648,8 @@ class Environment:
"""Returns all of the installed packages in a given environment"""
workingset = self.get_working_set()
packages = [
pkg for pkg in workingset
pkg
for pkg in workingset
if self.dist_is_in_project(pkg) and pkg.key != "python"
]
return packages
@@ -606,20 +665,23 @@ class Environment:
pip_options.cache_dir = self.project.s.PIPENV_CACHE_DIR
pip_options.pre = self.pipfile.get("pre", pre)
with pip_command._build_session(pip_options) as session:
finder = get_package_finder(install_cmd=pip_command, options=pip_options, session=session)
finder = get_package_finder(
install_cmd=pip_command, options=pip_options, session=session
)
yield finder
def get_package_info(self, pre=False):
# type: (bool) -> Generator[pkg_resources.Distribution, None, None]
from .vendor.pip_shims.shims import parse_version, pip_version
dependency_links = []
packages = self.get_installed_packages()
# This code is borrowed from pip's current implementation
if parse_version(pip_version) < parse_version("19.0"):
for dist in packages:
if dist.has_metadata('dependency_links.txt'):
if dist.has_metadata("dependency_links.txt"):
dependency_links.extend(
dist.get_metadata_lines('dependency_links.txt')
dist.get_metadata_lines("dependency_links.txt")
)
with self.get_finder() as finder:
@@ -627,24 +689,29 @@ class Environment:
finder.add_dependency_links(dependency_links)
for dist in packages:
typ = 'unknown'
typ = "unknown"
all_candidates = finder.find_all_candidates(dist.key)
if not self.pipfile.get("pre", finder.allow_all_prereleases):
# Remove prereleases
all_candidates = [
candidate for candidate in all_candidates
candidate
for candidate in all_candidates
if not candidate.version.is_prerelease
]
if not all_candidates:
continue
candidate_evaluator = finder.make_candidate_evaluator(project_name=dist.key)
best_candidate_result = candidate_evaluator.compute_best_candidate(all_candidates)
candidate_evaluator = finder.make_candidate_evaluator(
project_name=dist.key
)
best_candidate_result = candidate_evaluator.compute_best_candidate(
all_candidates
)
remote_version = best_candidate_result.best_candidate.version
if best_candidate_result.best_candidate.link.is_wheel:
typ = 'wheel'
typ = "wheel"
else:
typ = 'sdist'
typ = "sdist"
# This is dirty but makes the rest of the code much cleaner
dist.latest_version = remote_version
dist.latest_filetype = typ
@@ -653,7 +720,8 @@ class Environment:
def get_outdated_packages(self, pre=False):
# type: (bool) -> List[pkg_resources.Distribution]
return [
pkg for pkg in self.get_package_info(pre=pre)
pkg
for pkg in self.get_package_info(pre=pre)
if pkg.latest_version._key > pkg.parsed_version._key
]
@@ -664,15 +732,16 @@ class Environment:
d = node.as_dict()
if parent:
d['required_version'] = node.version_spec if node.version_spec else 'Any'
d["required_version"] = node.version_spec if node.version_spec else "Any"
else:
d['required_version'] = d['installed_version']
d["required_version"] = d["installed_version"]
get_children = lambda n: key_tree.get(n.key, []) # noqa
get_children = lambda n: key_tree.get(n.key, []) # noqa
d['dependencies'] = [
cls._get_requirements_for_package(c, key_tree, parent=node,
chain=chain+[c.project_name])
d["dependencies"] = [
cls._get_requirements_for_package(
c, key_tree, parent=node, chain=chain + [c.project_name]
)
for c in get_children(node)
if c.project_name not in chain
]
@@ -701,7 +770,7 @@ class Environment:
new_node = {
"package_name": node["package_name"],
"installed_version": node["installed_version"],
"required_version": node["required_version"]
"required_version": node["required_version"],
}
for dependency in node.get("dependencies", []):
for dep in cls.reverse_dependency(dependency):
@@ -712,6 +781,7 @@ class Environment:
def reverse_dependencies(self):
from vistir.misc import chunked, unnest
rdeps = {}
for req in self.get_package_requirements():
for d in self.reverse_dependency(req):
@@ -720,7 +790,7 @@ class Environment:
pkg = {
name: {
"installed": d["installed_version"],
"required": d["required_version"]
"required": d["required_version"],
}
}
parents = tuple(d.get("parent", ()))
@@ -735,7 +805,7 @@ class Environment:
entry = rdeps[k]
if entry.get("parents"):
rdeps[k]["parents"] = {
p for p, version in chunked(2, unnest(entry["parents"]))
p for p, version in chunked(2, unnest(entry["parents"]))
}
return rdeps
@@ -762,21 +832,26 @@ class Environment:
def is_satisfied(self, req):
match = next(
iter(
d for d in self.get_distributions()
d
for d in self.get_distributions()
if canonicalize_name(d.project_name) == req.normalized_name
), None
),
None,
)
if match is not None:
if req.editable and req.line_instance.is_local and self.find_egg(match):
requested_path = req.line_instance.path
return requested_path and vistir.compat.samefile(requested_path, match.location)
return requested_path and vistir.compat.samefile(
requested_path, match.location
)
elif match.has_metadata("direct_url.json"):
direct_url_metadata = json.loads(match.get_metadata("direct_url.json"))
commit_id = direct_url_metadata.get("vcs_info", {}).get("commit_id", "")
vcs_type = direct_url_metadata.get("vcs_info", {}).get("vcs", "")
_, pipfile_part = req.as_pipfile().popitem()
return (
vcs_type == req.vcs and commit_id == req.commit_hash
vcs_type == req.vcs
and commit_id == req.commit_hash
and direct_url_metadata["url"] == pipfile_part[req.vcs]
)
elif req.is_vcs or req.is_file_or_url:
@@ -801,7 +876,13 @@ class Environment:
c = None
with self.activated():
script = vistir.cmdparse.Script.parse(cmd)
c = vistir.misc.run(script._parts, return_object=True, nospin=True, cwd=cwd, write_to_stdout=False)
c = vistir.misc.run(
script._parts,
return_object=True,
nospin=True,
cwd=cwd,
write_to_stdout=False,
)
return c
def run_py(self, cmd, cwd=os.curdir):
@@ -820,7 +901,13 @@ class Environment:
else:
script = vistir.cmdparse.Script.parse([self.python, "-c"] + list(cmd))
with self.activated():
c = vistir.misc.run(script._parts, return_object=True, nospin=True, cwd=cwd, write_to_stdout=False)
c = vistir.misc.run(
script._parts,
return_object=True,
nospin=True,
cwd=cwd,
write_to_stdout=False,
)
return c
def run_activate_this(self):
@@ -865,30 +952,34 @@ class Environment:
self.add_dist("pip")
prefix = self.prefix.as_posix()
with vistir.contextmanagers.temp_environ(), vistir.contextmanagers.temp_path():
os.environ["PATH"] = os.pathsep.join([
vistir.compat.fs_str(self.script_basedir),
vistir.compat.fs_str(self.prefix.as_posix()),
os.environ.get("PATH", "")
])
os.environ["PATH"] = os.pathsep.join(
[
vistir.compat.fs_str(self.script_basedir),
vistir.compat.fs_str(self.prefix.as_posix()),
os.environ.get("PATH", ""),
]
)
os.environ["PYTHONIOENCODING"] = vistir.compat.fs_str("utf-8")
os.environ["PYTHONDONTWRITEBYTECODE"] = vistir.compat.fs_str("1")
if self.is_venv:
os.environ["PYTHONPATH"] = self.base_paths["PYTHONPATH"]
os.environ["VIRTUAL_ENV"] = vistir.compat.fs_str(prefix)
else:
if not self.project.s.PIPENV_USE_SYSTEM and not os.environ.get("VIRTUAL_ENV"):
if not self.project.s.PIPENV_USE_SYSTEM and not os.environ.get(
"VIRTUAL_ENV"
):
os.environ["PYTHONPATH"] = self.base_paths["PYTHONPATH"]
os.environ.pop("PYTHONHOME", None)
sys.path = self.sys_path
sys.prefix = self.sys_prefix
site.addsitedir(self.base_paths["purelib"])
pip = self.safe_import("pip") # noqa
pip = self.safe_import("pip") # noqa
pip_vendor = self.safe_import("pip._vendor")
pep517_dir = os.path.join(os.path.dirname(pip_vendor.__file__), "pep517")
site.addsitedir(pep517_dir)
os.environ["PYTHONPATH"] = os.pathsep.join([
os.environ.get("PYTHONPATH", self.base_paths["PYTHONPATH"]), pep517_dir
])
os.environ["PYTHONPATH"] = os.pathsep.join(
[os.environ.get("PYTHONPATH", self.base_paths["PYTHONPATH"]), pep517_dir]
)
if include_extras:
site.addsitedir(parent_path)
sys.path.extend([parent_path, patched_dir, vendor_dir])
@@ -905,6 +996,7 @@ class Environment:
@cached_property
def finders(self):
from pipenv.vendor.pythonfinder import Finder
finders = [
Finder(path=self.base_paths["scripts"], global_search=gs, system=False)
for gs in (False, True)
@@ -929,14 +1021,18 @@ class Environment:
install_arg = "install" if not editable else "develop"
install_keys = ["headers", "purelib", "platlib", "scripts", "data"]
install_args = [
self.environment.python, "-u", "-c", SETUPTOOLS_SHIM % setup_path,
install_arg, "--single-version-externally-managed", "--no-deps",
"--prefix={}".format(self.base_paths["prefix"]), "--no-warn-script-location"
self.environment.python,
"-u",
"-c",
SETUPTOOLS_SHIM % setup_path,
install_arg,
"--single-version-externally-managed",
"--no-deps",
"--prefix={}".format(self.base_paths["prefix"]),
"--no-warn-script-location",
]
for key in install_keys:
install_args.append(
f"--install-{key}={self.base_paths[key]}"
)
install_args.append(f"--install-{key}={self.base_paths[key]}")
return install_args
def install(self, requirements):
@@ -944,28 +1040,33 @@ class Environment:
requirements = [requirements]
with self.get_finder() as finder:
args = []
for format_control in ('no_binary', 'only_binary'):
for format_control in ("no_binary", "only_binary"):
formats = getattr(finder.format_control, format_control)
args.extend(('--' + format_control.replace('_', '-'),
','.join(sorted(formats or {':none:'}))))
args.extend(
(
"--" + format_control.replace("_", "-"),
",".join(sorted(formats or {":none:"})),
)
)
if finder.index_urls:
args.extend(['-i', finder.index_urls[0]])
args.extend(["-i", finder.index_urls[0]])
for extra_index in finder.index_urls[1:]:
args.extend(['--extra-index-url', extra_index])
args.extend(["--extra-index-url", extra_index])
else:
args.append('--no-index')
args.append("--no-index")
for link in finder.find_links:
args.extend(['--find-links', link])
args.extend(["--find-links", link])
for _, host, _ in finder.secure_origins:
args.extend(['--trusted-host', host])
args.extend(["--trusted-host", host])
if finder.allow_all_prereleases:
args.append('--pre')
args.append("--pre")
if finder.process_dependency_links:
args.append('--process-dependency-links')
args.append('--')
args.append("--process-dependency-links")
args.append("--")
args.extend(requirements)
out, _ = vistir.misc.run(args, return_object=False, nospin=True, block=True,
combine_stderr=False)
out, _ = vistir.misc.run(
args, return_object=False, nospin=True, block=True, combine_stderr=False
)
@contextlib.contextmanager
def uninstall(self, pkgname, *args, **kwargs):
@@ -983,18 +1084,21 @@ class Environment:
auto_confirm = kwargs.pop("auto_confirm", True)
verbose = kwargs.pop("verbose", False)
with self.activated():
monkey_patch = next(iter(
dist for dist in self.base_working_set
if dist.project_name == "recursive-monkey-patch"
), None)
monkey_patch = next(
iter(
dist
for dist in self.base_working_set
if dist.project_name == "recursive-monkey-patch"
),
None,
)
if monkey_patch:
monkey_patch.activate()
pip_shims = self.safe_import("pip_shims")
pathset_base = pip_shims.UninstallPathSet
pathset_base._permitted = PatchedUninstaller._permitted
dist = next(
iter(d for d in self.get_working_set() if d.project_name == pkgname),
None
iter(d for d in self.get_working_set() if d.project_name == pkgname), None
)
pathset = pathset_base.from_dist(dist)
if pathset is not None:
+20 -19
View File
@@ -10,7 +10,6 @@ from vistir.path import normalize_drive
from pipenv._compat import fix_utf8
from pipenv.vendor.vistir.misc import _isatty, fs_str
# HACK: avoid resolver.py uses the wrong byte code files.
# I hope I can remove this one day.
os.environ["PYTHONDONTWRITEBYTECODE"] = fs_str("1")
@@ -36,8 +35,7 @@ def env_to_bool(val):
def _is_env_truthy(name):
"""An environment variable is truthy if it exists and isn't one of (0, false, no, off)
"""
"""An environment variable is truthy if it exists and isn't one of (0, false, no, off)"""
if name not in os.environ:
return False
return os.environ.get(name).lower() not in _false_values
@@ -86,8 +84,8 @@ def normalize_pipfile_path(p):
except OSError:
loc = loc.absolute()
# Recase the path properly on Windows. From https://stackoverflow.com/a/35229734/5043728
if os.name == 'nt':
matches = glob.glob(re.sub(r'([^:/\\])(?=[/\\]|$)', r'[\1]', str(loc)))
if os.name == "nt":
matches = glob.glob(re.sub(r"([^:/\\])(?=[/\\]|$)", r"[\1]", str(loc)))
path_str = matches and matches[0] or str(loc)
else:
path_str = str(loc)
@@ -99,7 +97,7 @@ def normalize_pipfile_path(p):
os.environ.pop("__PYVENV_LAUNCHER__", None)
# Internal, to tell whether the command line session is interactive.
SESSION_IS_INTERACTIVE = _isatty(sys.stdout)
PIPENV_IS_CI = env_to_bool(os.environ.get('CI') or os.environ.get('TF_BUILD') or False)
PIPENV_IS_CI = env_to_bool(os.environ.get("CI") or os.environ.get("TF_BUILD") or False)
PIPENV_COLORBLIND = bool(os.environ.get("PIPENV_COLORBLIND"))
"""If set, disable terminal colors.
@@ -125,14 +123,18 @@ class Setting:
def initialize(self):
self.PIPENV_CACHE_DIR = os.environ.get("PIPENV_CACHE_DIR", user_cache_dir("pipenv"))
self.PIPENV_CACHE_DIR = os.environ.get(
"PIPENV_CACHE_DIR", user_cache_dir("pipenv")
)
"""Location for Pipenv to store it's package cache.
Default is to use appdir's user cache directory.
"""
# Tells Pipenv which Python to default to, when none is provided.
self.PIPENV_DEFAULT_PYTHON_VERSION = os.environ.get("PIPENV_DEFAULT_PYTHON_VERSION")
self.PIPENV_DEFAULT_PYTHON_VERSION = os.environ.get(
"PIPENV_DEFAULT_PYTHON_VERSION"
)
"""Use this Python version when creating new virtual environments by default.
This can be set to a version string, e.g. ``3.9``, or a path. Default is to use
@@ -180,7 +182,9 @@ class Setting:
and enables the user to use any user-built environments with Pipenv.
"""
self.PIPENV_INSTALL_TIMEOUT = int(os.environ.get("PIPENV_INSTALL_TIMEOUT", 60 * 15))
self.PIPENV_INSTALL_TIMEOUT = int(
os.environ.get("PIPENV_INSTALL_TIMEOUT", 60 * 15)
)
"""Max number of seconds to wait for package installation.
Defaults to 900 (15 minutes), a very long arbitrary time.
@@ -248,7 +252,7 @@ class Setting:
pipenv_pipfile = normalize_pipfile_path(pipenv_pipfile)
# Overwrite environment variable so that subprocesses can get the correct path.
# See https://github.com/pypa/pipenv/issues/3584
os.environ['PIPENV_PIPFILE'] = pipenv_pipfile
os.environ["PIPENV_PIPFILE"] = pipenv_pipfile
self.PIPENV_PIPFILE = pipenv_pipfile
"""If set, this specifies a custom Pipfile location.
@@ -332,10 +336,9 @@ class Setting:
Defaults to ``(w)ipe``
"""
self.PIPENV_RESOLVE_VCS = (
os.environ.get("PIPENV_RESOLVE_VCS") is None
or _is_env_truthy("PIPENV_RESOLVE_VCS")
)
self.PIPENV_RESOLVE_VCS = os.environ.get(
"PIPENV_RESOLVE_VCS"
) is None or _is_env_truthy("PIPENV_RESOLVE_VCS")
"""Tells Pipenv whether to resolve all VCS dependencies in full.
@@ -344,9 +347,7 @@ class Setting:
approach, you may set this to '0', 'off', or 'false'.
"""
self.PIPENV_PYUP_API_KEY = os.environ.get(
"PIPENV_PYUP_API_KEY", None
)
self.PIPENV_PYUP_API_KEY = os.environ.get("PIPENV_PYUP_API_KEY", None)
# Internal, support running in a different Python from sys.executable.
self.PIPENV_PYTHON = os.environ.get("PIPENV_PYTHON")
@@ -396,12 +397,12 @@ class Setting:
def is_using_venv():
# type: () -> bool
"""Check for venv-based virtual environment which sets sys.base_prefix"""
if getattr(sys, 'real_prefix', None) is not None:
if getattr(sys, "real_prefix", None) is not None:
# virtualenv venvs
result = True
else:
# PEP 405 venvs
result = sys.prefix != getattr(sys, 'base_prefix', sys.prefix)
result = sys.prefix != getattr(sys, "base_prefix", sys.prefix)
return result
+88 -76
View File
@@ -1,30 +1,29 @@
import itertools
import re
import sys
from collections import namedtuple
from traceback import format_tb
from pipenv import environments
from pipenv._compat import decode_for_output
from pipenv.patched import crayons
from pipenv.vendor.click.exceptions import (
ClickException, FileError, UsageError
)
from pipenv.vendor.vistir.misc import echo as click_echo
from pipenv.vendor import vistir
from pipenv.vendor.click.exceptions import ClickException, FileError, UsageError
from pipenv.vendor.vistir.misc import echo as click_echo
ANSI_REMOVAL_RE = re.compile(r"\033\[((?:\d|;)*)([a-zA-Z])", re.MULTILINE)
STRING_TYPES = ((str,), crayons.ColoredString)
if sys.version_info[:2] >= (3, 7):
KnownException = namedtuple(
'KnownException', ['exception_name', 'match_string', 'show_from_string', 'prefix'],
defaults=[None, None, None, ""]
"KnownException",
["exception_name", "match_string", "show_from_string", "prefix"],
defaults=[None, None, None, ""],
)
else:
KnownException = namedtuple(
'KnownException', ['exception_name', 'match_string', 'show_from_string', 'prefix'],
"KnownException",
["exception_name", "match_string", "show_from_string", "prefix"],
)
KnownException.__new__.__defaults__ = (None, None, None, "")
@@ -33,8 +32,8 @@ KNOWN_EXCEPTIONS = [
KnownException(
"VirtualenvCreationException",
match_string="do_create_virtualenv",
show_from_string=None
)
show_from_string=None,
),
]
@@ -51,9 +50,7 @@ def handle_exception(exc_type, exception, traceback, hook=sys.excepthook):
line = f" {line}"
else:
line = f" {line}"
line = "[{!s}]: {}".format(
exception.__class__.__name__, line
)
line = "[{!s}]: {}".format(exception.__class__.__name__, line)
formatted_lines.append(line)
# use new exception prettification rules to format exceptions according to
# UX rules
@@ -102,20 +99,27 @@ class PipenvCmdError(PipenvException):
def show(self, file=None):
if file is None:
file = vistir.misc.get_text_stderr()
click_echo("{} {}".format(
crayons.red("Error running command: "),
crayons.normal(decode_for_output(f"$ {self.cmd}", file), bold=True)
), err=True)
click_echo(
"{} {}".format(
crayons.red("Error running command: "),
crayons.normal(decode_for_output(f"$ {self.cmd}", file), bold=True),
),
err=True,
)
if self.out:
click_echo("{} {}".format(
crayons.normal("OUTPUT: "),
decode_for_output(self.out, file)
), err=True)
click_echo(
"{} {}".format(
crayons.normal("OUTPUT: "), decode_for_output(self.out, file)
),
err=True,
)
if self.err:
click_echo("{} {}".format(
crayons.normal("STDERR: "),
decode_for_output(self.err, file)
), err=True)
click_echo(
"{} {}".format(
crayons.normal("STDERR: "), decode_for_output(self.err, file)
),
err=True,
)
class JSONParseError(PipenvException):
@@ -128,18 +132,20 @@ class JSONParseError(PipenvException):
file = vistir.misc.get_text_stderr()
message = "{}\n{}".format(
crayons.normal("Failed parsing JSON results:", bold=True),
decode_for_output(self.message.strip(), file)
decode_for_output(self.message.strip(), file),
)
click_echo(message, err=True)
if self.error_text:
click_echo("{} {}".format(
crayons.normal("ERROR TEXT:", bold=True),
decode_for_output(self.error_text, file)
), err=True)
click_echo(
"{} {}".format(
crayons.normal("ERROR TEXT:", bold=True),
decode_for_output(self.error_text, file),
),
err=True,
)
class PipenvUsageError(UsageError):
def __init__(self, message=None, ctx=None, **kwargs):
formatted_message = "{0}: {1}"
msg_prefix = crayons.red("ERROR:", bold=True)
@@ -164,29 +170,30 @@ class PipenvUsageError(UsageError):
if color:
extra = getattr(crayons, color, "blue")(extra)
click_echo(decode_for_output(extra, file), file=file)
hint = ''
hint = ""
if self.cmd is not None and self.cmd.get_help_option(self.ctx) is not None:
hint = ('Try "%s %s" for help.\n'
% (self.ctx.command_path, self.ctx.help_option_names[0]))
hint = 'Try "%s %s" for help.\n' % (
self.ctx.command_path,
self.ctx.help_option_names[0],
)
if self.ctx is not None:
click_echo(self.ctx.get_usage() + '\n%s' % hint, file=file, color=color)
click_echo(self.ctx.get_usage() + "\n%s" % hint, file=file, color=color)
click_echo(self.message, file=file)
class PipenvFileError(FileError):
formatted_message = "{0} {{0}} {{1}}".format(
crayons.red("ERROR:", bold=True)
)
formatted_message = "{0} {{0}} {{1}}".format(crayons.red("ERROR:", bold=True))
def __init__(self, filename, message=None, **kwargs):
extra = kwargs.pop("extra", [])
if not message:
message = crayons.normal("Please ensure that the file exists!", bold=True)
message = self.formatted_message.format(
crayons.normal(f"{filename} not found!", bold=True),
message
crayons.normal(f"{filename} not found!", bold=True), message
)
FileError.__init__(
self, filename=filename, hint=decode_for_output(message), **kwargs
)
FileError.__init__(self, filename=filename, hint=decode_for_output(message), **kwargs)
self.extra = extra
def show(self, file=None):
@@ -203,14 +210,13 @@ class PipenvFileError(FileError):
class PipfileNotFound(PipenvFileError):
def __init__(self, filename="Pipfile", extra=None, **kwargs):
extra = kwargs.pop("extra", [])
message = (
"{} {}".format(
crayons.red("Aborting!", bold=True),
crayons.normal(
"Please ensure that the file exists and is located in your"
" project root directory.", bold=True
)
)
message = "{} {}".format(
crayons.red("Aborting!", bold=True),
crayons.normal(
"Please ensure that the file exists and is located in your"
" project root directory.",
bold=True,
),
)
super().__init__(filename, message=message, extra=extra, **kwargs)
@@ -221,7 +227,7 @@ class LockfileNotFound(PipenvFileError):
message = "{} {} {}".format(
crayons.normal("You need to run", bold=True),
crayons.red("$ pipenv lock", bold=True),
crayons.normal("before you can continue.", bold=True)
crayons.normal("before you can continue.", bold=True),
)
super().__init__(filename, message=message, extra=extra, **kwargs)
@@ -264,7 +270,6 @@ class SetupException(PipenvException):
class VirtualenvException(PipenvException):
def __init__(self, message=None, **kwargs):
if not message:
message = (
@@ -309,7 +314,7 @@ class UninstallError(PipenvException):
extra = [
"{} {}".format(
crayons.cyan("Attempted to run command: "),
crayons.yellow(f"$ {command!r}", bold=True)
crayons.yellow(f"$ {command!r}", bold=True),
)
]
extra.extend([crayons.cyan(line.strip()) for line in return_values.splitlines()])
@@ -317,7 +322,7 @@ class UninstallError(PipenvException):
package = " ".join(package)
message = "{!s} {!s}...".format(
crayons.normal("Failed to uninstall package(s)"),
crayons.yellow(f"{package}!s", bold=True)
crayons.yellow(f"{package}!s", bold=True),
)
self.exit_code = return_code
PipenvException.__init__(self, message=message, extra=extra)
@@ -332,8 +337,7 @@ class InstallError(PipenvException):
crayons.normal(f"{package!s}", bold=True)
)
message = "{} {}".format(
f"{package_message}",
crayons.yellow("Package installation failed...")
f"{package_message}", crayons.yellow("Package installation failed...")
)
extra = kwargs.pop("extra", [])
PipenvException.__init__(self, message=message, extra=extra, **kwargs)
@@ -344,17 +348,21 @@ class CacheError(PipenvException):
message = "{} {}\n{}".format(
crayons.cyan("Corrupt cache file"),
crayons.normal(f"{path!s}"),
crayons.normal('Consider trying "pipenv lock --clear" to clear the cache.')
crayons.normal('Consider trying "pipenv lock --clear" to clear the cache.'),
)
PipenvException.__init__(self, message=message)
class DependencyConflict(PipenvException):
def __init__(self, message):
extra = ["{} {}".format(
crayons.red("The operation failed...", bold=True),
crayons.red("A dependency conflict was detected and could not be resolved."),
)]
extra = [
"{} {}".format(
crayons.red("The operation failed...", bold=True),
crayons.red(
"A dependency conflict was detected and could not be resolved."
),
)
]
PipenvException.__init__(self, message, extra=extra)
@@ -382,21 +390,28 @@ class ResolutionFailure(PipenvException):
crayons.cyan(
"Please check your version specifier and version number. "
"See PEP440 for more information."
)
),
)
PipenvException.__init__(self, message, extra=extra)
class RequirementError(PipenvException):
def __init__(self, req=None):
from .utils import VCS_LIST
keys = ("name", "path",) + VCS_LIST + ("line", "uri", "url", "relpath")
keys = (
(
"name",
"path",
)
+ VCS_LIST
+ ("line", "uri", "url", "relpath")
)
if req is not None:
possible_display_values = [getattr(req, value, None) for value in keys]
req_value = next(iter(
val for val in possible_display_values if val is not None
), None)
req_value = next(
iter(val for val in possible_display_values if val is not None), None
)
if not req_value:
getstate_fn = getattr(req, "__getstate__", None)
slots = getattr(req, "__slots__", None)
@@ -405,22 +420,17 @@ class RequirementError(PipenvException):
req_value = getstate_fn()
elif slots:
slot_vals = [
(k, getattr(req, k, None)) for k in slots
if getattr(req, k, None)
(k, getattr(req, k, None)) for k in slots if getattr(req, k, None)
]
req_value = "\n".join([
f" {k}: {v}" for k, v in slot_vals
])
req_value = "\n".join([f" {k}: {v}" for k, v in slot_vals])
elif keys_fn:
values = [(k, req.get(k)) for k in keys_fn() if req.get(k)]
req_value = "\n".join([
f" {k}: {v}" for k, v in values
])
req_value = "\n".join([f" {k}: {v}" for k, v in values])
else:
req_value = getattr(req.line_instance, "line", None)
message = "{} {}".format(
crayons.normal(decode_for_output("Failed creating requirement instance")),
crayons.normal(decode_for_output(f"{req_value!r}"))
crayons.normal(decode_for_output(f"{req_value!r}")),
)
extra = [str(req)]
PipenvException.__init__(self, message, extra=extra)
@@ -432,7 +442,9 @@ def prettify_exc(error):
errors = []
for exc in KNOWN_EXCEPTIONS:
search_string = exc.match_string if exc.match_string else exc.exception_name
split_string = exc.show_from_string if exc.show_from_string else exc.exception_name
split_string = (
exc.show_from_string if exc.show_from_string else exc.exception_name
)
if search_string in error:
# for known exceptions with no display rules and no prefix
# we should simply show nothing
+2 -4
View File
@@ -3,7 +3,6 @@ import pprint
import sys
import pipenv
from pipenv.pep508checker import lookup
from pipenv.vendor import pythonfinder
@@ -69,9 +68,7 @@ def get_pipenv_diagnostics(project):
print("")
if project.lockfile_exists:
print("")
print_utf(
f"Contents of `Pipfile.lock` ({project.lockfile_location!r}):"
)
print_utf(f"Contents of `Pipfile.lock` ({project.lockfile_location!r}):")
print("")
print("```json")
with open(project.lockfile_location) as f:
@@ -82,4 +79,5 @@ def get_pipenv_diagnostics(project):
if __name__ == "__main__":
from pipenv.project import Project
get_pipenv_diagnostics(Project())
+42 -38
View File
@@ -1,12 +1,12 @@
import os
import operator
import os
import re
import sys
from abc import ABCMeta, abstractmethod
from pipenv.vendor import attr
from pipenv.utils.processes import subprocess_run
from pipenv.utils.shell import find_windows_executable
from pipenv.vendor import attr
@attr.s
@@ -20,15 +20,14 @@ class Version:
parts = [self.major, self.minor]
if self.patch is not None:
parts.append(self.patch)
return '.'.join(str(p) for p in parts)
return ".".join(str(p) for p in parts)
@classmethod
def parse(cls, name):
"""Parse an X.Y.Z or X.Y string into a version tuple.
"""
match = re.match(r'^(\d+)\.(\d+)(?:\.(\d+))?$', name)
"""Parse an X.Y.Z or X.Y string into a version tuple."""
match = re.match(r"^(\d+)\.(\d+)(?:\.(\d+))?$", name)
if not match:
raise ValueError(f'invalid version name {name!r}')
raise ValueError(f"invalid version name {name!r}")
major = int(match.group(1))
minor = int(match.group(2))
patch = match.group(3)
@@ -47,8 +46,7 @@ class Version:
return (self.major, self.minor, self.patch or 0)
def matches_minor(self, other):
"""Check whether this version matches the other in (major, minor).
"""
"""Check whether this version matches the other in (major, minor)."""
return (self.major, self.minor) == (other.major, other.minor)
@@ -64,7 +62,6 @@ class InstallerError(RuntimeError):
class Installer(metaclass=ABCMeta):
def __init__(self, project):
self.cmd = self._find_installer()
self.project = project
@@ -103,32 +100,37 @@ class Installer(metaclass=ABCMeta):
# Look for the Python installer using the equivalent of 'which'. On
# Homebrew-installed systems, the env var may not be set, but this
# strategy will work.
find_windows_executable('', name),
find_windows_executable("", name),
# Check for explicitly set install locations (e.g. PYENV_ROOT, ASDF_DIR).
os.path.join(os.path.expanduser(os.getenv(env_var, '/dev/null')), 'bin', name),
os.path.join(
os.path.expanduser(os.getenv(env_var, "/dev/null")), "bin", name
),
# Check the pyenv/asdf-recommended from-source install locations
os.path.join(os.path.expanduser(f'~/.{name}'), 'bin', name),
os.path.join(os.path.expanduser(f"~/.{name}"), "bin", name),
):
if candidate is not None and os.path.isfile(candidate) and os.access(candidate, os.X_OK):
if (
candidate is not None
and os.path.isfile(candidate)
and os.access(candidate, os.X_OK)
):
return candidate
raise InstallerNotFound()
def _run(self, *args, **kwargs):
timeout = kwargs.pop('timeout', 30)
shell = kwargs.pop('shell', False)
timeout = kwargs.pop("timeout", 30)
shell = kwargs.pop("shell", False)
if kwargs:
k = list(kwargs.keys())[0]
raise TypeError(f'unexpected keyword argument {k!r}')
raise TypeError(f"unexpected keyword argument {k!r}")
args = (self.cmd,) + tuple(args)
c = subprocess_run(args, timeout=timeout, shell=shell)
if c.returncode != 0:
raise InstallerError(f'failed to run {args}', c)
raise InstallerError(f"failed to run {args}", c)
return c
@abstractmethod
def iter_installable_versions(self):
"""Iterate through CPython versions available for Pipenv to install.
"""
"""Iterate through CPython versions available for Pipenv to install."""
pass
def find_version_to_install(self, name):
@@ -140,14 +142,17 @@ class Installer(metaclass=ABCMeta):
if version.patch is not None:
return name
try:
best_match = max((
inst_version
for inst_version in self.iter_installable_versions()
if inst_version.matches_minor(version)
), key=operator.attrgetter('cmpkey'))
best_match = max(
(
inst_version
for inst_version in self.iter_installable_versions()
if inst_version.matches_minor(version)
),
key=operator.attrgetter("cmpkey"),
)
except ValueError:
raise ValueError(
f'no installable version found for {name!r}',
f"no installable version found for {name!r}",
)
return best_match
@@ -168,17 +173,16 @@ class Pyenv(Installer):
WIN = sys.platform.startswith("win") or (sys.platform == "cli" and os.name == "nt")
def _find_installer(self):
return self._find_python_installer_by_name_and_env('pyenv', 'PYENV_ROOT')
return self._find_python_installer_by_name_and_env("pyenv", "PYENV_ROOT")
def _run(self, *args, **kwargs):
if Pyenv.WIN:
kwargs['shell'] = True
kwargs["shell"] = True
return super(Pyenv, self)._run(*args, **kwargs)
def iter_installable_versions(self):
"""Iterate through CPython versions available for Pipenv to install.
"""
for name in self._run('install', '--list').stdout.splitlines():
"""Iterate through CPython versions available for Pipenv to install."""
for name in self._run("install", "--list").stdout.splitlines():
try:
version = Version.parse(name.strip())
except ValueError:
@@ -192,7 +196,7 @@ class Pyenv(Installer):
A ValueError is raised if the given version does not have a match in
pyenv. A InstallerError is raised if the pyenv command fails.
"""
args = ['install', '-s', str(version)]
args = ["install", "-s", str(version)]
if Pyenv.WIN:
# pyenv-win skips installed versions by default and does not support -s
del args[1]
@@ -200,14 +204,12 @@ class Pyenv(Installer):
class Asdf(Installer):
def _find_installer(self):
return self._find_python_installer_by_name_and_env('asdf', 'ASDF_DIR')
return self._find_python_installer_by_name_and_env("asdf", "ASDF_DIR")
def iter_installable_versions(self):
"""Iterate through CPython versions available for asdf to install.
"""
for name in self._run('list-all', 'python').stdout.splitlines():
"""Iterate through CPython versions available for asdf to install."""
for name in self._run("list-all", "python").stdout.splitlines():
try:
version = Version.parse(name.strip())
except ValueError:
@@ -222,7 +224,9 @@ class Asdf(Installer):
asdf. A InstallerError is raised if the asdf command fails.
"""
c = self._run(
'install', 'python', str(version),
"install",
"python",
str(version),
timeout=self.project.s.PIPENV_INSTALL_TIMEOUT,
)
return c
+5 -5
View File
@@ -1058,28 +1058,28 @@ Update vendored dependencies and invocations
.INDENT 2.0
.IP \(bu 2
Update vendored and patched dependencies
\- Update patches on \fBpiptools\fP, \fBpip\fP, \fBpip\-shims\fP,
\- Update patches on \fBpiptools\fP, \fBpip\fP, \fBpip\-shims\fP,
.nf
\(ga\(ga
.fi
tomlkit\(ga
.IP \(bu 2
Fix invocations of dependencies
\- Fix custom
\- Fix custom
.nf
\(ga\(ga
.fi
InstallCommand\(ga instantiation
\- Update
\- Update
.nf
\(ga\(ga
.fi
PackageFinder\(ga usage
\- Fix
\- Fix
.nf
\(ga\(ga
.fi
Bool\(ga stringify attempts from
Bool\(ga stringify attempts from
.nf
\(ga\(ga
.fi
+15 -5
View File
@@ -11,18 +11,28 @@ class PopenProcess:
"""A wrapper of subprocess.Popen that
doesn't need to worry about the Pipe buffer exceeding the limit.
"""
def __init__(
self, args, *, block=True, encoding=DEFAULT_ENCODING, env=None, timeout=None, **other_kwargs
self,
args,
*,
block=True,
encoding=DEFAULT_ENCODING,
env=None,
timeout=None,
**other_kwargs
):
self.blocking = block
self.env = env
self.script = Script.parse(args)
if env is not None:
env = dict(os.environ, **env)
other_kwargs['env'] = env
other_kwargs['stdout'] = subprocess.PIPE
other_kwargs['stderr'] = subprocess.PIPE
self._process = subprocess.Popen(args, universal_newlines=True, encoding=encoding, **other_kwargs)
other_kwargs["env"] = env
other_kwargs["stdout"] = subprocess.PIPE
other_kwargs["stderr"] = subprocess.PIPE
self._process = subprocess.Popen(
args, universal_newlines=True, encoding=encoding, **other_kwargs
)
self._endtime = None
if timeout is not None:
self._endtime = _time() + timeout
-1
View File
@@ -15,7 +15,6 @@ import crayons
from pipenv.environments import PIPENV_COLORBLIND, PIPENV_HIDE_EMOJIS
STREAM = sys.stderr
MILL_TEMPLATE = "%s %s %i/%i\r"
DOTS_CHAR = "."
+86 -68
View File
@@ -6,10 +6,10 @@ import io
import json
import operator
import os
from pathlib import Path
import re
import sys
import urllib.parse
from pathlib import Path
import pipfile
import pipfile.api
@@ -20,11 +20,21 @@ import vistir
from pipenv.cmdparse import Script
from pipenv.core import system_which
from pipenv.environment import Environment
from pipenv.environments import Setting, is_type_checking, is_in_virtualenv, normalize_pipfile_path
from pipenv.utils.dependencies import get_canonical_names, is_editable, is_installable_file, is_star, python_version
from pipenv.environments import (
Setting,
is_in_virtualenv,
is_type_checking,
normalize_pipfile_path,
)
from pipenv.utils.dependencies import (
get_canonical_names,
is_editable,
is_installable_file,
is_star,
python_version,
)
from pipenv.utils.internet import get_url_name, is_valid_url, proper_case
from pipenv.utils.resolver import pep423_name
from pipenv.utils.toml import cleanup_toml, convert_toml_outline_tables
from pipenv.utils.shell import (
find_requirements,
find_windows_executable,
@@ -32,19 +42,17 @@ from pipenv.utils.shell import (
get_workon_home,
is_virtual_environment,
looks_like_dir,
safe_expandvars
safe_expandvars,
)
from pipenv.utils.toml import cleanup_toml, convert_toml_outline_tables
from pipenv.vendor.cached_property import cached_property
from pipenv.vendor.requirementslib.models.utils import (
get_default_pyproject_backend
)
from pipenv.vendor.requirementslib.models.utils import get_default_pyproject_backend
if is_type_checking():
from typing import Dict, List, Optional, Set, Text, Tuple, Union
import pkg_resources
TSource = Dict[Text, Union[Text, bool]]
TPackageEntry = Dict[str, Union[bool, str, List[str]]]
TPackage = Dict[str, TPackageEntry]
@@ -114,22 +122,20 @@ class Project:
self._requirements_location = None
self._original_dir = os.path.abspath(os.curdir)
self._environment = None
self._build_system = {
"requires": ["setuptools", "wheel"]
}
self._build_system = {"requires": ["setuptools", "wheel"]}
self.python_version = python_version
self.s = Setting()
if self.s.PIPENV_TEST_INDEX:
self.default_source = {
u"url": self.s.PIPENV_TEST_INDEX,
u"verify_ssl": True,
u"name": u"custom",
"url": self.s.PIPENV_TEST_INDEX,
"verify_ssl": True,
"name": "custom",
}
else:
self.default_source = {
u"url": u"https://pypi.org/simple",
u"verify_ssl": True,
u"name": u"pypi",
"url": "https://pypi.org/simple",
"verify_ssl": True,
"name": "pypi",
}
pipfile.api.DEFAULT_SOURCE = self.default_source
@@ -151,6 +157,7 @@ class Project:
def _build_package_list(self, package_section):
"""Returns a list of packages for pip-tools to consume."""
from pipenv.vendor.requirementslib.utils import is_vcs
ps = {}
# TODO: Separate the logic for showing packages from the filters for supplying pip-tools
for k, v in self.parsed_pipfile.get(package_section, {}).items():
@@ -217,9 +224,7 @@ class Project:
def required_python_version(self):
# type: () -> str
if self.pipfile_exists:
required = self.parsed_pipfile.get("requires", {}).get(
"python_full_version"
)
required = self.parsed_pipfile.get("requires", {}).get("python_full_version")
if not required:
required = self.parsed_pipfile.get("requires", {}).get("python_version")
if required != "*":
@@ -293,8 +298,10 @@ class Project:
def working_set(self):
# type: () -> pkg_resources.WorkingSet
from pipenv.utils.shell import load_path
sys_path = load_path(self.which("python"))
import pkg_resources
return pkg_resources.WorkingSet(sys_path)
@property
@@ -314,7 +321,7 @@ class Project:
return {
"dev": dev_keys,
"default": default_keys,
"combined": dev_keys | default_keys
"combined": dev_keys | default_keys,
}
@property
@@ -325,7 +332,7 @@ class Project:
return {
"dev": dev_keys,
"default": default_keys,
"combined": dev_keys | default_keys
"combined": dev_keys | default_keys,
}
def get_environment(self, allow_global=False):
@@ -339,8 +346,12 @@ class Project:
python = None
sources = self.sources if self.sources else [self.default_source]
environment = Environment(
prefix=prefix, python=python, is_venv=is_venv, sources=sources,
pipfile=self.parsed_pipfile, project=self
prefix=prefix,
python=python,
is_venv=is_venv,
sources=sources,
pipfile=self.parsed_pipfile,
project=self,
)
pipenv_dist = get_pipenv_dist(pkg="pipenv")
if pipenv_dist:
@@ -442,7 +453,8 @@ class Project:
virtualenv_env = os.getenv("VIRTUAL_ENV")
if (
"PIPENV_ACTIVE" not in os.environ
and not self.s.PIPENV_IGNORE_VIRTUALENVS and virtualenv_env
and not self.s.PIPENV_IGNORE_VIRTUALENVS
and virtualenv_env
):
return virtualenv_env
@@ -491,7 +503,7 @@ class Project:
# type: (str) -> None
"""Registers a proper name to the database."""
with self.proper_names_db_path.open("a") as f:
f.write(u"{0}\n".format(name))
f.write("{0}\n".format(name))
@property
def pipfile_location(self):
@@ -632,8 +644,8 @@ class Project:
@property
def _pipfile(self):
from .vendor.requirementslib.models.pipfile import \
Pipfile as ReqLibPipfile
from .vendor.requirementslib.models.pipfile import Pipfile as ReqLibPipfile
pf = ReqLibPipfile.load(self.pipfile_location)
return pf
@@ -660,6 +672,7 @@ class Project:
def _get_vcs_packages(self, dev=False):
from pipenv.vendor.requirementslib.utils import is_vcs
section = "dev-packages" if dev else "packages"
packages = {
k: v
@@ -727,15 +740,13 @@ class Project:
source_name = "pip_index_{}".format(i)
verify_ssl = index.startswith("https")
sources.append(
{u"url": index, u"verify_ssl": verify_ssl, u"name": source_name}
)
sources.append({"url": index, "verify_ssl": verify_ssl, "name": source_name})
data = {
u"source": sources,
"source": sources,
# Default packages.
u"packages": {},
u"dev-packages": {},
"packages": {},
"dev-packages": {},
}
# Default requires.
required_python = python
@@ -746,7 +757,7 @@ class Project:
required_python = self.which("python")
version = python_version(required_python) or self.s.PIPENV_DEFAULT_PYTHON_VERSION
if version and len(version.split(".")) > 2:
data[u"requires"] = {"python_version": ".".join(version.split(".")[:2])}
data["requires"] = {"python_version": ".".join(version.split(".")[:2])}
self.write_toml(data)
@classmethod
@@ -762,13 +773,15 @@ class Project:
return source
def get_or_create_lockfile(self, from_pipfile=False):
from pipenv.vendor.requirementslib.models.lockfile import \
Lockfile as Req_Lockfile
from pipenv.vendor.requirementslib.models.lockfile import (
Lockfile as Req_Lockfile,
)
lockfile = None
if from_pipfile and self.pipfile_exists:
lockfile_dict = {
"default": self._lockfile["default"].copy(),
"develop": self._lockfile["develop"].copy()
"develop": self._lockfile["develop"].copy(),
}
lockfile_dict.update({"_meta": self.get_lockfile_meta()})
lockfile = Req_Lockfile.from_data(
@@ -778,9 +791,13 @@ class Project:
try:
lockfile = Req_Lockfile.load(self.lockfile_location)
except OSError:
lockfile = Req_Lockfile.from_data(self.lockfile_location, self.lockfile_content)
lockfile = Req_Lockfile.from_data(
self.lockfile_location, self.lockfile_content
)
else:
lockfile = Req_Lockfile.from_data(path=self.lockfile_location, data=self._lockfile, meta_from_project=False)
lockfile = Req_Lockfile.from_data(
path=self.lockfile_location, data=self._lockfile, meta_from_project=False
)
if lockfile._lockfile is not None:
return lockfile
if self.lockfile_exists and self.lockfile_content:
@@ -790,9 +807,7 @@ class Project:
sources = self.pipfile_sources
elif not isinstance(sources, list):
sources = [sources]
lockfile_dict["_meta"]["sources"] = [
self.populate_source(s) for s in sources
]
lockfile_dict["_meta"]["sources"] = [self.populate_source(s) for s in sources]
_created_lockfile = Req_Lockfile.from_data(
path=self.lockfile_location, data=lockfile_dict, meta_from_project=False
)
@@ -803,6 +818,7 @@ class Project:
def get_lockfile_meta(self):
from .vendor.plette.lockfiles import PIPFILE_SPEC_CURRENT
if self.lockfile_exists:
sources = self.lockfile_content.get("_meta", {}).get("sources", [])
elif "source" in self.parsed_pipfile:
@@ -815,7 +831,7 @@ class Project:
"hash": {"sha256": self.calculate_pipfile_hash()},
"pipfile-spec": PIPFILE_SPEC_CURRENT,
"sources": [self.populate_source(s) for s in sources],
"requires": self.parsed_pipfile.get("requires", {})
"requires": self.parsed_pipfile.get("requires", {}),
}
def write_toml(self, data, path=None):
@@ -836,13 +852,12 @@ class Project:
table.update(data[section][package])
document[section][package] = table
else:
document[section][package] = tomlkit.string(data[section][package])
document[section][package] = tomlkit.string(
data[section][package]
)
formatted_data = tomlkit.dumps(document).rstrip()
if (
Path(path).absolute()
== Path(self.pipfile_location).absolute()
):
if Path(path).absolute() == Path(self.pipfile_location).absolute():
newlines = self._pipfile_newlines
else:
newlines = DEFAULT_NEWLINES
@@ -853,8 +868,7 @@ class Project:
self.clear_pipfile_cache()
def write_lockfile(self, content):
"""Write out the lockfile.
"""
"""Write out the lockfile."""
s = self._lockfile_encoder.encode(content)
open_kwargs = {"newline": self._lockfile_newlines, "encoding": "utf-8"}
with vistir.contextmanagers.atomic_open_for_write(
@@ -863,8 +877,8 @@ class Project:
f.write(s)
# Write newline at end of document. GH-319.
# Only need '\n' here; the file object handles the rest.
if not s.endswith(u"\n"):
f.write(u"\n")
if not s.endswith("\n"):
f.write("\n")
@property
def pipfile_sources(self):
@@ -914,14 +928,18 @@ class Project:
def find_source(sources, name=None, url=None):
source = None
if name:
source = next(iter(
s for s in sources if "name" in s and s["name"] == name
), None)
source = next(
iter(s for s in sources if "name" in s and s["name"] == name), None
)
elif url:
source = next(iter(
s for s in sources
if "url" in s and is_url_equal(url, s.get("url", ""))
), None)
source = next(
iter(
s
for s in sources
if "url" in s and is_url_equal(url, s.get("url", ""))
),
None,
)
if source is not None:
return source
@@ -961,9 +979,9 @@ class Project:
packages = set([pep423_name(pkg) for pkg in packages])
for section in ("dev-packages", "packages"):
pipfile_section = parsed.get(section, {})
pipfile_packages = set([
pep423_name(pkg_name) for pkg_name in pipfile_section.keys()
])
pipfile_packages = set(
[pep423_name(pkg_name) for pkg_name in pipfile_section.keys()]
)
to_remove = packages & pipfile_packages
# The normal toml parser can't handle deleting packages with preceding newlines
is_dev = section == "dev-packages"
@@ -995,9 +1013,7 @@ class Project:
self.write_toml(p)
def src_name_from_url(self, index_url):
name, _, tld_guess = urllib.parse.urlsplit(index_url).netloc.rpartition(
"."
)
name, _, tld_guess = urllib.parse.urlsplit(index_url).netloc.rpartition(".")
src_name = name.replace(".", "")
try:
self.get_source(name=src_name)
@@ -1005,6 +1021,7 @@ class Project:
name = src_name
else:
from random import randint
name = "{0}-{1}".format(src_name, randint(1, 1000))
return name
@@ -1109,6 +1126,7 @@ class Project:
@cached_property
def finders(self):
from .vendor.pythonfinder import Finder
scripts_dirname = "Scripts" if os.name == "nt" else "bin"
scripts_dir = os.path.join(self.virtualenv_location, scripts_dirname)
finders = [
+161 -71
View File
@@ -3,12 +3,12 @@ import logging
import os
import sys
os.environ["PIP_PYTHON_PATH"] = str(sys.executable)
def find_site_path(pkg, site_dir=None):
import pkg_resources
if site_dir is None:
site_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
working_set = pkg_resources.WorkingSet([site_dir] + sys.path[:])
@@ -17,7 +17,16 @@ def find_site_path(pkg, site_dir=None):
base_name = dist.project_name if dist.project_name else dist.key
name = None
if "top_level.txt" in dist.metadata_listdir(""):
name = next(iter([line.strip() for line in dist.get_metadata_lines("top_level.txt") if line is not None]), None)
name = next(
iter(
[
line.strip()
for line in dist.get_metadata_lines("top_level.txt")
if line is not None
]
),
None,
)
if name is None:
name = pkg_resources.safe_name(base_name).replace("-", "_")
if not any(pkg == _ for _ in [base_name, name]):
@@ -26,15 +35,15 @@ def find_site_path(pkg, site_dir=None):
path_options = [os.path.join(root, p) for p in path_options if p is not None]
path = next(iter(p for p in path_options if os.path.exists(p)), None)
if path is not None:
return (dist, path)
return (None, None)
return dist, path
return None, None
def _patch_path(pipenv_site=None):
import site
pipenv_libdir = os.path.dirname(os.path.abspath(__file__))
pipenv_site_dir = os.path.dirname(pipenv_libdir)
pipenv_dist = None
if pipenv_site is not None:
pipenv_dist, pipenv_path = find_site_path("pipenv", site_dir=pipenv_site)
else:
@@ -42,10 +51,16 @@ def _patch_path(pipenv_site=None):
if pipenv_dist is not None:
pipenv_dist.activate()
else:
site.addsitedir(next(iter(
sitedir for sitedir in (pipenv_site, pipenv_site_dir)
if sitedir is not None
), None))
site.addsitedir(
next(
iter(
sitedir
for sitedir in (pipenv_site, pipenv_site_dir)
if sitedir is not None
),
None,
)
)
if pipenv_path is not None:
pipenv_libdir = pipenv_path
for _dir in ("vendor", "patched", pipenv_libdir):
@@ -54,6 +69,7 @@ def _patch_path(pipenv_site=None):
def get_parser():
from argparse import ArgumentParser
parser = ArgumentParser("pipenv-resolver")
parser.add_argument("--pre", action="store_true", default=False)
parser.add_argument("--clear", action="store_true", default=False)
@@ -62,12 +78,24 @@ def get_parser():
parser.add_argument("--debug", action="store_true", default=False)
parser.add_argument("--system", action="store_true", default=False)
parser.add_argument("--parse-only", action="store_true", default=False)
parser.add_argument("--pipenv-site", metavar="pipenv_site_dir", action="store",
default=os.environ.get("PIPENV_SITE_DIR"))
parser.add_argument("--requirements-dir", metavar="requirements_dir", action="store",
default=os.environ.get("PIPENV_REQ_DIR"))
parser.add_argument("--write", metavar="write", action="store",
default=os.environ.get("PIPENV_RESOLVER_FILE"))
parser.add_argument(
"--pipenv-site",
metavar="pipenv_site_dir",
action="store",
default=os.environ.get("PIPENV_SITE_DIR"),
)
parser.add_argument(
"--requirements-dir",
metavar="requirements_dir",
action="store",
default=os.environ.get("PIPENV_REQ_DIR"),
)
parser.add_argument(
"--write",
metavar="write",
action="store",
default=os.environ.get("PIPENV_RESOLVER_FILE"),
)
parser.add_argument("packages", nargs="*")
return parser
@@ -103,6 +131,7 @@ class Entry:
def __init__(self, name, entry_dict, project, resolver, reverse_deps=None, dev=False):
super().__init__()
from pipenv.vendor.requirementslib.models.utils import tomlkit_value_to_python
self.name = name
if isinstance(entry_dict, dict):
self.entry_dict = self.clean_initial_dict(entry_dict)
@@ -137,6 +166,7 @@ class Entry:
@staticmethod
def make_requirement(name=None, entry=None, from_ireq=False):
from pipenv.vendor.requirementslib.models.requirements import Requirement
if from_ireq:
return Requirement.from_ireq(entry)
return Requirement.from_pipfile(name, entry)
@@ -152,12 +182,11 @@ class Entry:
@classmethod
def parse_pyparsing_exprs(cls, expr_iterable):
from pipenv.vendor.pyparsing import Literal, MatchFirst
keys = []
expr_list = []
expr = expr_iterable.copy()
if isinstance(expr, Literal) or (
expr.__class__.__name__ == Literal.__name__
):
if isinstance(expr, Literal) or (expr.__class__.__name__ == Literal.__name__):
keys.append(expr.match)
elif isinstance(expr, MatchFirst) or (
expr.__class__.__name__ == MatchFirst.__name__
@@ -174,13 +203,11 @@ class Entry:
def get_markers_from_dict(cls, entry_dict):
from pipenv.vendor.packaging import markers as packaging_markers
from pipenv.vendor.requirementslib.models.markers import normalize_marker_str
marker_keys = cls.parse_pyparsing_exprs(packaging_markers.VARIABLE)
markers = set()
keys_in_dict = [k for k in marker_keys if k in entry_dict]
markers = {
normalize_marker_str(f"{k} {entry_dict.pop(k)}")
for k in keys_in_dict
}
markers = {normalize_marker_str(f"{k} {entry_dict.pop(k)}") for k in keys_in_dict}
if "markers" in entry_dict:
markers.add(normalize_marker_str(entry_dict["markers"]))
if None in markers:
@@ -209,9 +236,7 @@ class Entry:
@property
def original_markers(self):
original_markers, lockfile_dict = self.get_markers_from_dict(
self.lockfile_dict
)
original_markers, lockfile_dict = self.get_markers_from_dict(self.lockfile_dict)
self.lockfile_dict = lockfile_dict
self._original_markers = self.marker_to_str(original_markers)
return self._original_markers
@@ -219,9 +244,11 @@ class Entry:
@staticmethod
def marker_to_str(marker):
from pipenv.vendor.requirementslib.models.markers import normalize_marker_str
if not marker:
return None
from pipenv.vendor.vistir.compat import Mapping
marker_str = None
if isinstance(marker, Mapping):
marker_dict, _ = Entry.get_markers_from_dict(marker)
@@ -274,7 +301,9 @@ class Entry:
@property
def pipfile_entry(self):
if self._pipfile_entry is None:
self._pipfile_entry = self.make_requirement(self.pipfile_name, self.pipfile_dict)
self._pipfile_entry = self.make_requirement(
self.pipfile_name, self.pipfile_dict
)
return self._pipfile_entry
@property
@@ -300,8 +329,9 @@ class Entry:
return self.project.pipfile_package_names["dev" if self.dev else "default"]
def create_parent(self, name, specifier="*"):
parent = self.create(name, specifier, self.project, self.resolver,
self.reverse_deps, self.dev)
parent = self.create(
name, specifier, self.project, self.resolver, self.reverse_deps, self.dev
)
parent._deptree = self.deptree
return parent
@@ -318,6 +348,7 @@ class Entry:
@staticmethod
def clean_specifier(specifier):
from pipenv.vendor.packaging.specifiers import Specifier
if not any(specifier.startswith(k) for k in Specifier._operators.keys()):
if specifier.strip().lower() in ["any", "<any>", "*"]:
return "*"
@@ -329,17 +360,19 @@ class Entry:
@staticmethod
def strip_version(specifier):
from pipenv.vendor.packaging.specifiers import Specifier
op = next(iter(
k for k in Specifier._operators.keys() if specifier.startswith(k)
), None)
op = next(
iter(k for k in Specifier._operators.keys() if specifier.startswith(k)), None
)
if op:
specifier = specifier[len(op):]
specifier = specifier[len(op) :]
while op:
op = next(iter(
k for k in Specifier._operators.keys() if specifier.startswith(k)
), None)
op = next(
iter(k for k in Specifier._operators.keys() if specifier.startswith(k)),
None,
)
if op:
specifier = specifier[len(op):]
specifier = specifier[len(op) :]
return specifier
@property
@@ -358,7 +391,8 @@ class Entry:
def parents_in_pipfile(self):
if not self._parents_in_pipfile:
self._parents_in_pipfile = [
p for p in self.flattened_parents
p
for p in self.flattened_parents
if p.normalized_name in self.pipfile_packages
]
return self._parents_in_pipfile
@@ -370,9 +404,9 @@ class Entry:
@property
def requirements(self):
if not self._requires:
self._requires = next(iter(
self.project.environment.get_package_requirements(self.name)
), {})
self._requires = next(
iter(self.project.environment.get_package_requirements(self.name)), {}
)
return self._requires
@property
@@ -403,14 +437,19 @@ class Entry:
def get_dependency(self, name):
if self.requirements:
return next(iter(
dep for dep in self.requirements.get("dependencies", [])
if dep and dep.get("package_name", "") == name
), {})
return next(
iter(
dep
for dep in self.requirements.get("dependencies", [])
if dep and dep.get("package_name", "") == name
),
{},
)
return {}
def get_parent_deps(self, unnest=False):
from pipenv.vendor.packaging.specifiers import Specifier
parents = []
for spec in self.reverse_deps.get(self.normalized_name, {}).get("parents", set()):
spec_match = next(iter(c for c in Specifier._operators if c in spec), None)
@@ -418,7 +457,9 @@ class Entry:
parent = None
if spec_match is not None:
spec_index = spec.index(spec_match)
specifier = self.clean_specifier(spec[spec_index:len(spec_match)]).strip()
specifier = self.clean_specifier(
spec[spec_index : len(spec_match)]
).strip()
name_start = spec_index + len(spec_match)
name = spec[name_start:].strip()
parent = self.create_parent(name, specifier)
@@ -460,13 +501,16 @@ class Entry:
self.entry_dict = self.lockfile_dict.copy()
elif can_use_updated:
if len(satisfied_by_versions) == 1:
self.entry_dict["version"] = next(iter(
sat_by for sat_by in satisfied_by_versions if sat_by
), None)
self.entry_dict["version"] = next(
iter(sat_by for sat_by in satisfied_by_versions if sat_by), None
)
hashes = None
if self.lockfile_entry.specifiers == satisfied_by:
ireq = self.lockfile_entry.as_ireq()
if not self.lockfile_entry.hashes and self.resolver._should_include_hash(ireq):
if (
not self.lockfile_entry.hashes
and self.resolver._should_include_hash(ireq)
):
hashes = self.resolver.get_hash(ireq)
else:
hashes = self.lockfile_entry.hashes
@@ -491,11 +535,12 @@ class Entry:
:rtype: Set
"""
constraints = {
c for c in self.resolver.parsed_constraints
if c and c.name == self.entry.name
c for c in self.resolver.parsed_constraints if c and c.name == self.entry.name
}
pipfile_constraint = self.get_pipfile_constraint()
if pipfile_constraint and not (self.pipfile_entry.editable or pipfile_constraint.editable):
if pipfile_constraint and not (
self.pipfile_entry.editable or pipfile_constraint.editable
):
constraints.add(pipfile_constraint)
return constraints
@@ -532,8 +577,10 @@ class Entry:
msg = (
"Cannot resolve conflicting version {}{} while {}{} is "
"locked.".format(
self.name, constraint.req.specifier,
self.name, self.updated_specifier
self.name,
constraint.req.specifier,
self.name,
self.updated_specifier,
)
)
raise DependencyConflict(msg)
@@ -545,12 +592,15 @@ class Entry:
continue
if not parent.validate_specifiers():
from pipenv.exceptions import DependencyConflict
msg = (
"Cannot resolve conflicting versions: (Root: {}) {}{} (Pipfile) "
"Incompatible with {}{} (resolved)\n".format(
self.name, parent.pipfile_name,
parent.pipfile_entry.requirement.specifiers, parent.name,
parent.updated_specifiers
self.name,
parent.pipfile_name,
parent.pipfile_entry.requirement.specifiers,
parent.name,
parent.updated_specifiers,
)
)
raise DependencyConflict(msg)
@@ -586,6 +636,7 @@ class Entry:
def clean_results(results, resolver, project, dev=False):
from pipenv.utils.dependencies import translate_markers
if not project.lockfile_exists:
return results
lockfile = project.lockfile_content
@@ -595,7 +646,9 @@ def clean_results(results, resolver, project, dev=False):
for result in results:
name = result.get("name")
entry_dict = result.copy()
entry = Entry(name, entry_dict, project, resolver, reverse_deps=reverse_deps, dev=dev)
entry = Entry(
name, entry_dict, project, resolver, reverse_deps=reverse_deps, dev=dev
)
entry_dict = translate_markers(entry.get_cleaned_dict(keep_outdated=False))
new_results.append(entry_dict)
return new_results
@@ -611,7 +664,9 @@ def clean_outdated(results, resolver, project, dev=False):
for result in results:
name = result.get("name")
entry_dict = result.copy()
entry = Entry(name, entry_dict, project, resolver, reverse_deps=reverse_deps, dev=dev)
entry = Entry(
name, entry_dict, project, resolver, reverse_deps=reverse_deps, dev=dev
)
# The old entry was editable but this one isnt; prefer the old one
# TODO: Should this be the case for all locking?
if entry.was_editable and not entry.is_editable:
@@ -622,9 +677,17 @@ def clean_outdated(results, resolver, project, dev=False):
if name in lockfile[alternate_section]:
lockfile_entry = lockfile[alternate_section][name]
if lockfile_entry and not entry.is_updated:
old_markers = next(iter(m for m in (
entry.lockfile_entry.markers, lockfile_entry.get("markers", None)
) if m is not None), None)
old_markers = next(
iter(
m
for m in (
entry.lockfile_entry.markers,
lockfile_entry.get("markers", None),
)
if m is not None
),
None,
)
new_markers = entry_dict.get("markers", None)
if old_markers:
old_markers = Entry.marker_to_str(old_markers)
@@ -644,14 +707,15 @@ def clean_outdated(results, resolver, project, dev=False):
def parse_packages(packages, pre, clear, system, requirements_dir=None):
from pipenv.utils.indexes import parse_indexes
from pipenv.vendor.requirementslib.models.requirements import Requirement
from pipenv.vendor.vistir.contextmanagers import cd, temp_path
from pipenv.utils.indexes import parse_indexes
parsed_packages = []
for package in packages:
*_, line = parse_indexes(package)
line = " ".join(line)
pf = dict()
pf = {}
req = Requirement.from_line(line)
if not req.name:
with temp_path(), cd(req.req.setup_info.base_dir):
@@ -676,6 +740,7 @@ def parse_packages(packages, pre, clear, system, requirements_dir=None):
def resolve_packages(pre, clear, verbose, system, write, requirements_dir, packages, dev):
from pipenv.utils.internet import create_mirror_source, replace_pypi_sources
from pipenv.utils.resolver import resolve_deps
pypi_mirror_source = (
create_mirror_source(os.environ["PIPENV_PYPI_MIRROR"])
if "PIPENV_PYPI_MIRROR" in os.environ
@@ -691,10 +756,11 @@ def resolve_packages(pre, clear, verbose, system, write, requirements_dir, packa
sources=sources,
clear=clear,
allow_global=system,
req_dir=requirements_dir
req_dir=requirements_dir,
)
from pipenv.project import Project
project = Project()
sources = (
replace_pypi_sources(project.pipfile_sources, pypi_mirror_source)
@@ -729,8 +795,20 @@ def resolve_packages(pre, clear, verbose, system, write, requirements_dir, packa
print(json.dumps([]))
def _main(pre, clear, verbose, system, write, requirements_dir, packages, parse_only=False, dev=False):
os.environ["PIPENV_REQUESTED_PYTHON_VERSION"] = ".".join([str(s) for s in sys.version_info[:3]])
def _main(
pre,
clear,
verbose,
system,
write,
requirements_dir,
packages,
parse_only=False,
dev=False,
):
os.environ["PIPENV_REQUESTED_PYTHON_VERSION"] = ".".join(
[str(s) for s in sys.version_info[:3]]
)
os.environ["PIP_PYTHON_PATH"] = str(sys.executable)
if parse_only:
parse_packages(
@@ -741,7 +819,9 @@ def _main(pre, clear, verbose, system, write, requirements_dir, packages, parse_
requirements_dir=requirements_dir,
)
else:
resolve_packages(pre, clear, verbose, system, write, requirements_dir, packages, dev)
resolve_packages(
pre, clear, verbose, system, write, requirements_dir, packages, dev
)
def main(argv=None):
@@ -749,8 +829,10 @@ def main(argv=None):
parsed, remaining = parser.parse_known_args(argv)
_patch_path(pipenv_site=parsed.pipenv_site)
import warnings
from pipenv.vendor.vistir.compat import ResourceWarning
from pipenv.vendor.vistir.misc import replace_with_text_stream
warnings.simplefilter("ignore", category=ResourceWarning)
replace_with_text_stream("stdout")
replace_with_text_stream("stderr")
@@ -758,9 +840,17 @@ def main(argv=None):
os.environ["PYTHONIOENCODING"] = "utf-8"
os.environ["PYTHONUNBUFFERED"] = "1"
parsed = handle_parsed_args(parsed)
_main(parsed.pre, parsed.clear, parsed.verbose, parsed.system, parsed.write,
parsed.requirements_dir, parsed.packages, parse_only=parsed.parse_only,
dev=parsed.dev)
_main(
parsed.pre,
parsed.clear,
parsed.verbose,
parsed.system,
parsed.write,
parsed.requirements_dir,
parsed.packages,
parse_only=parsed.parse_only,
dev=parsed.dev,
)
if __name__ == "__main__":
+14 -12
View File
@@ -10,7 +10,6 @@ from pipenv.vendor import shellingham
from pipenv.vendor.vistir.compat import Path, get_terminal_size
from pipenv.vendor.vistir.contextmanagers import temp_environ
ShellDetectionFailure = shellingham.ShellDetectionFailure
@@ -49,7 +48,7 @@ def _get_activate_script(cmd, venv):
command = "."
# Escape any special characters located within the virtualenv path to allow
# for proper activation.
venv_location = re.sub(r'([ &$()\[\]])', r"\\\1", str(venv))
venv_location = re.sub(r"([ &$()\[\]])", r"\\\1", str(venv))
# The leading space can make history cleaner in some shells.
return f" {command} {venv_location}/bin/activate{suffix}"
@@ -68,7 +67,7 @@ class Shell:
self.args = []
def __repr__(self):
return '{type}(cmd={cmd!r})'.format(
return "{type}(cmd={cmd!r})".format(
type=type(self).__name__,
cmd=self.cmd,
)
@@ -149,10 +148,9 @@ class Bash(Shell):
base_rc_src = f'source "{bashrc_path.as_posix()}"\n'
rcfile.write(base_rc_src)
export_path = 'export PATH="{}:$PATH"\n'.format(":".join(
self._format_path(python)
for python in _iter_python(venv)
))
export_path = 'export PATH="{}:$PATH"\n'.format(
":".join(self._format_path(python) for python in _iter_python(venv))
)
rcfile.write(export_path)
rcfile.flush()
self.args.extend(["--rcfile", rcfile.name])
@@ -165,7 +163,7 @@ class MsysBash(Bash):
if not python.drive:
return s
# Convert "C:/something" to "/c/something".
return f'/{s[0].lower()}{s[2:]}'
return f"/{s[0].lower()}{s[2:]}"
class CmderEmulatedShell(Shell):
@@ -207,16 +205,20 @@ SHELL_LOOKUP = collections.defaultdict(
lambda: collections.defaultdict(lambda: Shell),
{
"bash": collections.defaultdict(
lambda: Bash, {"msys": MsysBash},
lambda: Bash,
{"msys": MsysBash},
),
"cmd": collections.defaultdict(
lambda: Shell, {"cmder": CmderCommandPrompt},
lambda: Shell,
{"cmder": CmderCommandPrompt},
),
"powershell": collections.defaultdict(
lambda: Shell, {"cmder": CmderPowershell},
lambda: Shell,
{"cmder": CmderPowershell},
),
"pwsh": collections.defaultdict(
lambda: Shell, {"cmder": CmderPowershell},
lambda: Shell,
{"cmder": CmderPowershell},
),
},
)
+1
View File
@@ -1,2 +1,3 @@
import logging
logging.basicConfig(level=logging.ERROR)
+21 -19
View File
@@ -1,13 +1,13 @@
import os
import re
from contextlib import contextmanager
from pathlib import Path
from typing import Sequence, Mapping
from typing import Mapping, Sequence
from urllib.parse import urlparse
from packaging.markers import Marker
from pipenv import fs_str
from .constants import SCHEME_LIST, VCS_LIST
from .shell import temp_path
@@ -80,13 +80,7 @@ def pep423_name(name):
return name
def get_vcs_deps(
project=None,
dev=False,
pypi_mirror=None,
packages=None,
reqs=None
):
def get_vcs_deps(project=None, dev=False, pypi_mirror=None, packages=None, reqs=None):
from pipenv.vendor.requirementslib.models.requirements import Requirement
section = "vcs_dev_packages" if dev else "vcs_packages"
@@ -112,7 +106,7 @@ def get_vcs_deps(
try:
with temp_path(), locked_repository(requirement) as repo:
from pipenv.vendor.requirementslib.models.requirements import (
Requirement
Requirement,
)
# from distutils.sysconfig import get_python_lib
@@ -120,7 +114,7 @@ def get_vcs_deps(
commit_hash = repo.get_commit_hash()
name = requirement.normalized_name
lockfile[name] = requirement.pipfile_entry[1]
lockfile[name]['ref'] = commit_hash
lockfile[name]["ref"] = commit_hash
result.append(requirement)
except OSError:
continue
@@ -152,7 +146,7 @@ def translate_markers(pipfile_entry):
marker_str = new_pipfile.pop("markers")
if marker_str:
marker = str(Marker(marker_str))
if 'extra' not in marker:
if "extra" not in marker:
marker_set.add(marker)
for m in pipfile_markers:
entry = f"{pipfile_entry[m]}"
@@ -160,15 +154,19 @@ def translate_markers(pipfile_entry):
marker_set.add(str(Marker(f"{m} {entry}")))
new_pipfile.pop(m)
if marker_set:
new_pipfile["markers"] = str(Marker(" or ".join(
f"{s}" if " and " in s else s
for s in sorted(dedup(marker_set))
))).replace('"', "'")
new_pipfile["markers"] = str(
Marker(
" or ".join(
f"{s}" if " and " in s else s for s in sorted(dedup(marker_set))
)
)
).replace('"', "'")
return new_pipfile
def clean_resolved_dep(dep, is_top_level=False, pipfile_entry=None):
from pipenv.vendor.requirementslib.utils import is_vcs
name = pep423_name(dep["name"])
lockfile = {}
# We use this to determine if there are any markers on top level packages
@@ -195,7 +193,9 @@ def clean_resolved_dep(dep, is_top_level=False, pipfile_entry=None):
fs_key = next(iter(k for k in ["path", "file"] if k in dep), None)
pipfile_fs_key = None
if pipfile_entry:
pipfile_fs_key = next(iter(k for k in ["path", "file"] if k in pipfile_entry), None)
pipfile_fs_key = next(
iter(k for k in ["path", "file"] if k in pipfile_entry), None
)
if fs_key and pipfile_fs_key and fs_key != pipfile_fs_key:
lockfile[pipfile_fs_key] = pipfile_entry[pipfile_fs_key]
elif fs_key is not None:
@@ -248,7 +248,7 @@ def is_pinned_requirement(ireq):
def convert_deps_to_pip(deps, project=None, r=True, include_index=True):
""""Converts a Pipfile-formatted dependency to a pip-formatted one."""
""" "Converts a Pipfile-formatted dependency to a pip-formatted one."""
from pipenv.vendor.requirementslib.models.requirements import Requirement
dependencies = []
@@ -266,6 +266,7 @@ def convert_deps_to_pip(deps, project=None, r=True, include_index=True):
# Write requirements.txt to tmp directory.
from pipenv.vendor.vistir.path import create_tracked_tempfile
f = create_tracked_tempfile(suffix="-requirements.txt", delete=False)
f.write("\n".join(dependencies).encode("utf-8"))
f.close()
@@ -333,6 +334,7 @@ def is_installable_file(path):
@contextmanager
def locked_repository(requirement):
from pipenv.vendor.vistir.path import create_tracked_tempdir
if not requirement.is_vcs:
return
original_base = os.environ.pop("PIP_SHIMS_BASE_MODULE", None)
+20 -22
View File
@@ -1,18 +1,18 @@
import re
from urllib3.util import parse_url
from pipenv import environments
if environments.MYPY_RUNNING:
from typing import List, Optional, Text, Tuple, Union
from pipenv.project import Project, TSource
from pipenv.vendor.requirementslib.models.requirements import Requirement
from urllib3 import util as urllib3_util
from requirementslib import Requirement
from pipenv.vendor.vistir.compat import Mapping
from pipenv.exceptions import PipenvUsageError
from pipenv.vendor.vistir.compat import Mapping
from .internet import create_mirror_source, is_pypi_url
if environments.MYPY_RUNNING:
from typing import List, Optional, Union # noqa
from pipenv.project import Project, TSource # noqa
def prepare_pip_source_args(sources, pip_args=None):
if pip_args is None:
@@ -25,11 +25,9 @@ def prepare_pip_source_args(sources, pip_args=None):
pip_args.extend(["-i", package_url])
# Trust the host if it's not verified.
if not sources[0].get("verify_ssl", True):
url_parts = urllib3_util.parse_url(package_url)
url_parts = parse_url(package_url)
url_port = f":{url_parts.port}" if url_parts.port else ""
pip_args.extend(
["--trusted-host", f"{url_parts.host}{url_port}"]
)
pip_args.extend(["--trusted-host", f"{url_parts.host}{url_port}"])
# Add additional sources as extra indexes.
if len(sources) > 1:
for source in sources[1:]:
@@ -39,17 +37,16 @@ def prepare_pip_source_args(sources, pip_args=None):
pip_args.extend(["--extra-index-url", url])
# Trust the host if it's not verified.
if not source.get("verify_ssl", True):
url_parts = urllib3_util.parse_url(url)
url_parts = parse_url(url)
url_port = f":{url_parts.port}" if url_parts.port else ""
pip_args.extend(
["--trusted-host", f"{url_parts.host}{url_port}"]
)
pip_args.extend(["--trusted-host", f"{url_parts.host}{url_port}"])
return pip_args
def get_project_index(project, index=None, trusted_hosts=None):
# type: (Optional[Union[str, TSource]], Optional[List[str]], Optional[Project]) -> TSource
from pipenv.project import SourceNotFound
if trusted_hosts is None:
trusted_hosts = []
if isinstance(index, Mapping):
@@ -57,7 +54,7 @@ def get_project_index(project, index=None, trusted_hosts=None):
try:
source = project.find_source(index)
except SourceNotFound:
index_url = urllib3_util.parse_url(index)
index_url = parse_url(index)
src_name = project.src_name_from_url(index)
verify_ssl = index_url.host not in trusted_hosts
source = {"url": index, "verify_ssl": verify_ssl, "name": src_name}
@@ -109,8 +106,9 @@ def parse_indexes(line, strict=False):
index = args.index
extra_index = args.extra_index
trusted_host = args.trusted_host
if strict and sum(
bool(arg) for arg in (index, extra_index, trusted_host, remainder)
) > 1:
if (
strict
and sum(bool(arg) for arg in (index, extra_index, trusted_host, remainder)) > 1
):
raise ValueError("Index arguments must be on their own lines.")
return index, extra_index, trusted_host, remainder
+3 -4
View File
@@ -1,5 +1,6 @@
import re
from urllib.parse import urlparse
from urllib3 import util as urllib3_util
from pipenv.vendor import parse
@@ -75,7 +76,7 @@ def get_host_and_port(url):
:return: a string with the host:port pair if the URL includes port number explicitly; otherwise, returns host only
"""
url = urllib3_util.parse_url(url)
return '{}:{}'.format(url.host, url.port) if url.port else url.host
return "{}:{}".format(url.host, url.port) if url.port else url.host
def get_url_name(url):
@@ -120,9 +121,7 @@ def proper_case(package_name):
f"https://pypi.org/pypi/{package_name}/json", timeout=0.3, stream=True
)
if not r.ok:
raise OSError(
f"Unable to find package {package_name} in PyPI repository."
)
raise OSError(f"Unable to find package {package_name} in PyPI repository.")
r = parse.parse("https://pypi.org/pypi/{name}/json", r.url)
good_name = r["name"]
+11 -10
View File
@@ -1,6 +1,6 @@
from typing import Mapping
from .dependencies import pep423_name, translate_markers, clean_resolved_dep
from .dependencies import clean_resolved_dep, pep423_name, translate_markers
def format_requirement_for_lockfile(req, markers_lookup, index_lookup, hashes=None):
@@ -44,16 +44,12 @@ def get_locked_dep(dep, pipfile_section, prefer_pipfile=True):
# it now for development purposes
# TODO: Is this implementation clear? How can it be improved?
entry = None
cleaner_kwargs = {
"is_top_level": False,
"pipfile_entry": None
}
cleaner_kwargs = {"is_top_level": False, "pipfile_entry": None}
if isinstance(dep, Mapping) and dep.get("name", ""):
dep_name = pep423_name(dep["name"])
name = next(iter(
k for k in pipfile_section.keys()
if pep423_name(k) == dep_name
), None)
name = next(
iter(k for k in pipfile_section.keys() if pep423_name(k) == dep_name), None
)
entry = pipfile_section[name] if name else None
if entry:
@@ -66,7 +62,12 @@ def get_locked_dep(dep, pipfile_section, prefer_pipfile=True):
lockfile_name, lockfile_dict = lockfile_entry.copy().popitem()
lockfile_version = lockfile_dict.get("version", "")
# Keep pins from the lockfile
if prefer_pipfile and lockfile_version != version and version.startswith("==") and "*" not in version:
if (
prefer_pipfile
and lockfile_version != version
and version.startswith("==")
and "*" not in version
):
lockfile_dict["version"] = version
lockfile_entry[lockfile_name] = lockfile_dict
return lockfile_entry
+19 -15
View File
@@ -4,11 +4,11 @@ import subprocess
import crayons
from click import echo as click_echo
from pipenv.exceptions import PipenvCmdError
from pipenv import environments
from pipenv.exceptions import PipenvCmdError
if environments.MYPY_RUNNING:
from typing import Tuple
from typing import Tuple # noqa
def run_command(cmd, *args, is_verbose=False, **kwargs):
@@ -25,6 +25,7 @@ def run_command(cmd, *args, is_verbose=False, **kwargs):
from pipenv._compat import decode_for_output
from pipenv.cmdparse import Script
catch_exceptions = kwargs.pop("catch_exceptions", True)
if isinstance(cmd, ((str,), list, tuple)):
cmd = Script.parse(cmd)
@@ -38,17 +39,24 @@ def run_command(cmd, *args, is_verbose=False, **kwargs):
click_echo(f"Running command: $ {cmd.cmdify()}")
c = subprocess_run(command, *args, **kwargs)
if is_verbose:
click_echo("Command output: {}".format(
crayons.cyan(decode_for_output(c.stdout))
), err=True)
click_echo(
"Command output: {}".format(crayons.cyan(decode_for_output(c.stdout))),
err=True,
)
if c.returncode and catch_exceptions:
raise PipenvCmdError(cmd.cmdify(), c.stdout, c.stderr, c.returncode)
return c
def subprocess_run(
args, *, block=True, text=True, capture_output=True,
encoding="utf-8", env=None, **other_kwargs
args,
*,
block=True,
text=True,
capture_output=True,
encoding="utf-8",
env=None,
**other_kwargs,
):
"""A backward compatible version of subprocess.run().
@@ -61,17 +69,13 @@ def subprocess_run(
_env.update(env)
other_kwargs["env"] = _env
if capture_output:
other_kwargs['stdout'] = subprocess.PIPE
other_kwargs['stderr'] = subprocess.PIPE
other_kwargs["stdout"] = subprocess.PIPE
other_kwargs["stderr"] = subprocess.PIPE
if block:
return subprocess.run(
args, universal_newlines=text,
encoding=encoding, **other_kwargs
args, universal_newlines=text, encoding=encoding, **other_kwargs
)
else:
return subprocess.Popen(
args, universal_newlines=text,
encoding=encoding, **other_kwargs
args, universal_newlines=text, encoding=encoding, **other_kwargs
)
+204 -97
View File
@@ -7,27 +7,36 @@ import warnings
from functools import lru_cache
import crayons
from pipenv import environments
from click import echo as click_echo
from pipenv.exceptions import ResolutionFailure, RequirementError
from pipenv.vendor.requirementslib import Requirement, Pipfile
from pipenv.vendor.vistir import open_file, TemporaryDirectory
from .dependencies import get_vcs_deps, pep423_name, translate_markers, clean_pkg_version, \
is_pinned_requirement, convert_deps_to_pip, HackedPythonVersion
from pipenv import environments
from pipenv.exceptions import RequirementError, ResolutionFailure
from pipenv.vendor.requirementslib import Pipfile, Requirement
from pipenv.vendor.requirementslib.models.utils import DIRECT_URL_RE
from pipenv.vendor.vistir import TemporaryDirectory, open_file
from pipenv.vendor.vistir.path import create_tracked_tempdir
from .dependencies import (
HackedPythonVersion,
clean_pkg_version,
convert_deps_to_pip,
get_vcs_deps,
is_pinned_requirement,
pep423_name,
translate_markers,
)
from .indexes import parse_indexes, prepare_pip_source_args
from .internet import _get_requests_session
from .locking import format_requirement_for_lockfile, prepare_lockfile
from .shell import temp_environ, subprocess_run, make_posix
from .shell import make_posix, subprocess_run, temp_environ
from .spinner import create_spinner
if environments.MYPY_RUNNING:
from typing import Any, Dict, List, Optional, Tuple, Union
from pipenv.project import Project
from pipenv.vendor.requirementslib.models.pipfile import Pipfile
from pipenv.vendor.requirementslib.models.requirements import (
Line, Requirement
)
if environments.MYPY_RUNNING:
from typing import Any, Dict, List, Optional, Set, Tuple, Union # noqa
from pipenv.project import Project # noqa
from pipenv.vendor.requirementslib import Pipfile, Requirement # noqa
from pipenv.vendor.requirementslib.models.requirements import Line # noqa
class HashCacheMixin:
@@ -38,6 +47,7 @@ class HashCacheMixin:
cache key includes the hash value returned from the server). This ought to
avoid issues where the location on the server changes.
"""
def __init__(self, directory, session):
self.session = session
if not os.path.isdir(directory):
@@ -65,8 +75,16 @@ class HashCacheMixin:
class Resolver:
def __init__(
self, constraints, req_dir, project, sources, index_lookup=None,
markers_lookup=None, skipped=None, clear=False, pre=False
self,
constraints,
req_dir,
project,
sources,
index_lookup=None,
markers_lookup=None,
skipped=None,
clear=False,
pre=False,
):
self.initial_constraints = constraints
self.req_dir = req_dir
@@ -113,9 +131,9 @@ class Resolver:
from pipenv.vendor.pip_shims import shims
if not self._hash_cache:
self._hash_cache = type("HashCache", (HashCacheMixin, shims.SafeFileCache), {})(
os.path.join(self.project.s.PIPENV_CACHE_DIR, "hashes"), self.session
)
self._hash_cache = type(
"HashCache", (HashCacheMixin, shims.SafeFileCache), {}
)(os.path.join(self.project.s.PIPENV_CACHE_DIR, "hashes"), self.session)
return self._hash_cache
@classmethod
@@ -132,23 +150,31 @@ class Resolver:
):
# type: (...) -> Tuple[Set[str], Dict[str, Dict[str, Union[str, bool, List[str]]]], Dict[str, str], Dict[str, str]]
constraints = set() # type: Set[str]
skipped = dict() # type: Dict[str, Dict[str, Union[str, bool, List[str]]]]
skipped = {} # type: Dict[str, Dict[str, Union[str, bool, List[str]]]]
if index_lookup is None:
index_lookup = {}
if markers_lookup is None:
markers_lookup = {}
if not req_dir:
from pipenv.vendor.vistir.path import create_tracked_tempdir
req_dir = create_tracked_tempdir(prefix="pipenv-", suffix="-reqdir")
transient_resolver = cls(
[], req_dir, project, sources, index_lookup=index_lookup,
markers_lookup=markers_lookup, clear=clear, pre=pre
[],
req_dir,
project,
sources,
index_lookup=index_lookup,
markers_lookup=markers_lookup,
clear=clear,
pre=pre,
)
for dep in deps:
if not dep:
continue
req, req_idx, markers_idx = cls.parse_line(
dep, index_lookup=index_lookup, markers_lookup=markers_lookup, project=project
dep,
index_lookup=index_lookup,
markers_lookup=markers_lookup,
project=project,
)
index_lookup.update(req_idx)
markers_lookup.update(markers_idx)
@@ -158,12 +184,20 @@ class Resolver:
# to the lockfile directly
use_sources = None
if req.name in index_lookup:
use_sources = list(filter(lambda s: s.get('name') == index_lookup[req.name], sources))
use_sources = list(
filter(lambda s: s.get("name") == index_lookup[req.name], sources)
)
if not use_sources:
use_sources = sources
transient_resolver = cls(
[], req_dir, project, use_sources, index_lookup=index_lookup,
markers_lookup=markers_lookup, clear=clear, pre=pre
[],
req_dir,
project,
use_sources,
index_lookup=index_lookup,
markers_lookup=markers_lookup,
clear=clear,
pre=pre,
)
constraint_update, lockfile_update = cls.get_deps_from_req(
req, resolver=transient_resolver, resolve_vcs=project.s.PIPENV_RESOLVE_VCS
@@ -178,17 +212,17 @@ class Resolver:
line, # type: str
index_lookup=None, # type: Dict[str, str]
markers_lookup=None, # type: Dict[str, str]
project=None # type: Optional[Project]
project=None, # type: Optional[Project]
):
# type: (...) -> Tuple[Requirement, Dict[str, str], Dict[str, str]]
from pipenv.vendor.requirementslib.models.requirements import Requirement
from pipenv.vendor.requirementslib.models.utils import DIRECT_URL_RE
if index_lookup is None:
index_lookup = {}
if markers_lookup is None:
markers_lookup = {}
if project is None:
from pipenv.project import Project
project = Project()
index, extra_index, trust_host, remainder = parse_indexes(line)
line = " ".join(remainder)
@@ -202,13 +236,18 @@ class Resolver:
try:
req = Requirement.from_line(line)
except ValueError:
raise ResolutionFailure(f"Failed to resolve requirement from line: {line!s}")
raise ResolutionFailure(
f"Failed to resolve requirement from line: {line!s}"
)
else:
raise ResolutionFailure(f"Failed to resolve requirement from line: {line!s}")
raise ResolutionFailure(
f"Failed to resolve requirement from line: {line!s}"
)
if index:
try:
index_lookup[req.normalized_name] = project.get_source(
url=index, refresh=True).get("name")
url=index, refresh=True
).get("name")
except TypeError:
pass
try:
@@ -227,13 +266,13 @@ class Resolver:
# type: (Requirement, Optional["Resolver"], bool) -> Tuple[Set[str], Dict[str, Dict[str, Union[str, bool, List[str]]]]]
from pipenv.vendor.requirementslib.models.requirements import Requirement
from pipenv.vendor.requirementslib.models.utils import (
_requirement_to_str_lowercase_name
_requirement_to_str_lowercase_name,
)
from pipenv.vendor.requirementslib.utils import is_installable_dir
# TODO: this is way too complex, refactor this
constraints = set() # type: Set[str]
locked_deps = dict() # type: Dict[str, Dict[str, Union[str, bool, List[str]]]]
locked_deps = {} # type: Dict[str, Dict[str, Union[str, bool, List[str]]]]
if (req.is_file_or_url or req.is_vcs) and not req.is_wheel:
# for local packages with setup.py files and potential direct url deps:
if req.is_vcs:
@@ -243,7 +282,6 @@ class Resolver:
else:
_, entry = req.pipfile_entry
parsed_line = req.req.parsed_line # type: Line
setup_info = None # type: Any
try:
name = req.normalized_name
except TypeError:
@@ -255,8 +293,11 @@ class Resolver:
# Allow users to toggle resolution off for non-editable VCS packages
# but leave it on for local, installable folders on the filesystem
if resolve_vcs or (
req.editable or parsed_line.is_wheel or (
req.is_file_or_url and parsed_line.is_local
req.editable
or parsed_line.is_wheel
or (
req.is_file_or_url
and parsed_line.is_local
and is_installable_dir(parsed_line.path)
)
):
@@ -271,9 +312,7 @@ class Resolver:
if r.marker and not r.marker.evaluate():
new_constraints = {}
_, new_entry = req.pipfile_entry
new_lock = {
pep423_name(new_req.normalized_name): new_entry
}
new_lock = {pep423_name(new_req.normalized_name): new_entry}
else:
new_constraints, new_lock = cls.get_deps_from_req(
new_req, resolver
@@ -298,14 +337,22 @@ class Resolver:
else:
# if the dependency isn't installable, don't add it to constraints
# and instead add it directly to the lock
if req and req.requirement and (
req.requirement.marker and not req.requirement.marker.evaluate()
if (
req
and req.requirement
and (req.requirement.marker and not req.requirement.marker.evaluate())
):
pypi = resolver.finder if resolver else None
ireq = req.ireq
best_match = pypi.find_best_candidate(ireq.name, ireq.specifier).best_candidate if pypi else None
best_match = (
pypi.find_best_candidate(ireq.name, ireq.specifier).best_candidate
if pypi
else None
)
if best_match:
ireq.req.specifier = ireq.specifier.__class__(f"=={best_match.version}")
ireq.req.specifier = ireq.specifier.__class__(
f"=={best_match.version}"
)
hashes = resolver.collect_hashes(ireq) if resolver else []
new_req = Requirement.from_ireq(ireq)
new_req = new_req.add_hashes(hashes)
@@ -314,13 +361,13 @@ class Resolver:
click_echo(
"{} doesn't match your environment, "
"its dependencies won't be resolved.".format(req.as_line()),
err=True
err=True,
)
else:
click_echo(
"Could not find a version of {} that matches your environment, "
"it will be skipped.".format(req.as_line()),
err=True
err=True,
)
return constraints, locked_deps
constraints.add(req.constraint_line)
@@ -337,10 +384,10 @@ class Resolver:
sources=None, # type: List[str]
req_dir=None, # type: str
clear=False, # type: bool
pre=False # type: bool
pre=False, # type: bool
):
# type: (...) -> "Resolver"
from pipenv.vendor.vistir.path import create_tracked_tempdir
if not req_dir:
req_dir = create_tracked_tempdir(suffix="-requirements", prefix="pipenv-")
if index_lookup is None:
@@ -350,18 +397,31 @@ class Resolver:
if sources is None:
sources = project.sources
constraints, skipped, index_lookup, markers_lookup = cls.get_metadata(
deps, index_lookup, markers_lookup, project, sources, req_dir=req_dir,
pre=pre, clear=clear
deps,
index_lookup,
markers_lookup,
project,
sources,
req_dir=req_dir,
pre=pre,
clear=clear,
)
return Resolver(
constraints, req_dir, project, sources, index_lookup=index_lookup,
markers_lookup=markers_lookup, skipped=skipped, clear=clear, pre=pre
constraints,
req_dir,
project,
sources,
index_lookup=index_lookup,
markers_lookup=markers_lookup,
skipped=skipped,
clear=clear,
pre=pre,
)
@classmethod
def from_pipfile(cls, project, pipfile=None, dev=False, pre=False, clear=False):
# type: (Optional[Project], Optional[Pipfile], bool, bool, bool) -> "Resolver"
from pipenv.vendor.vistir.path import create_tracked_tempdir
if not pipfile:
pipfile = project._pipfile
req_dir = create_tracked_tempdir(suffix="-requirements", prefix="pipenv-")
@@ -371,12 +431,25 @@ class Resolver:
deps.update({req.as_line() for req in pipfile.dev_packages})
deps.update({req.as_line() for req in pipfile.packages})
constraints, skipped, index_lookup, markers_lookup = cls.get_metadata(
list(deps), index_lookup, markers_lookup, project, project.sources,
req_dir=req_dir, pre=pre, clear=clear
list(deps),
index_lookup,
markers_lookup,
project,
project.sources,
req_dir=req_dir,
pre=pre,
clear=clear,
)
return Resolver(
constraints, req_dir, project, project.sources, index_lookup=index_lookup,
markers_lookup=markers_lookup, skipped=skipped, clear=clear, pre=pre
constraints,
req_dir,
project,
project.sources,
index_lookup=index_lookup,
markers_lookup=markers_lookup,
skipped=skipped,
clear=clear,
pre=pre,
)
@property
@@ -410,6 +483,7 @@ class Resolver:
def prepare_constraint_file(self):
from pipenv.vendor.vistir.path import create_tracked_tempfile
constraints_file = create_tracked_tempfile(
mode="w",
prefix="pipenv-",
@@ -419,7 +493,8 @@ class Resolver:
)
skip_args = ("build-isolation", "use-pep517", "cache-dir")
args_to_add = [
arg for arg in self.pip_args
arg
for arg in self.pip_args
if not any(bad_arg in arg for bad_arg in skip_args)
]
if self.sources:
@@ -446,7 +521,9 @@ class Resolver:
pip_options.no_input = True
pip_options.progress_bar = "off"
pip_options.ignore_requires_python = True
pip_options.pre = self.pre or self.project.settings.get("allow_prereleases", False)
pip_options.pre = self.pre or self.project.settings.get(
"allow_prereleases", False
)
self._pip_options = pip_options
return self._pip_options
@@ -459,16 +536,17 @@ class Resolver:
@property
def finder(self):
from pipenv.vendor.pip_shims import shims
if self._finder is None:
self._finder = shims.get_package_finder(
install_cmd=self.pip_command,
options=self.pip_options,
session=self.session
session=self.session,
)
index_mapping = {}
for source in self.sources:
if source.get('name'):
index_mapping[source['name']] = source['url']
if source.get("name"):
index_mapping[source["name"]] = source["url"]
alt_index_lookup = {}
for req_name, index in self.index_lookup.items():
if index_mapping.get(index):
@@ -480,6 +558,7 @@ class Resolver:
@property
def ignore_compatibility_finder(self):
from pipenv.vendor.pip_shims import shims
if self._ignore_compatibility_finder is None:
ignore_compatibility_finder = shims.get_package_finder(
install_cmd=self.pip_command,
@@ -501,20 +580,26 @@ class Resolver:
pip_options.extra_index_urls = []
if self._parsed_constraints is None:
self._parsed_constraints = shims.parse_requirements(
self.constraint_file, finder=self.finder, session=self.session,
options=pip_options
self.constraint_file,
finder=self.finder,
session=self.session,
options=pip_options,
)
return self._parsed_constraints
@property
def constraints(self):
from pipenv.patched.notpip._internal.req.constructors import install_req_from_parsed_requirement
from pipenv.patched.notpip._internal.req.constructors import (
install_req_from_parsed_requirement,
)
if self._constraints is None:
self._constraints = [
install_req_from_parsed_requirement(
c, isolated=self.pip_options.build_isolation,
use_pep517=self.pip_options.use_pep517, user_supplied=True
c,
isolated=self.pip_options.build_isolation,
use_pep517=self.pip_options.use_pep517,
user_supplied=True,
)
for c in self.parsed_constraints
]
@@ -523,10 +608,14 @@ class Resolver:
@contextlib.contextmanager
def get_resolver(self, clear=False):
from pipenv.vendor.pip_shims.shims import (
WheelCache, get_requirement_tracker, global_tempdir_manager
WheelCache,
get_requirement_tracker,
global_tempdir_manager,
)
with global_tempdir_manager(), get_requirement_tracker() as req_tracker, TemporaryDirectory(suffix="-build", prefix="pipenv-") as directory:
with global_tempdir_manager(), get_requirement_tracker() as req_tracker, TemporaryDirectory(
suffix="-build", prefix="pipenv-"
) as directory:
pip_options = self.pip_options
finder = self.finder
wheel_cache = WheelCache(pip_options.cache_dir, pip_options.format_control)
@@ -554,8 +643,8 @@ class Resolver:
yield resolver
def resolve(self):
from pipenv.vendor.pip_shims.shims import InstallationError
from pipenv.exceptions import ResolutionFailure
from pipenv.vendor.pip_shims.shims import InstallationError
self.constraints # For some reason it is important to evaluate constraints before resolver context
with temp_environ(), self.get_resolver() as resolver:
@@ -570,12 +659,15 @@ class Resolver:
def resolve_constraints(self):
from pipenv.vendor.requirementslib.models.markers import marker_from_specifier
new_tree = set()
for result in self.resolved_tree:
if result.markers:
self.markers[result.name] = result.markers
else:
candidate = self.finder.find_best_candidate(result.name, result.specifier).best_candidate
candidate = self.finder.find_best_candidate(
result.name, result.specifier
).best_candidate
if candidate:
requires_python = candidate.link.requires_python
if requires_python:
@@ -625,7 +717,8 @@ class Resolver:
click_echo(
"{}: Error generating hash for {}".format(
crayons.red("Warning", bold=True), ireq.name
), err=True
),
err=True,
)
return None
@@ -688,13 +781,12 @@ class Resolver:
return req.name, entry
def clean_results(self):
from pipenv.vendor.requirementslib.models.requirements import (
Requirement
)
from pipenv.vendor.requirementslib.models.requirements import Requirement
reqs = [(Requirement.from_ireq(ireq), ireq) for ireq in self.resolved_tree]
results = {}
for req, ireq in reqs:
if (req.vcs and req.editable and not req.is_direct_url):
if req.vcs and req.editable and not req.is_direct_url:
continue
elif req.normalized_name in self.skipped.keys():
continue
@@ -723,8 +815,14 @@ class Resolver:
def _show_warning(message, category, filename, lineno, line):
warnings.showwarning(message=message, category=category, filename=filename,
lineno=lineno, file=sys.stderr, line=line)
warnings.showwarning(
message=message,
category=category,
filename=filename,
lineno=lineno,
file=sys.stderr,
line=line,
)
sys.stderr.flush()
@@ -738,8 +836,6 @@ def actually_resolve_deps(
pre,
req_dir=None,
):
from pipenv.vendor.vistir.path import create_tracked_tempdir
if not req_dir:
req_dir = create_tracked_tempdir(suffix="-requirements", prefix="pipenv-")
warning_list = []
@@ -753,8 +849,13 @@ def actually_resolve_deps(
resolver.resolve_constraints()
results = resolver.clean_results()
for warning in warning_list:
_show_warning(warning.message, warning.category, warning.filename, warning.lineno,
warning.line)
_show_warning(
warning.message,
warning.category,
warning.filename,
warning.lineno,
warning.line,
)
return (results, hashes, resolver.markers_lookup, resolver, resolver.skipped)
@@ -762,6 +863,7 @@ def resolve(cmd, sp, project):
from pipenv._compat import decode_output
from pipenv.cmdparse import Script
from pipenv.vendor.vistir.misc import echo
c = subprocess_run(Script.parse(cmd).cmd_args, block=False, env=os.environ.copy())
is_verbose = project.s.is_verbose()
err = ""
@@ -777,9 +879,7 @@ def resolve(cmd, sp, project):
returncode = c.poll()
out = c.stdout.read()
if returncode != 0:
sp.red.fail(environments.PIPENV_SPINNER_FAIL_TEXT.format(
"Locking Failed!"
))
sp.red.fail(environments.PIPENV_SPINNER_FAIL_TEXT.format("Locking Failed!"))
echo(out.strip(), err=True)
if not is_verbose:
echo(err, err=True)
@@ -800,7 +900,7 @@ def venv_resolve_deps(
dev=False,
pipfile=None,
lockfile=None,
keep_outdated=False
keep_outdated=False,
):
"""
Resolve dependencies for a pipenv project, acts as a portal to the target environment.
@@ -833,7 +933,6 @@ def venv_resolve_deps(
from pipenv._compat import decode_for_output
from pipenv.vendor.vistir.compat import JSONDecodeError, NamedTemporaryFile, Path
from pipenv.vendor.vistir.misc import fs_str
from pipenv.vendor.vistir.path import create_tracked_tempdir
results = []
pipfile_section = "dev-packages" if dev else "packages"
@@ -852,7 +951,7 @@ def venv_resolve_deps(
req_dir = create_tracked_tempdir(prefix="pipenv", suffix="requirements")
cmd = [
which("python", allow_global=allow_global),
Path(resolver.__file__.rstrip("co")).as_posix()
Path(resolver.__file__.rstrip("co")).as_posix(),
]
if pre:
cmd.append("--pre")
@@ -879,15 +978,15 @@ def venv_resolve_deps(
os.environ.pop("PIPENV_SITE_DIR", None)
if keep_outdated:
os.environ["PIPENV_KEEP_OUTDATED"] = fs_str("1")
with create_spinner(text=decode_for_output("Locking..."), setting=project.s) as sp:
with create_spinner(
text=decode_for_output("Locking..."), setting=project.s
) as sp:
# This conversion is somewhat slow on local and file-type requirements since
# we now download those requirements / make temporary folders to perform
# dependency resolution on them, so we are including this step inside the
# spinner context manager for the UX improvement
sp.write(decode_for_output("Building requirements..."))
deps = convert_deps_to_pip(
deps, project, r=False, include_index=True
)
deps = convert_deps_to_pip(deps, project, r=False, include_index=True)
constraints = set(deps)
os.environ["PIPENV_PACKAGES"] = str("\n".join(constraints))
sp.write(decode_for_output("Resolving dependencies..."))
@@ -898,7 +997,9 @@ def venv_resolve_deps(
if not project.s.is_verbose() and c.stderr.strip():
click_echo(crayons.yellow(f"Warning: {c.stderr.strip()}"), err=True)
else:
sp.red.fail(environments.PIPENV_SPINNER_FAIL_TEXT.format("Locking Failed!"))
sp.red.fail(
environments.PIPENV_SPINNER_FAIL_TEXT.format("Locking Failed!")
)
click_echo(f"Output: {c.stdout.strip()}", err=True)
click_echo(f"Error: {c.stderr.strip()}", err=True)
try:
@@ -926,7 +1027,7 @@ def resolve_deps(
clear=False,
pre=False,
allow_global=False,
req_dir=None
req_dir=None,
):
"""Given a list of dependencies, return a resolved list of dependencies,
using pip-tools -- and their hashes, using the warehouse API / pip.
@@ -944,7 +1045,6 @@ def resolve_deps(
# First (proper) attempt:
req_dir = req_dir if req_dir else os.environ.get("req_dir", None)
if not req_dir:
from pipenv.vendor.vistir.path import create_tracked_tempdir
req_dir = create_tracked_tempdir(prefix="pipenv-", suffix="-requirements")
with HackedPythonVersion(python_version=python, python_path=python_path):
try:
@@ -970,7 +1070,13 @@ def resolve_deps(
try:
# Attempt to resolve again, with different Python version information,
# particularly for particularly particular packages.
results, hashes, markers_lookup, resolver, skipped = actually_resolve_deps(
(
results,
hashes,
markers_lookup,
resolver,
skipped,
) = actually_resolve_deps(
deps,
index_lookup,
markers_lookup,
@@ -989,6 +1095,7 @@ def resolve_deps(
def get_pipenv_sitedir():
# type: () -> Optional[str]
import pkg_resources
site_dir = next(
iter(d for d in pkg_resources.working_set if d.key.lower() == "pipenv"), None
)
+32 -24
View File
@@ -1,5 +1,6 @@
import errno
import os
import posixpath
import re
import shlex
import shutil
@@ -8,17 +9,16 @@ import sys
import warnings
from contextlib import contextmanager
from functools import lru_cache
import posixpath
from pathlib import Path
from .constants import SCHEME_LIST
from .processes import subprocess_run
from pipenv import environments
from pipenv.vendor.vistir.compat import ResourceWarning
from .constants import SCHEME_LIST
from .processes import subprocess_run
if environments.MYPY_RUNNING:
from typing import Text
from typing import Text # noqa
@lru_cache()
@@ -51,6 +51,7 @@ def make_posix(path):
def get_pipenv_dist(pkg="pipenv", pipenv_site=None):
from pipenv.resolver import find_site_path
pipenv_libdir = os.path.dirname(os.path.abspath(__file__))
if pipenv_site is None:
pipenv_site = os.path.dirname(pipenv_libdir)
@@ -80,8 +81,8 @@ def looks_like_dir(path):
def load_path(python):
import json
from pathlib import Path
python = Path(python).as_posix()
json_dump_commmand = '"import json, sys; print(json.dumps(sys.path));"'
c = subprocess_run([python, "-c", json_dump_commmand])
@@ -96,9 +97,9 @@ def path_to_url(path):
def normalize_path(path):
return os.path.expandvars(os.path.expanduser(
os.path.normcase(os.path.normpath(os.path.abspath(str(path))))
))
return os.path.expandvars(
os.path.expanduser(os.path.normcase(os.path.normpath(os.path.abspath(str(path)))))
)
def get_windows_path(*args):
@@ -157,7 +158,7 @@ def walk_up(bottom):
def find_requirements(max_depth=3):
"""Returns the path of a requirements.txt file in parent directories."""
i = 0
for c, d, f in walk_up(os.getcwd()):
for c, _, _ in walk_up(os.getcwd()):
i += 1
if i < max_depth:
r = os.path.join(c, "requirements.txt")
@@ -183,13 +184,12 @@ def temp_environ():
def escape_cmd(cmd):
if any(special_char in cmd for special_char in ["<", ">", "&", ".", "^", "|", "?"]):
cmd = f'\"{cmd}\"'
cmd = f'"{cmd}"'
return cmd
def safe_expandvars(value):
"""Call os.path.expandvars if value is a string, otherwise do nothing.
"""
"""Call os.path.expandvars if value is a string, otherwise do nothing."""
if isinstance(value, str):
return os.path.expandvars(value)
return value
@@ -239,8 +239,8 @@ def is_virtual_environment(path):
"""
if not path.is_dir():
return False
for bindir_name in ('bin', 'Scripts'):
for python in path.joinpath(bindir_name).glob('python*'):
for bindir_name in ("bin", "Scripts"):
for python in path.joinpath(bindir_name).glob("python*"):
try:
exeness = python.is_file() and os.access(str(python), os.X_OK)
except OSError:
@@ -252,10 +252,17 @@ def is_virtual_environment(path):
def mkdir_p(newdir):
"""works the way a good mkdir should :)
<<<<<<< HEAD
- already exists, silently complete
- regular file in the way, raise an exception
- parent directory(ies) does not exist, make them as well
From: http://code.activestate.com/recipes/82465-a-friendly-mkdir/
=======
- already exists, silently complete
- regular file in the way, raise an exception
- parent directory(ies) does not exist, make them as well
From: http://code.activestate.com/recipes/82465-a-friendly-mkdir/
>>>>>>> main
"""
if os.path.isdir(newdir):
pass
@@ -295,19 +302,18 @@ def find_python(finder, line=None):
"""
if line and not isinstance(line, str):
raise TypeError(
f"Invalid python search type: expected string, received {line!r}"
)
raise TypeError(f"Invalid python search type: expected string, received {line!r}")
if line and os.path.isabs(line):
if os.name == "nt":
line = make_posix(line)
return line
if not finder:
from pipenv.vendor.pythonfinder import Finder
finder = Finder(global_search=True)
if not line:
result = next(iter(finder.find_all_python_versions()), None)
elif line and line[0].isdigit() or re.match(r'[\d\.]+', line):
elif line and line[0].isdigit() or re.match(r"[\d\.]+", line):
result = finder.find_python_version(line)
else:
result = finder.find_python_version(name=line)
@@ -339,9 +345,13 @@ def is_python_command(line):
raise TypeError(f"Not a valid command to check: {line!r}")
from pipenv.vendor.pythonfinder.utils import PYTHON_IMPLEMENTATIONS
is_version = re.match(r'\d+(\.\d+)*', line)
if (line.startswith("python") or is_version
or any(line.startswith(v) for v in PYTHON_IMPLEMENTATIONS)):
is_version = re.match(r"\d+(\.\d+)*", line)
if (
line.startswith("python")
or is_version
or any(line.startswith(v) for v in PYTHON_IMPLEMENTATIONS)
):
return True
# we are less sure about this but we can guess
if line.startswith("py"):
@@ -405,9 +415,7 @@ def handle_remove_readonly(func, path, exc):
Windows source repo folders are read-only by default, so this error handler
attempts to set them as writeable and then proceed with deletion."""
# Check for read-only attribute
default_warning_message = (
"Unable to remove file due to permissions restriction: {!r}"
)
default_warning_message = "Unable to remove file due to permissions restriction: {!r}"
# split the initial exception out into its type, exception, and traceback
exc_type, exc_exception, exc_tb = exc
if is_readonly_path(path):
+3 -1
View File
@@ -5,6 +5,7 @@ import contextlib
def create_spinner(text, setting, nospin=None, spinner_name=None):
from pipenv.vendor.vistir import spin
from pipenv.vendor.vistir.misc import fs_str
if not spinner_name:
spinner_name = setting.PIPENV_SPINNER
if nospin is None:
@@ -12,6 +13,7 @@ def create_spinner(text, setting, nospin=None, spinner_name=None):
with spin.create_spinner(
spinner_name=spinner_name,
start_text=fs_str(text),
nospin=nospin, write_to_stdout=False
nospin=nospin,
write_to_stdout=False,
) as sp:
yield sp
+7 -2
View File
@@ -27,6 +27,7 @@ def cleanup_toml(tml):
def convert_toml_outline_tables(parsed):
"""Converts all outline tables to inline tables."""
def convert_tomlkit_table(section):
if isinstance(section, tomlkit.items.Table):
body = section.value._body
@@ -35,14 +36,18 @@ def convert_toml_outline_tables(parsed):
for key, value in body:
if not key:
continue
if hasattr(value, "keys") and not isinstance(value, tomlkit.items.InlineTable):
if hasattr(value, "keys") and not isinstance(
value, tomlkit.items.InlineTable
):
table = tomlkit.inline_table()
table.update(value.value)
section[key.key] = table
def convert_toml_table(section):
for package, value in section.items():
if hasattr(value, "keys") and not isinstance(value, toml.decoder.InlineTableDict):
if hasattr(value, "keys") and not isinstance(
value, toml.decoder.InlineTableDict
):
table = toml.TomlDecoder().get_empty_inline_table()
table.update(value)
section[package] = table
+5
View File
@@ -70,3 +70,8 @@ template = "news/towncrier_template.rst"
directory = "removal"
name = "Removals and Deprecations"
showcontent = true
[[tool.towncrier.type]]
directory = "process"
name = "Relates to dev process changes"
showcontent = true
+3 -3
View File
@@ -4,7 +4,7 @@ import os
import sys
from shutil import rmtree
from setuptools import find_packages, setup, Command
from setuptools import Command, find_packages, setup
here = os.path.abspath(os.path.dirname(__file__))
@@ -25,7 +25,7 @@ required = [
"certifi",
"setuptools>=36.2.1",
"virtualenv-clone>=0.2.5",
"virtualenv"
"virtualenv",
]
extras = {
"dev": [
@@ -113,7 +113,7 @@ setup(
version=about["__version__"],
description="Python Development Workflow for Humans.",
long_description=long_description,
long_description_content_type='text/markdown',
long_description_content_type="text/markdown",
author="Pipenv maintainer team",
author_email="distutils-sig@python.org",
url="https://github.com/pypa/pipenv",
-1
View File
@@ -6,7 +6,6 @@ import invoke
from . import release, vendoring
ROOT = Path(".").parent.parent.absolute()
ns = invoke.Collection(vendoring, release, release.clean_mdchangelog)
+25 -22
View File
@@ -2,11 +2,10 @@ import datetime
import os
import pathlib
import re
import sys
import subprocess
import sys
import invoke
from parver import Version
from pipenv.__version__ import __version__
@@ -14,7 +13,6 @@ from pipenv.vendor.vistir.contextmanagers import temp_environ
from .vendoring import _get_git_root, drop_dir
VERSION_FILE = "pipenv/__version__.py"
ROOT = pathlib.Path(".").parent.parent.absolute()
PACKAGE_NAME = "pipenv"
@@ -49,8 +47,7 @@ def get_build_dir(ctx):
def _render_log():
"""Totally tap into Towncrier internals to get an in-memory result.
"""
"""Totally tap into Towncrier internals to get an in-memory result."""
rendered = subprocess.check_output(["towncrier", "--draft"]).decode("utf-8")
return rendered
@@ -66,7 +63,9 @@ release_help = {
@invoke.task(help=release_help)
def release(ctx, manual=False, local=False, dry_run=False, pre=False, tag=None, month_offset="0"):
def release(
ctx, manual=False, local=False, dry_run=False, pre=False, tag=None, month_offset="0"
):
trunc_month = False
if pre:
trunc_month = True
@@ -77,7 +76,7 @@ def release(ctx, manual=False, local=False, dry_run=False, pre=False, tag=None,
pre=pre,
tag=tag,
month_offset=month_offset,
trunc_month=trunc_month
trunc_month=trunc_month,
)
tag_content = _render_log()
if dry_run:
@@ -93,9 +92,7 @@ def release(ctx, manual=False, local=False, dry_run=False, pre=False, tag=None,
ctx.run(f"git add {get_version_file(ctx).as_posix()}")
else:
ctx.run("towncrier")
ctx.run(
f"git add CHANGELOG.rst news/ {get_version_file(ctx).as_posix()}"
)
ctx.run(f"git add CHANGELOG.rst news/ {get_version_file(ctx).as_posix()}")
log("removing changelog draft if present")
draft_changelog = pathlib.Path("CHANGELOG.draft.rst")
if draft_changelog.exists():
@@ -150,11 +147,9 @@ def build_dists(ctx):
log("Building sdist using %s ...." % executable)
os.environ["PIPENV_PYTHON"] = py_version
ctx.run("pipenv install --dev", env=env)
ctx.run(
"pipenv run pip install -e . --upgrade --upgrade-strategy=eager", env=env
)
ctx.run("pipenv run pip install -e . --upgrade --upgrade-strategy=eager", env=env)
log("Building wheel using python %s ...." % py_version)
ctx.run(f"pipenv run python setup.py sdist bdist_wheel", env=env)
ctx.run("pipenv run python setup.py sdist bdist_wheel", env=env)
@invoke.task(build_dists)
@@ -266,9 +261,11 @@ def date_offset(dt, month_offset=0, day_offset=0, truncate=False):
"month": dt.month + month_offset,
"year": dt.year + year_offset,
}
log("Getting updated date from date: {} using month offset: {} and year offset {}".format(
dt, new_month, replace_args["year"]
))
log(
"Getting updated date from date: {} using month offset: {} and year offset {}".format(
dt, new_month, replace_args["year"]
)
)
if day_offset:
dt = dt + datetime.timedelta(days=day_offset)
log(f"updated date using day offset: {day_offset} => {dt}")
@@ -279,7 +276,16 @@ def date_offset(dt, month_offset=0, day_offset=0, truncate=False):
@invoke.task
def bump_version(ctx, dry_run=False, dev=False, pre=False, tag=None, commit=False, month_offset="0", trunc_month=False):
def bump_version(
ctx,
dry_run=False,
dev=False,
pre=False,
tag=None,
commit=False,
month_offset="0",
trunc_month=False,
):
current_version = Version.parse(__version__)
current_date = datetime.date(*current_version.release)
today = datetime.date.today()
@@ -295,10 +301,7 @@ def bump_version(ctx, dry_run=False, dev=False, pre=False, tag=None, commit=Fals
):
day_offset = 1
target_day = date_offset(
today,
month_offset=month_offset,
day_offset=day_offset,
truncate=trunc_month
today, month_offset=month_offset, day_offset=day_offset, truncate=trunc_month
)
log(f"target_day: {target_day}")
target_timetuple = target_day.timetuple()[:3]
+27 -32
View File
@@ -3,26 +3,20 @@
""""Vendoring script, python 3.5 needed"""
import itertools
import os
import re
import shutil
# from tempfile import TemporaryDirectory
import tarfile
import zipfile
from pathlib import Path
from tempfile import TemporaryDirectory
import bs4
import invoke
import requests
from urllib3.util import parse_url as urllib3_parse
from tempfile import TemporaryDirectory
from pipenv.vendor.vistir.contextmanagers import open_file
import pipenv.vendor.parse as parse
from pipenv.vendor.vistir.contextmanagers import open_file
TASK_NAME = "update"
@@ -190,10 +184,16 @@ def rewrite_file_imports(item, vendored_libs):
for lib, to_lib in vendored_libs.items():
text = re.sub(
r"^(?m)(\s*)import %s((?:\.\S*)?\s+as)" % lib, r"\1import %s\2" % to_lib, text,
r"^(?m)(\s*)import %s((?:\.\S*)?\s+as)" % lib,
r"\1import %s\2" % to_lib,
text,
)
text = re.sub(r"^(?m)(\s*)from %s([\s\.]+)" % lib, r"\1from %s\2" % to_lib, text)
text = re.sub(r"^(?m)(\s*)import %s(\s*[,\n#])" % lib, r"\1import %s as %s\2" % (to_lib, lib), text)
text = re.sub(
r"^(?m)(\s*)import %s(\s*[,\n#])" % lib,
r"\1import %s as %s\2" % (to_lib, lib),
text,
)
for pattern, sub in GLOBAL_REPLACEMENT:
text = re.sub(pattern, sub, text)
item.write_text(text, encoding="utf-8")
@@ -209,7 +209,7 @@ def _recursive_write_to_zip(zf, path, root=None):
return
if root is None:
if not path.is_dir():
raise ValueError('root is required for non-directory path')
raise ValueError("root is required for non-directory path")
root = path
if not path.is_dir():
zf.write(str(path), str(path.relative_to(root)))
@@ -251,7 +251,7 @@ def write_backport_imports(ctx, vendor_dir):
def _ensure_package_in_requirements(ctx, requirements_file, package):
requirement = None
log("using requirements file: %s" % requirements_file)
req_file_lines = [l for l in requirements_file.read_text().splitlines()]
req_file_lines = [line for line in requirements_file.read_text().splitlines()]
if package:
match = [r for r in req_file_lines if r.strip().lower().startswith(package)]
matched_req = None
@@ -274,8 +274,10 @@ def _ensure_package_in_requirements(ctx, requirements_file, package):
def install_pyyaml(ctx, vendor_dir):
with TemporaryDirectory(prefix="pipenv-", suffix="-yaml") as download_dir:
pip_command = "pip download --no-binary=:all: --no-clean --no-deps -d {} pyyaml".format(
download_dir
pip_command = (
"pip download --no-binary=:all: --no-clean --no-deps -d {} pyyaml".format(
download_dir
)
)
log(f"downloading deps via pip: {pip_command}")
ctx.run(pip_command)
@@ -291,7 +293,9 @@ def install_pyyaml(ctx, vendor_dir):
if yaml_dir.exists():
drop_dir(yaml_dir)
path_dict["current_path"].rename(path_dict["destination"])
path_dict["destination"].joinpath("LICENSE").write_text(extracted.joinpath("LICENSE").read_text())
path_dict["destination"].joinpath("LICENSE").write_text(
extracted.joinpath("LICENSE").read_text()
)
def install(ctx, vendor_dir, package=None):
@@ -305,7 +309,8 @@ def install(ctx, vendor_dir, package=None):
# the chain.
ctx.run(
"pip install -t {} --no-compile --no-deps --upgrade {}".format(
vendor_dir.as_posix(), requirement,
vendor_dir.as_posix(),
requirement,
)
)
# read licenses from distinfo files if possible
@@ -319,22 +324,16 @@ def install(ctx, vendor_dir, package=None):
license_file.read_text()
)
elif vendor_dir.joinpath(f"{pkg}.py").exists():
vendor_dir.joinpath(f"{pkg}.LICENSE").write_text(
license_file.read_text()
)
vendor_dir.joinpath(f"{pkg}.LICENSE").write_text(license_file.read_text())
else:
pkg = pkg.replace("-", "?").replace("_", "?")
matched_path = next(
iter(pth for pth in vendor_dir.glob(f"{pkg}*")), None
)
matched_path = next(iter(pth for pth in vendor_dir.glob(f"{pkg}*")), None)
if matched_path is not None:
if matched_path.is_dir():
target = vendor_dir.joinpath(matched_path).joinpath("LICENSE")
else:
target = vendor_dir.joinpath(f"{matched_path}.LICENSE")
target.write_text(
license_file.read_text()
)
target.write_text(license_file.read_text())
def post_install_cleanup(ctx, vendor_dir):
@@ -460,7 +459,7 @@ def packages_missing_licenses(
".".join(lic)
for lic in itertools.product(("LICENSE", "LICENSE-MIT"), LICENSE_EXTS)
]
for i, req in enumerate(requirements):
for _, req in enumerate(requirements):
if req.startswith("git+"):
pkg = req.strip().split("#egg=")[1]
else:
@@ -549,9 +548,7 @@ def download_licenses(
if "." in backend:
backend, _, _ = backend.partition(".")
ctx.run(f"pip install {backend}")
ctx.run(
f"{cmd} --no-build-isolation -d {tmp_dir.as_posix()} {req}"
)
ctx.run(f"{cmd} --no-build-isolation -d {tmp_dir.as_posix()} {req}")
for sdist in tmp_dir.iterdir():
extract_license(vendor_dir, sdist)
drop_dir(tmp_dir)
@@ -720,9 +717,7 @@ def unpin_and_copy_requirements(ctx, requirement_file, name="requirements.txt"):
ctx.run("pipenv --rm", env=env, hide=True)
result = list(sorted(line.strip() for line in result.splitlines()[1:]))
new_requirements = requirement_file.parent.joinpath(name)
requirement_file.rename(
requirement_file.parent.joinpath(f"{name}.bak")
)
requirement_file.rename(requirement_file.parent.joinpath(f"{name}.bak"))
new_requirements.write_text("\n".join(result))
return result