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 git submodule update --init --recursive
python -m pip install -e . --upgrade python -m pip install -e . --upgrade
pipenv install --deploy --dev --python=${{ steps.python-path.outputs.path }} 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 - name: Run tests
env: env:
PIPENV_DEFAULT_PYTHON_VERSION: ${{ matrix.python-version }} PIPENV_DEFAULT_PYTHON_VERSION: ${{ matrix.python-version }}
@@ -109,4 +117,3 @@ jobs:
- run: | - run: |
python -m pip install --upgrade wheel invoke parver bs4 vistir towncrier python -m pip install --upgrade wheel invoke parver bs4 vistir towncrier
python -m invoke vendoring.update 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 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 - 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`` 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. 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`` - 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 Bug Fixes
--------- ---------
@@ -43,7 +43,7 @@ Bug Fixes
Features & Improvements 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>`_ - New CLI command ``verify``, checks the Pipfile.lock is up-to-date `#4893 <https://github.com/pypa/pipenv/issues/4893>`_
Behavior Changes 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>`_ - 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>`_ - 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>`_ - 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>`_ - Environment variables from dot env files are now properly expanded when included in scripts. `#4975 <https://github.com/pypa/pipenv/issues/4975>`_
Vendored Libraries 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>`_ (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 Removals and Deprecations
@@ -207,7 +207,7 @@ Vendored Libraries
- ``tomli 1.1.0`` - ``tomli 1.1.0``
- ``wheel 0.36.2`` `#4747 <https://github.com/pypa/pipenv/issues/4747>`_ - ``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>`_ - 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 vendor libraries:
- Update ``requirementslib`` from ``1.5.16`` to ``1.6.1`` - 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 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). - 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 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>`_ - 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>`_ - 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>`_ - 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 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 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 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>`_ - 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>`_, - 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>`_, `#3114 <https://github.com/pypa/pipenv/issues/3114>`_,
`#3117 <https://github.com/pypa/pipenv/issues/3117>`_ `#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>`_ - 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 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>`_ - 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>`_ - 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>`_ - 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>`_ - 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 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>`_ - 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 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>`_ 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). - 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``) - 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>`_ 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 Bug Fixes
@@ -918,7 +918,7 @@ Bug Fixes
`#2867 <https://github.com/pypa/pipenv/issues/2867>`_, `#2867 <https://github.com/pypa/pipenv/issues/2867>`_,
`#2880 <https://github.com/pypa/pipenv/issues/2880>`_ `#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>`_ - 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>`_ - 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 - Fixed the example url for doing "pipenv install -e
some-repository-url#egg=something", it was missing the "egg=" in the fragment 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>`_ - 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>`_ - 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} pytest_pypi = {path = "./tests/pytest-pypi", editable = true}
stdeb = {version="*", markers="sys_platform == 'linux'"} stdeb = {version="*", markers="sys_platform == 'linux'"}
dataclasses = {version="*", markers="python_version < '3.7'"} dataclasses = {version="*", markers="python_version < '3.7'"}
importlib-resources = {version = "*", markers = "python_version < '3.7'"}
sphinxcontrib-spelling = "<4.3.0" sphinxcontrib-spelling = "<4.3.0"
pre-commit = "*"
atomicwrites = {version = "*", markers="sys_platform == 'win32'"}
[packages] [packages]
Generated
+292 -180
View File
@@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "b6632ccfba082244f188747d88665264be87621552d2c1bbebaf36174bc24e8a" "sha256": "0d25587c8b692005c51421eb35b08c878ca1d6e14e175d3c9ed74fd4d637476d"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": {}, "requires": {},
@@ -39,11 +39,11 @@
}, },
"attrs": { "attrs": {
"hashes": [ "hashes": [
"sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1", "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4",
"sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb" "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "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": { "babel": {
"hashes": [ "hashes": [
@@ -53,14 +53,6 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.9.1" "version": "==2.9.1"
}, },
"backports.entry-points-selectable": {
"hashes": [
"sha256:7fceed9532a7aa2bd888654a7314f864a3c16a4e710b34a58cfc0f08114c663b",
"sha256:914b21a479fde881635f7af5adc7f6e38d6b274be32269070c53b698c60d5386"
],
"markers": "python_version >= '2.7'",
"version": "==1.1.1"
},
"beautifulsoup4": { "beautifulsoup4": {
"hashes": [ "hashes": [
"sha256:9a315ce70049920ea4572a4055bc4bd700c940521d36fc858205ad4fcde149bf", "sha256:9a315ce70049920ea4572a4055bc4bd700c940521d36fc858205ad4fcde149bf",
@@ -71,11 +63,11 @@
}, },
"black": { "black": {
"hashes": [ "hashes": [
"sha256:0b1f66cbfadcd332ceeaeecf6373d9991d451868d2e2219ad0ac1213fb701117", "sha256:77b80f693a569e2e527958459634f18df9b0ba2625ba4e0c2d5da5be42e6f2b3",
"sha256:83f3852301c8dcb229e9c444dd79f573c8d31c7c2dad9bbaaa94c808630e32aa" "sha256:a615e69ae185e08fdd73e4715e260e2479c861b5740057fde6e8b4e3b7dd589f"
], ],
"markers": "python_full_version >= '3.6.2'", "markers": "python_full_version >= '3.6.2'",
"version": "==21.11b0" "version": "==21.12b0"
}, },
"bleach": { "bleach": {
"hashes": [ "hashes": [
@@ -98,13 +90,76 @@
], ],
"version": "==2021.10.8" "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": { "charset-normalizer": {
"hashes": [ "hashes": [
"sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0", "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597",
"sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b" "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"
], ],
"markers": "python_version >= '3.0'", "markers": "python_version >= '3.0'",
"version": "==2.0.7" "version": "==2.0.12"
}, },
"click": { "click": {
"hashes": [ "hashes": [
@@ -125,9 +180,35 @@
"sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b", "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b",
"sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2" "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" "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": { "dataclasses": {
"hashes": [ "hashes": [
"sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf", "sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf",
@@ -139,10 +220,10 @@
}, },
"distlib": { "distlib": {
"hashes": [ "hashes": [
"sha256:c8b54e8454e5bf6237cc84c20e8264c3e991e824ef27e8f1e81049867d861e31", "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b",
"sha256:d982d0751ff6eaaab5e2ec8e691d949ee80eddf01a62eaa96ddb11531fe16b05" "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"
], ],
"version": "==0.3.3" "version": "==0.3.4"
}, },
"docutils": { "docutils": {
"hashes": [ "hashes": [
@@ -162,11 +243,11 @@
}, },
"filelock": { "filelock": {
"hashes": [ "hashes": [
"sha256:2e139a228bcf56dd8b2274a65174d005c4a6b68540ee0bdbb92c76f43f29f7e8", "sha256:0f12f552b42b5bf60dba233710bf71337d35494fc8bdd4fd6d9f6d082ad45e06",
"sha256:93d512b32a23baf4cac44ffd72ccf70732aeff7b8050fcaf6d3ec406d954baf4" "sha256:a4bc51381e01502a30e9f06dd4fa19a1712eab852b6fb0f84fd7cce0793d8ca3"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==3.4.0" "version": "==3.4.1"
}, },
"flake8": { "flake8": {
"hashes": [ "hashes": [
@@ -186,11 +267,19 @@
}, },
"flask": { "flask": {
"hashes": [ "hashes": [
"sha256:7b2fb8e934ddd50731893bdcdb00fc8c0315916f9fcd50d22c7cc1a95ab634e2", "sha256:59da8a3170004800a2837844bfa84d49b022550616070f7cb1a659682b2e7c9f",
"sha256:cb90f62f1d8e4dc4621f52106613488b5ba826b2e1e10a33eac92f723093ab6a" "sha256:e1120c228ca2f553b470df4a5fa927ab66258467526069981b3eb0a91902687d"
], ],
"markers": "python_version >= '3.6'", "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": { "idna": {
"hashes": [ "hashes": [
@@ -210,19 +299,20 @@
}, },
"importlib-metadata": { "importlib-metadata": {
"hashes": [ "hashes": [
"sha256:53ccfd5c134223e497627b9815d5030edf77d2ed573922f7a0b8f8bb81a1c100", "sha256:65a9576a5b2d58ca44d133c42a241905cc45e34d2c06fd5ba2bafa221e5d7b5e",
"sha256:75bdec14c397f528724c1bfd9709d660b33a4d2e77387a3358f20b848bb5e5fb" "sha256:766abffff765960fcc18003801f7044eb6755ffae4521c8e8ce8e83b9c9b0668"
], ],
"markers": "python_version < '3.8'", "markers": "python_version < '3.8'",
"version": "==4.8.2" "version": "==4.8.3"
}, },
"importlib-resources": { "importlib-resources": {
"hashes": [ "hashes": [
"sha256:33a95faed5fc19b4bc16b29a6eeae248a3fe69dd55d4d229d2b480e23eeaad45", "sha256:203d70dda34cfbfbb42324a8d4211196e7d3e858de21a5eb68c6d1cdd99e4e98",
"sha256:d756e2f85dd4de2ba89be0b21dba2a3bbec2e871a42a3a16719258a11f87506b" "sha256:ae35ed1cfe8c0d6c1a53ecd168167f01fa93b893d51a62cdf23aea044c67211b"
], ],
"index": "pypi",
"markers": "python_version < '3.7'", "markers": "python_version < '3.7'",
"version": "==5.4.0" "version": "==5.2.3"
}, },
"incremental": { "incremental": {
"hashes": [ "hashes": [
@@ -240,11 +330,10 @@
}, },
"invoke": { "invoke": {
"hashes": [ "hashes": [
"sha256:374d1e2ecf78981da94bfaf95366216aaec27c2d6a7b7d5818d92da55aa258d3", "sha256:a5159fc63dba6ca2a87a1e33d282b99cea69711b03c64a35bb4e1c53c6c4afa0",
"sha256:769e90caeb1bd07d484821732f931f1ad8916a38e3f3e618644687fc09cb6317", "sha256:e332e49de40463f2016315f51df42313855772be86435686156bc18f45b5cc6c"
"sha256:e6c9917a1e3e73e7ea91fdf82d5f151ccfe85bf30cc65cdb892444c02dbb5f74"
], ],
"version": "==1.6.0" "version": "==1.7.0"
}, },
"itsdangerous": { "itsdangerous": {
"hashes": [ "hashes": [
@@ -254,6 +343,14 @@
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==2.0.1" "version": "==2.0.1"
}, },
"jeepney": {
"hashes": [
"sha256:1b5a0ea5c0e7b166b2f5895b91a08c14de8915afda4407fb5022a195224958ac",
"sha256:fa9e232dfa0c498bd0b8a3a73b8d8a31978304dcef0515adc859d4e096f96f4f"
],
"markers": "sys_platform == 'linux'",
"version": "==0.7.1"
},
"jinja2": { "jinja2": {
"hashes": [ "hashes": [
"sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8", "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8",
@@ -264,11 +361,11 @@
}, },
"keyring": { "keyring": {
"hashes": [ "hashes": [
"sha256:6334aee6073db2fb1f30892697b1730105b5e9a77ce7e61fca6b435225493efe", "sha256:17e49fb0d6883c2b4445359434dba95aad84aabb29bbff044ad0ed7100232eca",
"sha256:bd2145a237ed70c8ce72978b497619ddfcae640b6dcf494402d5143e37755c6e" "sha256:89cbd74d4683ed164c8082fb38619341097741323b3786905c6dac04d6915a55"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==23.2.1" "version": "==23.4.1"
}, },
"markupsafe": { "markupsafe": {
"hashes": [ "hashes": [
@@ -367,13 +464,20 @@
], ],
"version": "==0.4.3" "version": "==0.4.3"
}, },
"nodeenv": {
"hashes": [
"sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b",
"sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7"
],
"version": "==1.6.0"
},
"packaging": { "packaging": {
"hashes": [ "hashes": [
"sha256:096d689d78ca690e4cd8a89568ba06d07ca097e3306a4381635073ca91479966", "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb",
"sha256:14317396d1e8cdb122989b916fa2c7e9ca8e2be9e8060a6eff75b6b7b4d8a7e0" "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==21.2" "version": "==21.3"
}, },
"parver": { "parver": {
"hashes": [ "hashes": [
@@ -408,10 +512,10 @@
}, },
"pkginfo": { "pkginfo": {
"hashes": [ "hashes": [
"sha256:37ecd857b47e5f55949c41ed061eb51a0bee97a87c969219d144c0e023982779", "sha256:542e0d0b6750e2e21c20179803e40ab50598d8066d51097a0e382cba9eb02bff",
"sha256:e7432f81d08adec7297633191bbf0bd47faf13cd8724c3a13250e51d542635bd" "sha256:c24c487c6a7f72c66e816ab1796b96ac6c3d14d49338293d2141664330b55ffc"
], ],
"version": "==1.7.1" "version": "==1.8.2"
}, },
"platformdirs": { "platformdirs": {
"hashes": [ "hashes": [
@@ -429,6 +533,14 @@
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==1.0.0" "version": "==1.0.0"
}, },
"pre-commit": {
"hashes": [
"sha256:725fa7459782d7bec5ead072810e47351de01709be838c2ce1726b9591dad616",
"sha256:c1a8040ff15ad3d648c70cc3e55b93e4d2d5b687320955505587fd79bbaed06a"
],
"index": "pypi",
"version": "==2.17.0"
},
"py": { "py": {
"hashes": [ "hashes": [
"sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719", "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719",
@@ -445,6 +557,13 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.7.0" "version": "==2.7.0"
}, },
"pycparser": {
"hashes": [
"sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9",
"sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"
],
"version": "==2.21"
},
"pyenchant": { "pyenchant": {
"hashes": [ "hashes": [
"sha256:1cf830c6614362a78aab78d50eaf7c6c93831369c52e1bb64ffae1df0341e637", "sha256:1cf830c6614362a78aab78d50eaf7c6c93831369c52e1bb64ffae1df0341e637",
@@ -465,35 +584,35 @@
}, },
"pygments": { "pygments": {
"hashes": [ "hashes": [
"sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380", "sha256:44238f1b60a76d78fc8ca0528ee429702aae011c265fe6a8dd8b63049ae41c65",
"sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6" "sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a"
], ],
"markers": "python_version >= '3.5'", "markers": "python_version >= '3.5'",
"version": "==2.10.0" "version": "==2.11.2"
}, },
"pyparsing": { "pyparsing": {
"hashes": [ "hashes": [
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea",
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"
], ],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "markers": "python_version >= '3.6'",
"version": "==2.4.7" "version": "==3.0.7"
}, },
"pytest": { "pytest": {
"hashes": [ "hashes": [
"sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89", "sha256:9ce3ff477af913ecf6321fe337b93a2c0dcf2a0a1439c43f5452112c1e4280db",
"sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134" "sha256:e30905a0c131d3d94b89624a1cc5afec3e0ba2fbdb151867d8e0ebd49850f171"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==6.2.5" "version": "==7.0.1"
}, },
"pytest-forked": { "pytest-forked": {
"hashes": [ "hashes": [
"sha256:6aa9ac7e00ad1a539c41bec6d21011332de671e938c7637378ec9710204e37ca", "sha256:8b67587c8f98cbbadfdd804539ed5455b6ed03802203485dd2f53c1422d7440e",
"sha256:dc4147784048e70ef5d437951728825a131b81714b398d5d52f17c7c144d8815" "sha256:bbbb6717efc886b9d64537b41fb1497cfaf3c9601276be8da2cccfea5a3c8ad8"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "markers": "python_version >= '3.6'",
"version": "==1.3.0" "version": "==1.4.0"
}, },
"pytest-pypi": { "pytest-pypi": {
"editable": true, "editable": true,
@@ -501,19 +620,19 @@
}, },
"pytest-timeout": { "pytest-timeout": {
"hashes": [ "hashes": [
"sha256:329bdea323d3e5bea4737070dd85a0d1021dbecb2da5342dc25284fdb929dff0", "sha256:c07ca07404c612f8abbe22294b23c368e2e5104b521c1790195561f37e1ac3d9",
"sha256:a5ec4eceddb8ea726911848593d668594107e797621e97f93a1d1dbc6fbb9080" "sha256:f6f50101443ce70ad325ceb4473c4255e9d74e3c7cd0ef827309dfa4c0d975c6"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==2.0.1" "version": "==2.1.0"
}, },
"pytest-xdist": { "pytest-xdist": {
"hashes": [ "hashes": [
"sha256:7b61ebb46997a0820a263553179d6d1e25a8c50d8a8620cd1aa1e20e3be99168", "sha256:4580deca3ff04ddb2ac53eba39d76cb5dd5edeac050cb6fbc768b0dd712b4edf",
"sha256:89b330316f7fc475f999c81b577c2b926c9569f3d397ae432c0c2e2496d61ff9" "sha256:6fe5c74fec98906deb8f2d2b616b5c782022744978e7bd4695d39c8f42d0ce65"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==2.4.0" "version": "==2.5.0"
}, },
"pytz": { "pytz": {
"hashes": [ "hashes": [
@@ -522,82 +641,60 @@
], ],
"version": "==2021.3" "version": "==2021.3"
}, },
"pywin32-ctypes": { "pyyaml": {
"hashes": [ "hashes": [
"sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942", "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293",
"sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98" "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'", "markers": "python_version >= '3.6'",
"version": "==0.2.0" "version": "==6.0"
}, },
"readme-renderer": { "readme-renderer": {
"hashes": [ "hashes": [
"sha256:3286806450d9961d6e3b5f8a59f77e61503799aca5155c8d8d40359b4e1e1adc", "sha256:262510fe6aae81ed4e94d8b169077f325614c0b1a45916a80442c6576264a9c2",
"sha256:8299700d7a910c304072a7601eafada6712a5b011a20139417e1b1e9f04645d8" "sha256:dfb4d17f21706d145f7473e0b61ca245ba58e810cf9b2209a48239677f82e5b0"
], ],
"version": "==30.0" "markers": "python_version >= '3.6'",
}, "version": "==34.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"
}, },
"requests": { "requests": {
"hashes": [ "hashes": [
"sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24", "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61",
"sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7" "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", "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": { "requests-toolbelt": {
"hashes": [ "hashes": [
@@ -611,15 +708,24 @@
"sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835", "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835",
"sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97" "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"
], ],
"markers": "python_version >= '3.7'",
"version": "==1.5.0" "version": "==1.5.0"
}, },
"secretstorage": {
"hashes": [
"sha256:422d82c36172d88d6a0ed5afdec956514b189ddbfb72fefab0c8a1cee4eaf71f",
"sha256:fd666c51a6bf200643495a04abb261f83229dcb6fd8472ec393df7ffc8b6f195"
],
"markers": "sys_platform == 'linux'",
"version": "==3.3.1"
},
"setuptools": { "setuptools": {
"hashes": [ "hashes": [
"sha256:94ee891f4759150cded601a6beb6b08400413aefd0267b692f3f8c6e0bb238e7", "sha256:22c7348c6d2976a52632c67f7ab0cdf40147db7789f9aed18734643fe9cf3373",
"sha256:fb537610c2dfe77b5896e3ee53dd53fbdd9adc48076c8f28cee3a30fb59a5038" "sha256:4ce92f1e1f8f01233ee9952c04f6b81d1e02939d6e1b488428154974a4d0783e"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==59.1.1" "version": "==59.6.0"
}, },
"six": { "six": {
"hashes": [ "hashes": [
@@ -720,6 +826,7 @@
"hashes": [ "hashes": [
"sha256:08c22c9c03b28a140fe3ec5064b53a5288279f22e596ca06b0be698d50c93cf2" "sha256:08c22c9c03b28a140fe3ec5064b53a5288279f22e596ca06b0be698d50c93cf2"
], ],
"index": "pypi",
"markers": "sys_platform == 'linux'", "markers": "sys_platform == 'linux'",
"version": "==0.10.0" "version": "==0.10.0"
}, },
@@ -733,83 +840,88 @@
}, },
"tomli": { "tomli": {
"hashes": [ "hashes": [
"sha256:c6ce0015eb38820eaf32b5db832dbc26deb3dd427bd5f6556cf0acac2c214fee", "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f",
"sha256:f04066f68f5554911363063a30b108d2b5a5b1a010aa8b6132af78489fe3aade" "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==1.2.2" "version": "==1.2.3"
}, },
"towncrier": { "towncrier": {
"hashes": [ "hashes": [
"sha256:930454ab86da25aae01bf0f37927e565d9366b12b948d5f533c7c7641dba7b16", "sha256:9cb6f45c16e1a1eec9d0e7651165e7be60cd0ab81d13a5c96ca97a498ae87f48",
"sha256:bfc86ad9dd28c53dfefbb6d7d33875a8f459c5491e18857be4bcff096b3192c3" "sha256:fc5a88a2a54988e3a8ed2b60d553599da8330f65722cc607c839614ed87e0f92"
], ],
"version": "==21.9.0rc1" "version": "==21.9.0"
}, },
"tqdm": { "tqdm": {
"hashes": [ "hashes": [
"sha256:8dd278a422499cd6b727e6ae4061c40b48fce8b76d1ccbf5d34fca9b7f925b0c", "sha256:1d9835ede8e394bb8c9dcbffbca02d717217113adc679236873eeaac5bc0b3cd",
"sha256:d359de7217506c9851b7869f3708d8ee53ed70a1b8edbba4dbcb47442592920d" "sha256:e643e071046f17139dea55b880dc9b33822ce21613b4a4f5ea57f202833dbc29"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "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": { "twine": {
"hashes": [ "hashes": [
"sha256:4caad5ef4722e127b3749052fcbffaaf71719b19d4fd4973b29c469957adeba2", "sha256:8efa52658e0ae770686a13b675569328f1fba9837e5de1867bfe5f46a9aefe19",
"sha256:916070f8ecbd1985ebed5dbb02b9bda9a092882a96d7069d542d4fc0bb5c673c" "sha256:d0550fca9dc19f3d5e8eadfce0c227294df0a2a951251a4385797c8a6198b7c8"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==3.6.0" "version": "==3.8.0"
}, },
"typed-ast": { "typed-ast": {
"hashes": [ "hashes": [
"sha256:14fed8820114a389a2b7e91624db5f85f3f6682fda09fe0268a59aabd28fe5f5", "sha256:0eb77764ea470f14fcbb89d51bc6bbf5e7623446ac4ed06cbd9ca9495b62e36e",
"sha256:155b74b078be842d2eb630dd30a280025eca0a5383c7d45853c27afee65f278f", "sha256:1098df9a0592dd4c8c0ccfc2e98931278a6c6c53cb3a3e2cf7e9ee3b06153344",
"sha256:224afecb8b39739f5c9562794a7c98325cb9d972712e1a98b6989a4720219541", "sha256:183b183b7771a508395d2cbffd6db67d6ad52958a5fdc99f450d954003900266",
"sha256:361b9e5d27bd8e3ccb6ea6ad6c4f3c0be322a1a0f8177db6d56264fa0ae40410", "sha256:18fe320f354d6f9ad3147859b6e16649a0781425268c4dde596093177660e71a",
"sha256:37ba2ab65a0028b1a4f2b61a8fe77f12d242731977d274a03d68ebb751271508", "sha256:26a432dc219c6b6f38be20a958cbe1abffcc5492821d7e27f08606ef99e0dffd",
"sha256:49af5b8f6f03ed1eb89ee06c1d7c2e7c8e743d720c3746a5857609a1abc94c94", "sha256:294a6903a4d087db805a7656989f613371915fc45c8cc0ddc5c5a0a8ad9bea4d",
"sha256:51040bf45aacefa44fa67fb9ebcd1f2bec73182b99a532c2394eea7dabd18e24", "sha256:31d8c6b2df19a777bc8826770b872a45a1f30cfefcfd729491baa5237faae837",
"sha256:52ca2b2b524d770bed7a393371a38e91943f9160a190141e0df911586066ecda", "sha256:33b4a19ddc9fc551ebabca9765d54d04600c4a50eda13893dadf67ed81d9a098",
"sha256:618912cbc7e17b4aeba86ffe071698c6e2d292acbd6d1d5ec1ee724b8c4ae450", "sha256:42c47c3b43fe3a39ddf8de1d40dbbfca60ac8530a36c9b198ea5b9efac75c09e",
"sha256:65c81abbabda7d760df7304d843cc9dbe7ef5d485504ca59a46ae2d1731d2428", "sha256:525a2d4088e70a9f75b08b3f87a51acc9cde640e19cc523c7e41aa355564ae27",
"sha256:7b310a207ee9fde3f46ba327989e6cba4195bc0c8c70a158456e7b10233e6bed", "sha256:58ae097a325e9bb7a684572d20eb3e1809802c5c9ec7108e85da1eb6c1a3331b",
"sha256:7e6731044f748340ef68dcadb5172a4b1f40847a2983fe3983b2a66445fbc8e6", "sha256:676d051b1da67a852c0447621fdd11c4e104827417bf216092ec3e286f7da596",
"sha256:806e0c7346b9b4af8c62d9a29053f484599921a4448c37fbbcbbf15c25138570", "sha256:74cac86cc586db8dfda0ce65d8bcd2bf17b58668dfcc3652762f3ef0e6677e76",
"sha256:a67fd5914603e2165e075f1b12f5a8356bfb9557e8bfb74511108cfbab0f51ed", "sha256:8c08d6625bb258179b6e512f55ad20f9dfef019bbfbe3095247401e053a3ea30",
"sha256:e4374a76e61399a173137e7984a1d7e356038cf844f24fd8aea46c8029a2f712", "sha256:90904d889ab8e81a956f2c0935a523cc4e077c7847a836abee832f868d5c26a4",
"sha256:e8a9b9c87801cecaad3b4c2b8876387115d1a14caa602c1618cedbb0cb2a14e6", "sha256:963a0ccc9a4188524e6e6d39b12c9ca24cc2d45a71cfdd04a26d883c922b4b78",
"sha256:ea517c2bb11c5e4ba7a83a91482a2837041181d57d3ed0749a6c382a2b6b7086", "sha256:bbebc31bf11762b63bf61aaae232becb41c5bf6b3461b80a4df7e791fabb3aca",
"sha256:ec184dfb5d3d11e82841dbb973e7092b75f306b625fad7b2e665b64c5d60ab3f", "sha256:bc2542e83ac8399752bc16e0b35e038bdb659ba237f4222616b4e83fb9654985",
"sha256:ff4ad88271aa7a55f19b6a161ed44e088c393846d954729549e3cde8257747bb" "sha256:c29dd9a3a9d259c9fa19d19738d021632d673f6ed9b35a739f48e5f807f264fb",
"sha256:c7407cfcad702f0b6c0e0f3e7ab876cd1d2c13b14ce770e412c0c4b9728a0f88",
"sha256:da0a98d458010bf4fe535f2d1e367a2e2060e105978873c04c04212fb20543f7",
"sha256:df05aa5b241e2e8045f5f4367a9f6187b09c4cdf8578bb219861c4e27c443db5",
"sha256:f290617f74a610849bd8f5514e34ae3d09eafd521dceaa6cf68b3f4414266d4e",
"sha256:f30ddd110634c2d7534b2d4e0e22967e88366b0d356b24de87419cc4410c41b7"
], ],
"markers": "python_version < '3.8' and implementation_name == 'cpython'", "markers": "python_version < '3.8' and implementation_name == 'cpython'",
"version": "==1.5.0" "version": "==1.5.2"
}, },
"typing-extensions": { "typing-extensions": {
"hashes": [ "hashes": [
"sha256:2cdf80e4e04866a9b3689a51869016d36db0814d84b8d8a568d22781d45d27ed", "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42",
"sha256:829704698b22e13ec9eaf959122315eabb370b0884400e9818334d8b677023d9" "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==4.0.0" "version": "==4.1.1"
}, },
"urllib3": { "urllib3": {
"hashes": [ "hashes": [
"sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece", "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14",
"sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844" "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'", "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": { "virtualenv": {
"hashes": [ "hashes": [
"sha256:4b02e52a624336eece99c96e3ab7111f469c24ba226a53ec474e8e787b365814", "sha256:c3e01300fb8495bc00ed70741f5271fc95fed067eb7106297be73d30879af60c",
"sha256:576d05b46eace16a9c348085f7d0dc8ef28713a2cabaa1cf0aea41e8f12c9218" "sha256:ce8901d3bbf3b90393498187f2d56797a8a452fb2d0d7efc6fd837554d6f679c"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "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": { "virtualenv-clone": {
"hashes": [ "hashes": [
@@ -828,18 +940,18 @@
}, },
"werkzeug": { "werkzeug": {
"hashes": [ "hashes": [
"sha256:63d3dc1cf60e7b7e35e97fa9861f7397283b75d765afcaefd993d6046899de8f", "sha256:1421ebfc7648a39a5c58c601b154165d05cf47a3cd0ccb70857cbdacf6c8f2b8",
"sha256:aa2bb6fc8dee8d6c504c0ac1e7f5f7dc5810a9903e793b6f715a9f015bdadb9a" "sha256:b863f8ff057c522164b6067c9e28b041161b4be5ba4d0daceeaa50a163822d3c"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==2.0.2" "version": "==2.0.3"
}, },
"zipp": { "zipp": {
"hashes": [ "hashes": [
"sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832", "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832",
"sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc" "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version < '3.10'",
"version": "==3.6.0" "version": "==3.6.0"
} }
} }
+7 -7
View File
@@ -274,7 +274,7 @@ Example::
.. note:: .. 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 insecure Python packages and `makes it available to the
community for free <https://pyup.io/safety/>`__. Pipenv community for free <https://pyup.io/safety/>`__. Pipenv
makes an API call to retrieve those results and use them 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'] >>> os.environ['HELLO']
'WORLD' '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 $ cat .env
CONFIG_PATH=${HOME}/.config/foo 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 It's reasonably common for platform specific Python bindings for
operating system interfaces to only be available through the system operating system interfaces to only be available through the system
package manager, and hence unavailable for installation into virtual package manager, and hence unavailable for installation into virtual
environments with `pip`. In these cases, the virtual environment can environments with ``pip``. In these cases, the virtual environment can
be created with access to the system `site-packages` directory:: be created with access to the system ``site-packages`` directory::
$ pipenv --three --site-packages $ 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 into the virtual environment and system packages are only used for
interfaces that don't participate in Python-level dependency resolution 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 $ PIP_IGNORE_INSTALLED=1 pipenv install --dev
@@ -618,7 +618,7 @@ Libraries are ultimately meant to be used in some **application**. Applications
To summarize: 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 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. - 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``. - 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. - 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. - 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>`_. - ``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>`_. - 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 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones. # ones.
extensions = [ extensions = [
'sphinx.ext.autodoc', "sphinx.ext.autodoc",
'sphinx.ext.todo', "sphinx.ext.todo",
'sphinx.ext.coverage', "sphinx.ext.coverage",
'sphinx.ext.viewcode', "sphinx.ext.viewcode",
'sphinx_click.ext', "sphinx_click.ext",
] ]
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates'] templates_path = ["_templates"]
# The suffix(es) of source filenames. # The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string: # You can specify multiple suffix as a list of string:
# #
# source_suffix = ['.rst', '.md'] # source_suffix = ['.rst', '.md']
source_suffix = '.rst' source_suffix = ".rst"
# The master toctree document. # The master toctree document.
master_doc = 'index' master_doc = "index"
# General information about the project. # 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>' 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 # The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the # |version| and |release|, also used in various other places throughout the
# built documents. # built documents.
# #
# The short X.Y version. # The short X.Y version.
version = about['__version__'] version = about["__version__"]
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = about['__version__'] release = about["__version__"]
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.
@@ -77,10 +77,10 @@ language = None
# List of patterns, relative to source directory, that match files and # List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files. # directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path # 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. # 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. # If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True 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 # The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes. # 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 # 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 # further. For a list of options available for each theme, see the
# documentation. # documentation.
# #
html_theme_options = { html_theme_options = {
'show_powered_by': False, "show_powered_by": False,
'github_user': 'pypa', "github_user": "pypa",
'github_repo': 'pipenv', "github_repo": "pipenv",
'github_banner': False, "github_banner": False,
'show_related': False "show_related": False,
} }
html_sidebars = { html_sidebars = {
'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html', "index": ["sidebarintro.html", "sourcelink.html", "searchbox.html", "hacks.html"],
'hacks.html'], "**": [
'**': ['sidebarlogo.html', 'localtoc.html', 'relations.html', "sidebarlogo.html",
'sourcelink.html', 'searchbox.html', 'hacks.html'] "localtoc.html",
"relations.html",
"sourcelink.html",
"searchbox.html",
"hacks.html",
],
} }
# Add any paths that contain custom static files (such as style sheets) here, # 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, # relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css". # so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static'] html_static_path = ["_static"]
def setup(app): def setup(app):
app.add_stylesheet('custom.css') app.add_stylesheet("custom.css")
# -- Options for HTMLHelp output ------------------------------------------ # -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder. # Output file base name for HTML help builder.
htmlhelp_basename = 'pipenvdoc' htmlhelp_basename = "pipenvdoc"
# -- Options for LaTeX output --------------------------------------------- # -- Options for LaTeX output ---------------------------------------------
@@ -134,15 +139,12 @@ latex_elements = {
# The paper size ('letterpaper' or 'a4paper'). # The paper size ('letterpaper' or 'a4paper').
# #
# 'papersize': 'letterpaper', # 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt'). # The font size ('10pt', '11pt' or '12pt').
# #
# 'pointsize': '10pt', # 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble. # Additional stuff for the LaTeX preamble.
# #
# 'preamble': '', # 'preamble': '',
# Latex figure (float) alignment # Latex figure (float) alignment
# #
# 'figure_align': 'htbp', # 'figure_align': 'htbp',
@@ -152,8 +154,7 @@ latex_elements = {
# (source start file, target name, title, # (source start file, target name, title,
# author, documentclass [howto, manual, or own class]). # author, documentclass [howto, manual, or own class]).
latex_documents = [ latex_documents = [
(master_doc, 'pipenv.tex', 'pipenv Documentation', (master_doc, "pipenv.tex", "pipenv Documentation", "Kenneth Reitz", "manual"),
'Kenneth Reitz', 'manual'),
] ]
@@ -161,10 +162,7 @@ latex_documents = [
# One entry per manual page. List of tuples # One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section). # (source start file, name, description, authors, manual section).
man_pages = [ man_pages = [(master_doc, "pipenv", "pipenv Documentation", [author], 1)]
(master_doc, 'pipenv', 'pipenv Documentation',
[author], 1)
]
# -- Options for Texinfo output ------------------------------------------- # -- Options for Texinfo output -------------------------------------------
@@ -173,9 +171,15 @@ man_pages = [
# (source start file, target name, title, author, # (source start file, target name, title, author,
# dir menu entry, description, category) # dir menu entry, description, category)
texinfo_documents = [ texinfo_documents = [
(master_doc, 'pipenv', 'pipenv Documentation', (
author, 'pipenv', 'One line description of project.', master_doc,
'Miscellaneous'), "pipenv",
"pipenv Documentation",
author,
"pipenv",
"One line description of project.",
"Miscellaneous",
),
] ]
@@ -197,4 +201,4 @@ epub_copyright = copyright
# epub_uid = '' # epub_uid = ''
# A list of files that should not be packed into the epub file. # 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. 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 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 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 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 .. _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: .. _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 will allow you, for example, to run a single test case, or
``fail fast`` if you need it. ``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 .. 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 this may take some work, for example, on a specific Mac installation, the following
steps may be needed:: steps may be needed::
.. code-block:: bash
# Make sure the tests can access github # Make sure the tests can access github
if [ "$SSH_AGENT_PID" = "" ] if [ "$SSH_AGENT_PID" = "" ]
then then
eval `ssh-agent` eval ``ssh-agent``
ssh-add ssh-add
fi fi
+1 -1
View File
@@ -1,7 +1,7 @@
.. pipenv documentation master file, created by .. pipenv documentation master file, created by
sphinx-quickstart on Mon Jan 30 13:28:36 2017. sphinx-quickstart on Mon Jan 30 13:28:36 2017.
You can adapt this file completely to your liking, but it should at least 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 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 # Don't manually edit this script! Check ths instructions in the link above to
# create get-pip.py and patch the following: # create get-pip.py and patch the following:
#+++ ./get-pip/public/get-pip.py 2022-01-12 16:52:11.920161471 +0100 # +++ ./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 # --- ./pipenv/get-pipenv.py 2022-01-12 20:11:35.816906142 +0100
#@@ -55,7 +28,7 @@ # @@ -55,7 +28,7 @@
# message_parts = [ # message_parts = [
# "This script does not work on Python {}.{}".format(*this_python), # "This script does not work on Python {}.{}".format(*this_python),
# "The minimum supported Python version is {}.{}.".format(*min_version), # "The minimum supported Python version is {}.{}.".format(*min_version),
#- "Please use an alternative installation https://pipenv.pypa.io/en/latest/install/", # - "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 https://bootstrap.pypa.io/pip/{}.{}/get-pip.py instead.".format(*this_python),
# ] # ]
# print("ERROR: " + " ".join(message_parts)) # print("ERROR: " + " ".join(message_parts))
# sys.exit(1) # sys.exit(1)
#@@ -70,7 +43,7 @@ # @@ -70,7 +43,7 @@
# #
# def determine_pip_install_arguments(): # def determine_pip_install_arguments():
# implicit_pip = True # implicit_pip = True
#+ implicit_setuptools = False # + implicit_setuptools = False
#- implicit_setuptools = True # - implicit_setuptools = True
# implicit_wheel = True # implicit_wheel = True
# #
# # Check if the user has requested us not to install setuptools # # 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 # # We only want to implicitly install setuptools and wheel if they don't
# # already exist on the target platform. # # 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 # + # a dependency in setup.py
# if implicit_setuptools: # if implicit_setuptools:
# try: # try:
# import setuptools # noqa # import setuptools # noqa
#@@ -109,8 +80,6 @@ # @@ -109,8 +80,6 @@
# args += ["setuptools"] # args += ["setuptools"]
# if implicit_wheel: # if implicit_wheel:
# args += ["wheel"] # args += ["wheel"]
#+ # +
#+ args += ["pipenv"] # + args += ["pipenv"]
# #
# return ["install", "--upgrade", "--force-reinstall"] + args # return ["install", "--upgrade", "--force-reinstall"] + args
# YMMV, so dig a bit to find how to add pipenv to the args passed to pip. # YMMV, so dig a bit to find how to add pipenv to the args passed to pip.
import sys import sys
@@ -103,17 +103,19 @@ def determine_pip_install_arguments():
# We only want to implicitly install setuptools and wheel if they don't # We only want to implicitly install setuptools and wheel if they don't
# already exist on the target platform. # 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 # a dependency in setup.py
if implicit_setuptools: if implicit_setuptools:
try: try:
import setuptools # noqa import setuptools # noqa
implicit_setuptools = False implicit_setuptools = False
except ImportError: except ImportError:
pass pass
if implicit_wheel: if implicit_wheel:
try: try:
import wheel # noqa import wheel # noqa
implicit_wheel = False implicit_wheel = False
except ImportError: except ImportError:
pass pass
@@ -125,7 +127,7 @@ def determine_pip_install_arguments():
args += ["setuptools"] args += ["setuptools"]
if implicit_wheel: if implicit_wheel:
args += ["wheel"] args += ["wheel"]
args += ["pipenv"] args += ["pipenv"]
return ["install", "--upgrade", "--force-reinstall"] + args 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 # Execute the included pip and use it to install the latest pip and
# setuptools from PyPI # setuptools from PyPI
from pip._internal.cli.main import main as pip_entry_point from pip._internal.cli.main import main as pip_entry_point
args = determine_pip_install_arguments() args = determine_pip_install_arguments()
sys.exit(pip_entry_point(args)) 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 sys
import warnings 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_ROOT = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
PIPENV_VENDOR = os.sep.join([PIPENV_ROOT, "vendor"]) PIPENV_VENDOR = os.sep.join([PIPENV_ROOT, "vendor"])
@@ -54,8 +53,8 @@ if os.name == "nt":
sys.stdout = stdout sys.stdout = stdout
sys.stderr = stderr sys.stderr = stderr
from .cli import cli
from . import resolver # noqa from . import resolver # noqa
from .cli import cli
if __name__ == "__main__": if __name__ == "__main__":
cli() cli()
-1
View File
@@ -1,5 +1,4 @@
from pipenv.cli import cli from pipenv.cli import cli
if __name__ == "__main__": if __name__ == "__main__":
cli() cli()
+10 -3
View File
@@ -12,13 +12,19 @@ warnings.filterwarnings("ignore", category=ResourceWarning)
__all__ = [ __all__ = [
"getpreferredencoding", "DEFAULT_ENCODING", "canonical_encoding_name", "getpreferredencoding",
"force_encoding", "UNICODE_TO_ASCII_TRANSLATION_MAP", "decode_output", "fix_utf8" "DEFAULT_ENCODING",
"canonical_encoding_name",
"force_encoding",
"UNICODE_TO_ASCII_TRANSLATION_MAP",
"decode_output",
"fix_utf8",
] ]
def getpreferredencoding(): def getpreferredencoding():
import locale import locale
# Borrowed from Invoke # Borrowed from Invoke
# (see https://github.com/pyinvoke/invoke/blob/93af29d/invoke/runners.py#L881) # (see https://github.com/pyinvoke/invoke/blob/93af29d/invoke/runners.py#L881)
return locale.getpreferredencoding(False) return locale.getpreferredencoding(False)
@@ -29,6 +35,7 @@ DEFAULT_ENCODING = getpreferredencoding()
def canonical_encoding_name(name): def canonical_encoding_name(name):
import codecs import codecs
try: try:
codec = codecs.lookup(name) codec = codecs.lookup(name)
except LookupError: except LookupError:
@@ -55,7 +62,7 @@ def force_encoding():
if stdout_encoding != "utf-8" or stderr_encoding != "utf-8": if stdout_encoding != "utf-8" or stderr_encoding != "utf-8":
try: try:
from ctypes import pythonapi, py_object, c_char_p from ctypes import c_char_p, py_object, pythonapi
except ImportError: except ImportError:
return DEFAULT_ENCODING, DEFAULT_ENCODING return DEFAULT_ENCODING, DEFAULT_ENCODING
try: 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.__version__ import __version__
from pipenv._compat import fix_utf8 from pipenv._compat import fix_utf8
from pipenv.cli.options import ( from pipenv.cli.options import (
CONTEXT_SETTINGS, PipenvGroup, common_options, deploy_option, CONTEXT_SETTINGS,
general_options, install_options, lock_options, pass_state, PipenvGroup,
pypi_mirror_option, python_option, site_packages_option, skip_lock_option, common_options,
sync_options, system_option, three_option, uninstall_options, verbose_option 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.exceptions import PipenvOptionsError
from pipenv.patched import crayons from pipenv.patched import crayons
from pipenv.utils.processes import subprocess_run from pipenv.utils.processes import subprocess_run
from pipenv.vendor.click import ( from pipenv.vendor.click import (
Choice, argument, echo, edit, group, option, pass_context, secho, types, Choice,
version_option argument,
echo,
edit,
group,
option,
pass_context,
secho,
version_option,
) )
subcommand_context = CONTEXT_SETTINGS.copy() subcommand_context = CONTEXT_SETTINGS.copy()
subcommand_context.update({ subcommand_context.update({"ignore_unknown_options": True, "allow_extra_args": True})
"ignore_unknown_options": True,
"allow_extra_args": True
})
subcommand_context_no_interspersion = subcommand_context.copy() subcommand_context_no_interspersion = subcommand_context.copy()
subcommand_context_no_interspersion["allow_interspersed_args"] = False 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) @group(cls=PipenvGroup, invoke_without_command=True, context_settings=CONTEXT_SETTINGS)
@option("--where", is_flag=True, default=False, help="Output project home information.") @option("--where", is_flag=True, default=False, help="Output project home information.")
@option("--venv", is_flag=True, default=False, help="Output virtualenv 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(
@option("--envs", is_flag=True, default=False, help="Output Environment Variable options.") "--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("--rm", is_flag=True, default=False, help="Remove the virtualenv.")
@option("--bare", is_flag=True, default=False, help="Minimal output.") @option("--bare", is_flag=True, default=False, help="Minimal output.")
@option("--man", is_flag=True, default=False, help="Display manpage.") @option("--man", is_flag=True, default=False, help="Display manpage.")
@@ -58,21 +78,33 @@ def cli(
support=None, support=None,
help=False, help=False,
site_packages=None, 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 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 man:
if system_which("man"): if system_which("man"):
path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "pipenv.1") path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "pipenv.1")
os.execle(system_which("man"), "man", path, os.environ) os.execle(system_which("man"), "man", path, os.environ)
return 0 return 0
else: 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 return 1
if envs: if envs:
echo("The following environment variables can be set, to do various things:\n") echo("The following environment variables can be set, to do various things:\n")
@@ -114,7 +146,7 @@ def cli(
"{}({}){}".format( "{}({}){}".format(
crayons.red("No virtualenv has been created for this project"), crayons.red("No virtualenv has been created for this project"),
crayons.normal(state.project.project_directory, bold=True), crayons.normal(state.project.project_directory, bold=True),
crayons.red(" yet!") crayons.red(" yet!"),
), ),
err=True, err=True,
) )
@@ -183,10 +215,7 @@ def cli(
@skip_lock_option @skip_lock_option
@install_options @install_options
@pass_state @pass_state
def install( def install(state, **kwargs):
state,
**kwargs
):
"""Installs provided packages and adds them to Pipfile, or (if no packages are given), installs all packages from Pipfile.""" """Installs provided packages and adds them to Pipfile, or (if no packages are given), installs all packages from Pipfile."""
from ..core import do_install from ..core import do_install
@@ -210,13 +239,13 @@ def install(
extra_index_url=state.extra_index_urls, extra_index_url=state.extra_index_urls,
packages=state.installstate.packages, packages=state.installstate.packages,
editable_packages=state.installstate.editables, editable_packages=state.installstate.editables,
site_packages=state.site_packages site_packages=state.site_packages,
) )
@cli.command( @cli.command(
short_help="Uninstalls a provided package and removes it from Pipfile.", short_help="Uninstalls a provided package and removes it from Pipfile.",
context_settings=subcommand_context context_settings=subcommand_context,
) )
@option( @option(
"--all-dev", "--all-dev",
@@ -233,15 +262,10 @@ def install(
@uninstall_options @uninstall_options
@pass_state @pass_state
@pass_context @pass_context
def uninstall( def uninstall(ctx, state, all_dev=False, all=False, **kwargs):
ctx,
state,
all_dev=False,
all=False,
**kwargs
):
"""Uninstalls a provided package and removes it from Pipfile.""" """Uninstalls a provided package and removes it from Pipfile."""
from ..core import do_uninstall from ..core import do_uninstall
retcode = do_uninstall( retcode = do_uninstall(
state.project, state.project,
packages=state.installstate.packages, packages=state.installstate.packages,
@@ -254,7 +278,7 @@ def uninstall(
all=all, all=all,
keep_outdated=state.installstate.keep_outdated, keep_outdated=state.installstate.keep_outdated,
pypi_mirror=state.pypi_mirror, pypi_mirror=state.pypi_mirror,
ctx=ctx ctx=ctx,
) )
if retcode: if retcode:
sys.exit(retcode) sys.exit(retcode)
@@ -280,11 +304,7 @@ LOCK_DEV_NOTE = """\
@lock_options @lock_options
@pass_state @pass_state
@pass_context @pass_context
def lock( def lock(ctx, state, **kwargs):
ctx,
state,
**kwargs
):
"""Generates Pipfile.lock.""" """Generates Pipfile.lock."""
from ..core import do_init, do_lock, ensure_project 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 # Note that we don't pass clear on to ensure_project as it is also
# handled in do_lock # handled in do_lock
ensure_project( ensure_project(
state.project, three=state.three, python=state.python, pypi_mirror=state.pypi_mirror, state.project,
warn=(not state.quiet), site_packages=state.site_packages, 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 emit_requirements = state.lockoptions.emit_requirements
dev = state.installstate.dev dev = state.installstate.dev
@@ -325,7 +349,7 @@ def lock(
raise PipenvOptionsError( raise PipenvOptionsError(
"--dev-only", "--dev-only",
"--dev-only is only permitted in combination with --requirements. " "--dev-only is only permitted in combination with --requirements. "
"Aborting." "Aborting.",
) )
do_lock( do_lock(
state.project, state.project,
@@ -347,7 +371,7 @@ def lock(
is_flag=True, is_flag=True,
default=False, default=False,
help="Run in shell in fancy mode. Make sure the shell have no path manipulating" 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( @option(
"--anyway", "--anyway",
@@ -409,6 +433,7 @@ def shell(
def run(state, command, args): def run(state, command, args):
"""Spawns a command installed into the virtualenv.""" """Spawns a command installed into the virtualenv."""
from ..core import do_run from ..core import do_run
do_run( do_run(
state.project, state.project,
command=command, command=command,
@@ -416,21 +441,21 @@ def run(state, command, args):
three=state.three, three=state.three,
python=state.python, python=state.python,
pypi_mirror=state.pypi_mirror, pypi_mirror=state.pypi_mirror,
quiet=state.quiet quiet=state.quiet,
) )
@cli.command( @cli.command(
short_help="Checks for PyUp Safety security vulnerabilities and against" short_help="Checks for PyUp Safety security vulnerabilities and against"
" PEP 508 markers provided in Pipfile.", " PEP 508 markers provided in Pipfile.",
context_settings=subcommand_context context_settings=subcommand_context,
) )
@option( @option(
"--db", "--db",
nargs=1, 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." help="Path to a local PyUp Safety vulnerabilities database."
" Default: ENV PIPENV_SAFETY_DB or None.", " Default: ENV PIPENV_SAFETY_DB or None.",
) )
@option( @option(
"--ignore", "--ignore",
@@ -447,8 +472,11 @@ def run(state, command, args):
@option( @option(
"--key", "--key",
help="Safety API key from PyUp.io for scanning dependencies against a live" help="Safety API key from PyUp.io for scanning dependencies against a live"
" vulnerabilities database. Leave blank for scanning against a" " vulnerabilities database. Leave blank for scanning against a"
" database that only updates once a month.", " database that only updates once a month.",
)
@option(
"--quiet", is_flag=True, help="Quiet standard output, except vulnerability report."
) )
@common_options @common_options
@system_option @system_option
@@ -461,7 +489,7 @@ def check(
output="default", output="default",
key=None, key=None,
quiet=False, quiet=False,
**kwargs **kwargs,
): ):
"""Checks for PyUp Safety security vulnerabilities and against PEP 508 markers provided in Pipfile.""" """Checks for PyUp Safety security vulnerabilities and against PEP 508 markers provided in Pipfile."""
from ..core import do_check from ..core import do_check
@@ -482,31 +510,33 @@ def check(
@cli.command(short_help="Runs lock, then sync.", context_settings=CONTEXT_SETTINGS) @cli.command(short_help="Runs lock, then sync.", context_settings=CONTEXT_SETTINGS)
@option("--bare", is_flag=True, default=False, help="Minimal output.") @option("--bare", is_flag=True, default=False, help="Minimal output.")
@option( @option("--outdated", is_flag=True, default=False, help="List out-of-date dependencies.")
"--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.") @option("--dry-run", is_flag=True, default=None, help="List out-of-date dependencies.")
@install_options @install_options
@pass_state @pass_state
@pass_context @pass_context
def update( def update(ctx, state, bare=False, dry_run=None, outdated=False, **kwargs):
ctx,
state,
bare=False,
dry_run=None,
outdated=False,
**kwargs
):
"""Runs lock, then sync.""" """Runs lock, then sync."""
from ..core import do_lock, do_outdated, do_sync, ensure_project from ..core import do_lock, do_outdated, do_sync, ensure_project
ensure_project( ensure_project(
state.project, three=state.three, python=state.python, pypi_mirror=state.pypi_mirror, state.project,
warn=(not state.quiet), site_packages=state.site_packages, clear=state.clear 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: if not outdated:
outdated = bool(dry_run) outdated = bool(dry_run)
if outdated: 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] packages = [p for p in state.installstate.packages if p]
editable = [p for p in state.installstate.editables if p] editable = [p for p in state.installstate.editables if p]
if not packages: if not packages:
@@ -557,7 +587,7 @@ def update(
@cli.command( @cli.command(
short_help="Displays currently-installed dependency graph information.", 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("--bare", is_flag=True, default=False, help="Minimal output.")
@option("--json", is_flag=True, default=False, help="Output JSON.") @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( @cli.command(
short_help="View a given module in your editor.", name="open", short_help="View a given module in your editor.",
context_settings=CONTEXT_SETTINGS name="open",
context_settings=CONTEXT_SETTINGS,
) )
@common_options @common_options
@argument("module", nargs=1) @argument("module", nargs=1)
@@ -590,11 +621,18 @@ def run_open(state, module, *args, **kwargs):
# Ensure that virtualenv is available. # Ensure that virtualenv is available.
ensure_project( ensure_project(
state.project, three=state.three, python=state.python, state.project,
validate=False, pypi_mirror=state.pypi_mirror, three=state.three,
python=state.python,
validate=False,
pypi_mirror=state.pypi_mirror,
) )
c = subprocess_run( 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: if c.returncode:
echo(crayons.red("Module not found!")) echo(crayons.red("Module not found!"))
@@ -611,21 +649,14 @@ def run_open(state, module, *args, **kwargs):
@cli.command( @cli.command(
short_help="Installs all packages specified in Pipfile.lock.", short_help="Installs all packages specified in Pipfile.lock.",
context_settings=CONTEXT_SETTINGS context_settings=CONTEXT_SETTINGS,
) )
@system_option @system_option
@option("--bare", is_flag=True, default=False, help="Minimal output.") @option("--bare", is_flag=True, default=False, help="Minimal output.")
@sync_options @sync_options
@pass_state @pass_state
@pass_context @pass_context
def sync( def sync(ctx, state, bare=False, user=False, unused=False, **kwargs):
ctx,
state,
bare=False,
user=False,
unused=False,
**kwargs
):
"""Installs all packages specified in Pipfile.lock.""" """Installs all packages specified in Pipfile.lock."""
from ..core import do_sync from ..core import do_sync
@@ -641,7 +672,7 @@ def sync(
unused=unused, unused=unused,
sequential=state.installstate.sequential, sequential=state.installstate.sequential,
pypi_mirror=state.pypi_mirror, pypi_mirror=state.pypi_mirror,
system=state.system system=state.system,
) )
if retcode: if retcode:
ctx.abort() ctx.abort()
@@ -649,7 +680,7 @@ def sync(
@cli.command( @cli.command(
short_help="Uninstalls all packages not specified in Pipfile.lock.", 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("--bare", is_flag=True, default=False, help="Minimal output.")
@option("--dry-run", is_flag=True, default=False, help="Just output unneeded packages.") @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): def clean(state, dry_run=False, bare=False, user=False):
"""Uninstalls all packages not specified in Pipfile.lock.""" """Uninstalls all packages not specified in Pipfile.lock."""
from ..core import do_clean 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( @cli.command(
@@ -675,7 +712,7 @@ def scripts(state):
if not state.project.pipfile_exists: if not state.project.pipfile_exists:
echo("No Pipfile present at project home.", err=True) echo("No Pipfile present at project home.", err=True)
sys.exit(1) 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)) 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())) second_column_width = max(len(word) for word in ["Script"] + list(scripts.values()))
lines = ["{0:<{width}} Script".format("Command", width=first_column_width)] lines = ["{0:<{width}} Script".format("Command", width=first_column_width)]
@@ -699,13 +736,13 @@ def verify(state):
sys.exit(1) sys.exit(1)
if state.project.get_lockfile_hash() != state.project.calculate_pipfile_hash(): if state.project.get_lockfile_hash() != state.project.calculate_pipfile_hash():
echo( 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) crayons.yellow("$ pipenv lock", bold=True)
), ),
err=True err=True,
) )
sys.exit(1) sys.exit(1)
echo(crayons.green('Pipfile.lock is up-to-date.')) echo(crayons.green("Pipfile.lock is up-to-date."))
sys.exit(0) sys.exit(0)
+258 -78
View File
@@ -3,17 +3,19 @@ import os
from pipenv.project import Project from pipenv.project import Project
from pipenv.utils.internet import is_valid_url from pipenv.utils.internet import is_valid_url
from pipenv.vendor.click import ( from pipenv.vendor.click import (
BadArgumentUsage, BadParameter, Group, Option, argument, echo, BadArgumentUsage,
make_pass_decorator, option BadParameter,
Group,
Option,
argument,
echo,
make_pass_decorator,
option,
) )
from pipenv.vendor.click import types as click_types from pipenv.vendor.click import types as click_types
from pipenv.vendor.click_didyoumean import DYMMixin 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): class PipenvGroup(DYMMixin, Group):
@@ -53,6 +55,7 @@ class PipenvGroup(DYMMixin, Group):
""" """
return super().main(*args, **kwargs, windows_expand_args=False) return super().main(*args, **kwargs, windows_expand_args=False)
class State: class State:
def __init__(self): def __init__(self):
self.index = None self.index = None
@@ -102,9 +105,16 @@ def index_option(f):
state = ctx.ensure_object(State) state = ctx.ensure_object(State)
state.index = value state.index = value
return value return value
return option('-i', '--index', expose_value=False, envvar="PIP_INDEX_URL",
help='Target PyPI-compatible package index url.', nargs=1, return option(
callback=callback)(f) "-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): def extra_index_option(f):
@@ -112,9 +122,15 @@ def extra_index_option(f):
state = ctx.ensure_object(State) state = ctx.ensure_object(State)
state.extra_index_urls.extend(list(value)) state.extra_index_urls.extend(list(value))
return 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.", return option(
callback=callback, envvar="PIP_EXTRA_INDEX_URL")(f) "--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): def editable_option(f):
@@ -122,11 +138,16 @@ def editable_option(f):
state = ctx.ensure_object(State) state = ctx.ensure_object(State)
state.installstate.editables.extend(value) state.installstate.editables.extend(value)
return value return value
return option('-e', '--editable', expose_value=False, multiple=True,
callback=callback, type=click_types.STRING, help=( return option(
"An editable Python package URL or path, often to a VCS " "-e",
"repository." "--editable",
))(f) 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): def sequential_option(f):
@@ -134,9 +155,17 @@ def sequential_option(f):
state = ctx.ensure_object(State) state = ctx.ensure_object(State)
state.installstate.sequential = value state.installstate.sequential = value
return value return value
return option("--sequential", is_flag=True, default=False, expose_value=False,
help="Install dependencies one-at-a-time, instead of concurrently.", return option(
callback=callback, type=click_types.BOOL, show_envvar=True)(f) "--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): def skip_lock_option(f):
@@ -144,10 +173,18 @@ def skip_lock_option(f):
state = ctx.ensure_object(State) state = ctx.ensure_object(State)
state.installstate.skip_lock = value state.installstate.skip_lock = value
return 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.", return option(
envvar="PIPENV_SKIP_LOCK", callback=callback, type=click_types.BOOL, "--skip-lock",
show_envvar=True)(f) 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): def keep_outdated_option(f):
@@ -155,9 +192,17 @@ def keep_outdated_option(f):
state = ctx.ensure_object(State) state = ctx.ensure_object(State)
state.installstate.keep_outdated = value state.installstate.keep_outdated = value
return 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.", return option(
callback=callback, type=click_types.BOOL, show_envvar=True)(f) "--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): def selective_upgrade_option(f):
@@ -165,9 +210,16 @@ def selective_upgrade_option(f):
state = ctx.ensure_object(State) state = ctx.ensure_object(State)
state.installstate.selective_upgrade = value state.installstate.selective_upgrade = value
return value return value
return option("--selective-upgrade", is_flag=True, default=False, type=click_types.BOOL,
help="Update specified packages.", callback=callback, return option(
expose_value=False)(f) "--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): def ignore_pipfile_option(f):
@@ -175,9 +227,17 @@ def ignore_pipfile_option(f):
state = ctx.ensure_object(State) state = ctx.ensure_object(State)
state.installstate.ignore_pipfile = value state.installstate.ignore_pipfile = value
return value return value
return option("--ignore-pipfile", is_flag=True, default=False, expose_value=False,
help="Ignore Pipfile when installing, using the Pipfile.lock.", return option(
callback=callback, type=click_types.BOOL, show_envvar=True)(f) "--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): def _dev_option(f, help_text):
@@ -185,9 +245,18 @@ def _dev_option(f, help_text):
state = ctx.ensure_object(State) state = ctx.ensure_object(State)
state.installstate.dev = value state.installstate.dev = value
return value return value
return option("--dev", "-d", is_flag=True, default=False, type=click_types.BOOL,
help=help_text, callback=callback, return option(
expose_value=False, show_envvar=True)(f) "--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): def install_dev_option(f):
@@ -199,7 +268,9 @@ def lock_dev_option(f):
def uninstall_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): def pre_option(f):
@@ -207,8 +278,16 @@ def pre_option(f):
state = ctx.ensure_object(State) state = ctx.ensure_object(State)
state.installstate.pre = value state.installstate.pre = value
return 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): def package_arg(f):
@@ -216,8 +295,14 @@ def package_arg(f):
state = ctx.ensure_object(State) state = ctx.ensure_object(State)
state.installstate.packages.extend(value) state.installstate.packages.extend(value)
return 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): def three_option(f):
@@ -226,9 +311,15 @@ def three_option(f):
if value is not None: if value is not None:
state.three = value state.three = value
return value return value
return option("--three", is_flag=True, default=None,
help="Use Python 3/2 when creating virtualenv.", callback=callback, return option(
expose_value=False)(f) "--three",
is_flag=True,
default=None,
help="Use Python 3 when creating virtualenv.",
callback=callback,
expose_value=False,
)(f)
def python_option(f): def python_option(f):
@@ -237,10 +328,17 @@ def python_option(f):
if value is not None: if value is not None:
state.python = validate_python_path(ctx, param, value) state.python = validate_python_path(ctx, param, value)
return value return value
return option("--python", default="", nargs=1, callback=callback,
help="Specify which version of Python virtualenv should use.", return option(
expose_value=False, allow_from_autoenv=False, "--python",
type=click_types.STRING)(f) 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): def pypi_mirror_option(f):
@@ -250,8 +348,14 @@ def pypi_mirror_option(f):
if value is not None: if value is not None:
state.pypi_mirror = validate_pypi_mirror(ctx, param, value) state.pypi_mirror = validate_pypi_mirror(ctx, param, value)
return 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): def verbose_option(f):
@@ -261,12 +365,20 @@ def verbose_option(f):
if state.quiet: if state.quiet:
raise BadArgumentUsage( raise BadArgumentUsage(
"--verbose and --quiet are mutually exclusive! Please choose one!", "--verbose and --quiet are mutually exclusive! Please choose one!",
ctx=ctx ctx=ctx,
) )
state.verbose = True state.verbose = True
setup_verbosity(ctx, param, 1) 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): def quiet_option(f):
@@ -276,12 +388,20 @@ def quiet_option(f):
if state.verbose: if state.verbose:
raise BadArgumentUsage( raise BadArgumentUsage(
"--verbose and --quiet are mutually exclusive! Please choose one!", "--verbose and --quiet are mutually exclusive! Please choose one!",
ctx=ctx ctx=ctx,
) )
state.quiet = True state.quiet = True
setup_verbosity(ctx, param, -1) 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): def site_packages_option(f):
@@ -290,9 +410,16 @@ def site_packages_option(f):
validate_bool_or_none(ctx, param, value) validate_bool_or_none(ctx, param, value)
state.site_packages = value state.site_packages = value
return value return value
return option("--site-packages/--no-site-packages", is_flag=True, default=None,
help="Enable site-packages for the virtualenv.", callback=callback, return option(
expose_value=False, show_envvar=True)(f) "--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): def clear_option(f):
@@ -300,9 +427,16 @@ def clear_option(f):
state = ctx.ensure_object(State) state = ctx.ensure_object(State)
state.clear = value state.clear = value
return value return value
return option("--clear", is_flag=True, callback=callback, type=click_types.BOOL,
help="Clears caches (pipenv, pip).", return option(
expose_value=False, show_envvar=True)(f) "--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): def system_option(f):
@@ -311,9 +445,17 @@ def system_option(f):
if value is not None: if value is not None:
state.system = value state.system = value
return value return value
return option("--system", is_flag=True, default=False, help="System pip management.",
callback=callback, type=click_types.BOOL, expose_value=False, return option(
show_envvar=True)(f) "--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): def requirementstxt_option(f):
@@ -322,9 +464,17 @@ def requirementstxt_option(f):
if value: if value:
state.installstate.requirementstxt = value state.installstate.requirementstxt = value
return value return value
return option("--requirements", "-r", nargs=1, default="", expose_value=False,
help="Import a requirements.txt file.", callback=callback, return option(
type=click_types.STRING)(f) "--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): def emit_requirements_flag(f):
@@ -333,8 +483,16 @@ def emit_requirements_flag(f):
if value: if value:
state.lockoptions.emit_requirements = value state.lockoptions.emit_requirements = value
return 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): def emit_requirements_header_flag(f):
@@ -343,8 +501,15 @@ def emit_requirements_header_flag(f):
if value: if value:
state.lockoptions.emit_requirements_header = value state.lockoptions.emit_requirements_header = value
return 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): def dev_only_flag(f):
@@ -353,8 +518,15 @@ def dev_only_flag(f):
if value: if value:
state.lockoptions.dev_only = value state.lockoptions.dev_only = value
return 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): def deploy_option(f):
@@ -362,15 +534,23 @@ def deploy_option(f):
state = ctx.ensure_object(State) state = ctx.ensure_object(State)
state.installstate.deploy = value state.installstate.deploy = value
return 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" return option(
" wrong.", callback=callback, expose_value=False)(f) "--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): def setup_verbosity(ctx, param, value):
if not value: if not value:
return return
import logging import logging
loggers = ("pip",) loggers = ("pip",)
if value == 1: if value == 1:
for logger in loggers: 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 See also: https://docs.python.org/3/library/subprocess.html#converting-argument-sequence
""" """
return " ".join(itertools.chain( return " ".join(
[_quote_if_contains(self.command, r'[\s^()]')], itertools.chain(
(_quote_if_contains(arg, r'[\s^]') for arg in self.args), [_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 pkg_resources
import pipenv import pipenv
from pipenv.environments import is_type_checking 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.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 import vistir
from pipenv.vendor.cached_property import cached_property from pipenv.vendor.cached_property import cached_property
from pipenv.vendor.packaging.utils import canonicalize_name from pipenv.vendor.packaging.utils import canonicalize_name
if is_type_checking(): if is_type_checking():
from types import ModuleType from types import ModuleType
from typing import ( from typing import ContextManager, Dict, Generator, List, Optional, Set, Union
ContextManager, Dict, Generator, List, Optional, Set, Union
)
import pip_shims.shims import pip_shims.shims
import tomlkit import tomlkit
@@ -47,10 +43,10 @@ class Environment:
base_working_set=None, # type: pkg_resources.WorkingSet base_working_set=None, # type: pkg_resources.WorkingSet
pipfile=None, # type: Optional[Union[tomlkit.toml_document.TOMLDocument, TPipfile]] pipfile=None, # type: Optional[Union[tomlkit.toml_document.TOMLDocument, TPipfile]]
sources=None, # type: Optional[List[TSource]] sources=None, # type: Optional[List[TSource]]
project=None # type: Optional[Project] project=None, # type: Optional[Project]
): ):
super().__init__() 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 self.base_working_set = base_working_set if base_working_set else BASE_WORKING_SET
prefix = normalize_path(prefix) prefix = normalize_path(prefix)
self._python = None self._python = None
@@ -82,9 +78,10 @@ class Environment:
self._modules[name] = importlib.import_module(name) self._modules[name] = importlib.import_module(name)
module = self._modules[name] module = self._modules[name]
if not module: if not module:
dist = next(iter( dist = next(
dist for dist in self.base_working_set if dist.project_name == name iter(dist for dist in self.base_working_set if dist.project_name == name),
), None) None,
)
if dist: if dist:
dist.activate() dist.activate()
module = importlib.import_module(name) module = importlib.import_module(name)
@@ -152,7 +149,9 @@ class Environment:
if not os.path.exists(include_dir): if not os.path.exists(include_dir):
include_dirs = self.get_include_path() include_dirs = self.get_include_path()
if include_dirs: 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: if not include_path:
return {} return {}
include_dir = Path(include_path) include_dir = Path(include_path)
@@ -169,7 +168,8 @@ class Environment:
base, leaf = os.path.split(path) base, leaf = os.path.split(path)
base, parent = os.path.split(base) base, parent = os.path.split(base)
leaf = os.path.join(parent, leaf).replace( 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 os.path.join(base, leaf)
return path return path
@@ -213,16 +213,21 @@ class Environment:
try: try:
paths = self.get_paths() paths = self.get_paths()
except Exception: except Exception:
install_scheme = 'nt' if (os.name == 'nt') else 'posix_prefix' install_scheme = "nt" if (os.name == "nt") else "posix_prefix"
paths = get_paths(install_scheme, vars={ paths = get_paths(
'base': prefix, install_scheme,
'platbase': prefix, vars={
}) "base": prefix,
"platbase": prefix,
},
)
current_version = get_python_version() current_version = get_python_version()
try: try:
for k in list(paths.keys()): for k in list(paths.keys()):
if not os.path.exists(paths[k]): 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: except OSError:
# Sometimes virtualenvs are made using virtualenv interpreters and there is no # Sometimes virtualenvs are made using virtualenv interpreters and there is no
# include directory, which will cause this approach to fail. This failsafe # include directory, which will cause this approach to fail. This failsafe
@@ -231,11 +236,14 @@ class Environment:
paths.update(self.get_lib_paths()) paths.update(self.get_lib_paths())
paths["scripts"] = self.script_basedir paths["scripts"] = self.script_basedir
if not paths: if not paths:
install_scheme = 'nt' if (os.name == 'nt') else 'posix_prefix' install_scheme = "nt" if (os.name == "nt") else "posix_prefix"
paths = get_paths(install_scheme, vars={ paths = get_paths(
'base': prefix, install_scheme,
'platbase': prefix, vars={
}) "base": prefix,
"platbase": prefix,
},
)
if not os.path.exists(paths["purelib"]) and not os.path.exists(paths["platlib"]): if not os.path.exists(paths["purelib"]) and not os.path.exists(paths["platlib"]):
lib_paths = self.get_lib_paths() lib_paths = self.get_lib_paths()
paths.update(lib_paths) paths.update(lib_paths)
@@ -249,7 +257,7 @@ class Environment:
else: else:
lib_dirs = purelib + os.pathsep + platlib lib_dirs = purelib + os.pathsep + platlib
paths["libdir"] = purelib paths["libdir"] = purelib
paths['PYTHONPATH'] = os.pathsep.join(["", ".", lib_dirs]) paths["PYTHONPATH"] = os.pathsep.join(["", ".", lib_dirs])
paths["libdirs"] = lib_dirs paths["libdirs"] = lib_dirs
return paths return paths
@@ -258,11 +266,14 @@ class Environment:
# type: () -> str # type: () -> str
"""Path to the environment scripts dir""" """Path to the environment scripts dir"""
prefix = make_posix(self.prefix.as_posix()) prefix = make_posix(self.prefix.as_posix())
install_scheme = 'nt' if (os.name == 'nt') else 'posix_prefix' install_scheme = "nt" if (os.name == "nt") else "posix_prefix"
paths = get_paths(install_scheme, vars={ paths = get_paths(
'base': prefix, install_scheme,
'platbase': prefix, vars={
}) "base": prefix,
"platbase": prefix,
},
)
return paths["scripts"] return paths["scripts"]
@property @property
@@ -291,20 +302,30 @@ class Environment:
""" """
from .vendor.vistir.compat import JSONDecodeError from .vendor.vistir.compat import JSONDecodeError
current_executable = Path(sys.executable).as_posix() current_executable = Path(sys.executable).as_posix()
if not self.python or self.python == current_executable: if not self.python or self.python == current_executable:
return sys.path return sys.path
elif any([sys.prefix == self.prefix, not self.is_venv]): elif any([sys.prefix == self.prefix, not self.is_venv]):
return sys.path return sys.path
cmd_args = [self.python, "-c", "import json, sys; print(json.dumps(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: try:
path = json.loads(path.strip()) path = json.loads(path.strip())
except JSONDecodeError: except JSONDecodeError:
path = sys.path path = sys.path
return 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 # type: (bool, bool, bool, bool) -> str
"""Build the text for running a command in the given environment """Build the text for running a command in the given environment
@@ -332,16 +353,26 @@ class Environment:
dist_prefix = f"{key}lib" dist_prefix = f"{key}lib"
# XXX: We need to get 'stdlib' or 'platstdlib' # XXX: We need to get 'stdlib' or 'platstdlib'
sys_prefix = "{}stdlib".format("" if key == "pure" else key) 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(
pylib_lines.append(f"u'{sys_prefix}': u'{{{{0}}}}'.format({sysconfig_line.format(sys_prefix)})") 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: if python_inc:
for key, var, val in (("include", "inc", "0"), ("platinclude", "inc", "1")): 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 lines = pylib_lines + pyinc_lines
if scripts: 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: 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) lines_as_str = ",".join(lines)
py_command = py_command % lines_as_str py_command = py_command % lines_as_str
return py_command return py_command
@@ -357,7 +388,9 @@ class Environment:
tmpfile = vistir.path.create_tracked_tempfile(suffix=".json") tmpfile = vistir.path.create_tracked_tempfile(suffix=".json")
tmpfile.close() tmpfile.close()
tmpfile_path = make_posix(tmpfile.name) 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)] command = [self.python, "-c", py_command.format(tmpfile_path)]
c = subprocess_run(command) c = subprocess_run(command)
if c.returncode == 0: if c.returncode == 0:
@@ -366,7 +399,14 @@ class Environment:
paths = json.load(fh) paths = json.load(fh)
if "purelib" in paths: if "purelib" in paths:
paths["libdir"] = paths["purelib"] = make_posix(paths["purelib"]) 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: if key in paths:
paths[key] = make_posix(paths[key]) paths[key] = make_posix(paths[key])
return paths return paths
@@ -405,16 +445,27 @@ class Environment:
if not paths: if not paths:
if not self.prefix.joinpath("lib").exists(): if not self.prefix.joinpath("lib").exists():
return {} return {}
stdlib_path = next(iter([ stdlib_path = next(
p for p in self.prefix.joinpath("lib").iterdir() iter(
if p.name.startswith("python") [
]), None) p
for p in self.prefix.joinpath("lib").iterdir()
if p.name.startswith("python")
]
),
None,
)
lib_path = None lib_path = None
if stdlib_path: if stdlib_path:
lib_path = next(iter([ lib_path = next(
p.as_posix() for p in stdlib_path.iterdir() iter(
if p.name == "site-packages" [
])) p.as_posix()
for p in stdlib_path.iterdir()
if p.name == "site-packages"
]
)
)
paths = {"stdlib": stdlib_path.as_posix()} paths = {"stdlib": stdlib_path.as_posix()}
if lib_path: if lib_path:
paths["purelib"] = lib_path paths["purelib"] = lib_path
@@ -503,9 +554,10 @@ class Environment:
when installing. when installing.
""" """
from .vendor.packaging.version import parse as parse_version from .vendor.packaging.version import parse as parse_version
pip = next(iter(
pkg for pkg in self.get_installed_packages() if pkg.key == "pip" pip = next(
), None) iter(pkg for pkg in self.get_installed_packages() if pkg.key == "pip"), None
)
if pip is not None: if pip is not None:
return parse_version(pip.version) return parse_version(pip.version)
return parse_version("20.2") return parse_version("20.2")
@@ -542,8 +594,12 @@ class Environment:
:rtype: iterator :rtype: iterator
""" """
pip_target_dir = os.environ.get('PIP_TARGET') pip_target_dir = os.environ.get("PIP_TARGET")
libdirs = [pip_target_dir] if pip_target_dir else self.base_paths["libdirs"].split(os.pathsep) 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) dists = (pkg_resources.find_distributions(libdir) for libdir in libdirs)
yield from itertools.chain.from_iterable(dists) yield from itertools.chain.from_iterable(dists)
@@ -575,8 +631,10 @@ class Environment:
# type: (pkg_resources.Distribution) -> bool # type: (pkg_resources.Distribution) -> bool
"""Determine whether the supplied distribution is in the environment.""" """Determine whether the supplied distribution is in the environment."""
from .environments import normalize_pipfile_path as _normalized from .environments import normalize_pipfile_path as _normalized
prefixes = [ 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())) if _normalized(prefix).startswith(_normalized(self.prefix.as_posix()))
] ]
location = self.locate_dist(dist) location = self.locate_dist(dist)
@@ -590,7 +648,8 @@ class Environment:
"""Returns all of the installed packages in a given environment""" """Returns all of the installed packages in a given environment"""
workingset = self.get_working_set() workingset = self.get_working_set()
packages = [ packages = [
pkg for pkg in workingset pkg
for pkg in workingset
if self.dist_is_in_project(pkg) and pkg.key != "python" if self.dist_is_in_project(pkg) and pkg.key != "python"
] ]
return packages return packages
@@ -606,20 +665,23 @@ class Environment:
pip_options.cache_dir = self.project.s.PIPENV_CACHE_DIR pip_options.cache_dir = self.project.s.PIPENV_CACHE_DIR
pip_options.pre = self.pipfile.get("pre", pre) pip_options.pre = self.pipfile.get("pre", pre)
with pip_command._build_session(pip_options) as session: 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 yield finder
def get_package_info(self, pre=False): def get_package_info(self, pre=False):
# type: (bool) -> Generator[pkg_resources.Distribution, None, None] # type: (bool) -> Generator[pkg_resources.Distribution, None, None]
from .vendor.pip_shims.shims import parse_version, pip_version from .vendor.pip_shims.shims import parse_version, pip_version
dependency_links = [] dependency_links = []
packages = self.get_installed_packages() packages = self.get_installed_packages()
# This code is borrowed from pip's current implementation # This code is borrowed from pip's current implementation
if parse_version(pip_version) < parse_version("19.0"): if parse_version(pip_version) < parse_version("19.0"):
for dist in packages: for dist in packages:
if dist.has_metadata('dependency_links.txt'): if dist.has_metadata("dependency_links.txt"):
dependency_links.extend( dependency_links.extend(
dist.get_metadata_lines('dependency_links.txt') dist.get_metadata_lines("dependency_links.txt")
) )
with self.get_finder() as finder: with self.get_finder() as finder:
@@ -627,24 +689,29 @@ class Environment:
finder.add_dependency_links(dependency_links) finder.add_dependency_links(dependency_links)
for dist in packages: for dist in packages:
typ = 'unknown' typ = "unknown"
all_candidates = finder.find_all_candidates(dist.key) all_candidates = finder.find_all_candidates(dist.key)
if not self.pipfile.get("pre", finder.allow_all_prereleases): if not self.pipfile.get("pre", finder.allow_all_prereleases):
# Remove prereleases # Remove prereleases
all_candidates = [ all_candidates = [
candidate for candidate in all_candidates candidate
for candidate in all_candidates
if not candidate.version.is_prerelease if not candidate.version.is_prerelease
] ]
if not all_candidates: if not all_candidates:
continue continue
candidate_evaluator = finder.make_candidate_evaluator(project_name=dist.key) candidate_evaluator = finder.make_candidate_evaluator(
best_candidate_result = candidate_evaluator.compute_best_candidate(all_candidates) project_name=dist.key
)
best_candidate_result = candidate_evaluator.compute_best_candidate(
all_candidates
)
remote_version = best_candidate_result.best_candidate.version remote_version = best_candidate_result.best_candidate.version
if best_candidate_result.best_candidate.link.is_wheel: if best_candidate_result.best_candidate.link.is_wheel:
typ = 'wheel' typ = "wheel"
else: else:
typ = 'sdist' typ = "sdist"
# This is dirty but makes the rest of the code much cleaner # This is dirty but makes the rest of the code much cleaner
dist.latest_version = remote_version dist.latest_version = remote_version
dist.latest_filetype = typ dist.latest_filetype = typ
@@ -653,7 +720,8 @@ class Environment:
def get_outdated_packages(self, pre=False): def get_outdated_packages(self, pre=False):
# type: (bool) -> List[pkg_resources.Distribution] # type: (bool) -> List[pkg_resources.Distribution]
return [ 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 if pkg.latest_version._key > pkg.parsed_version._key
] ]
@@ -664,15 +732,16 @@ class Environment:
d = node.as_dict() d = node.as_dict()
if parent: 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: 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'] = [ d["dependencies"] = [
cls._get_requirements_for_package(c, key_tree, parent=node, cls._get_requirements_for_package(
chain=chain+[c.project_name]) c, key_tree, parent=node, chain=chain + [c.project_name]
)
for c in get_children(node) for c in get_children(node)
if c.project_name not in chain if c.project_name not in chain
] ]
@@ -701,7 +770,7 @@ class Environment:
new_node = { new_node = {
"package_name": node["package_name"], "package_name": node["package_name"],
"installed_version": node["installed_version"], "installed_version": node["installed_version"],
"required_version": node["required_version"] "required_version": node["required_version"],
} }
for dependency in node.get("dependencies", []): for dependency in node.get("dependencies", []):
for dep in cls.reverse_dependency(dependency): for dep in cls.reverse_dependency(dependency):
@@ -712,6 +781,7 @@ class Environment:
def reverse_dependencies(self): def reverse_dependencies(self):
from vistir.misc import chunked, unnest from vistir.misc import chunked, unnest
rdeps = {} rdeps = {}
for req in self.get_package_requirements(): for req in self.get_package_requirements():
for d in self.reverse_dependency(req): for d in self.reverse_dependency(req):
@@ -720,7 +790,7 @@ class Environment:
pkg = { pkg = {
name: { name: {
"installed": d["installed_version"], "installed": d["installed_version"],
"required": d["required_version"] "required": d["required_version"],
} }
} }
parents = tuple(d.get("parent", ())) parents = tuple(d.get("parent", ()))
@@ -735,7 +805,7 @@ class Environment:
entry = rdeps[k] entry = rdeps[k]
if entry.get("parents"): if entry.get("parents"):
rdeps[k]["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 return rdeps
@@ -762,21 +832,26 @@ class Environment:
def is_satisfied(self, req): def is_satisfied(self, req):
match = next( match = next(
iter( iter(
d for d in self.get_distributions() d
for d in self.get_distributions()
if canonicalize_name(d.project_name) == req.normalized_name if canonicalize_name(d.project_name) == req.normalized_name
), None ),
None,
) )
if match is not None: if match is not None:
if req.editable and req.line_instance.is_local and self.find_egg(match): if req.editable and req.line_instance.is_local and self.find_egg(match):
requested_path = req.line_instance.path 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"): elif match.has_metadata("direct_url.json"):
direct_url_metadata = json.loads(match.get_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", "") commit_id = direct_url_metadata.get("vcs_info", {}).get("commit_id", "")
vcs_type = direct_url_metadata.get("vcs_info", {}).get("vcs", "") vcs_type = direct_url_metadata.get("vcs_info", {}).get("vcs", "")
_, pipfile_part = req.as_pipfile().popitem() _, pipfile_part = req.as_pipfile().popitem()
return ( 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] and direct_url_metadata["url"] == pipfile_part[req.vcs]
) )
elif req.is_vcs or req.is_file_or_url: elif req.is_vcs or req.is_file_or_url:
@@ -801,7 +876,13 @@ class Environment:
c = None c = None
with self.activated(): with self.activated():
script = vistir.cmdparse.Script.parse(cmd) 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 return c
def run_py(self, cmd, cwd=os.curdir): def run_py(self, cmd, cwd=os.curdir):
@@ -820,7 +901,13 @@ class Environment:
else: else:
script = vistir.cmdparse.Script.parse([self.python, "-c"] + list(cmd)) script = vistir.cmdparse.Script.parse([self.python, "-c"] + list(cmd))
with self.activated(): 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 return c
def run_activate_this(self): def run_activate_this(self):
@@ -865,30 +952,34 @@ class Environment:
self.add_dist("pip") self.add_dist("pip")
prefix = self.prefix.as_posix() prefix = self.prefix.as_posix()
with vistir.contextmanagers.temp_environ(), vistir.contextmanagers.temp_path(): with vistir.contextmanagers.temp_environ(), vistir.contextmanagers.temp_path():
os.environ["PATH"] = os.pathsep.join([ os.environ["PATH"] = os.pathsep.join(
vistir.compat.fs_str(self.script_basedir), [
vistir.compat.fs_str(self.prefix.as_posix()), vistir.compat.fs_str(self.script_basedir),
os.environ.get("PATH", "") vistir.compat.fs_str(self.prefix.as_posix()),
]) os.environ.get("PATH", ""),
]
)
os.environ["PYTHONIOENCODING"] = vistir.compat.fs_str("utf-8") os.environ["PYTHONIOENCODING"] = vistir.compat.fs_str("utf-8")
os.environ["PYTHONDONTWRITEBYTECODE"] = vistir.compat.fs_str("1") os.environ["PYTHONDONTWRITEBYTECODE"] = vistir.compat.fs_str("1")
if self.is_venv: if self.is_venv:
os.environ["PYTHONPATH"] = self.base_paths["PYTHONPATH"] os.environ["PYTHONPATH"] = self.base_paths["PYTHONPATH"]
os.environ["VIRTUAL_ENV"] = vistir.compat.fs_str(prefix) os.environ["VIRTUAL_ENV"] = vistir.compat.fs_str(prefix)
else: 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["PYTHONPATH"] = self.base_paths["PYTHONPATH"]
os.environ.pop("PYTHONHOME", None) os.environ.pop("PYTHONHOME", None)
sys.path = self.sys_path sys.path = self.sys_path
sys.prefix = self.sys_prefix sys.prefix = self.sys_prefix
site.addsitedir(self.base_paths["purelib"]) 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") pip_vendor = self.safe_import("pip._vendor")
pep517_dir = os.path.join(os.path.dirname(pip_vendor.__file__), "pep517") pep517_dir = os.path.join(os.path.dirname(pip_vendor.__file__), "pep517")
site.addsitedir(pep517_dir) site.addsitedir(pep517_dir)
os.environ["PYTHONPATH"] = os.pathsep.join([ os.environ["PYTHONPATH"] = os.pathsep.join(
os.environ.get("PYTHONPATH", self.base_paths["PYTHONPATH"]), pep517_dir [os.environ.get("PYTHONPATH", self.base_paths["PYTHONPATH"]), pep517_dir]
]) )
if include_extras: if include_extras:
site.addsitedir(parent_path) site.addsitedir(parent_path)
sys.path.extend([parent_path, patched_dir, vendor_dir]) sys.path.extend([parent_path, patched_dir, vendor_dir])
@@ -905,6 +996,7 @@ class Environment:
@cached_property @cached_property
def finders(self): def finders(self):
from pipenv.vendor.pythonfinder import Finder from pipenv.vendor.pythonfinder import Finder
finders = [ finders = [
Finder(path=self.base_paths["scripts"], global_search=gs, system=False) Finder(path=self.base_paths["scripts"], global_search=gs, system=False)
for gs in (False, True) for gs in (False, True)
@@ -929,14 +1021,18 @@ class Environment:
install_arg = "install" if not editable else "develop" install_arg = "install" if not editable else "develop"
install_keys = ["headers", "purelib", "platlib", "scripts", "data"] install_keys = ["headers", "purelib", "platlib", "scripts", "data"]
install_args = [ install_args = [
self.environment.python, "-u", "-c", SETUPTOOLS_SHIM % setup_path, self.environment.python,
install_arg, "--single-version-externally-managed", "--no-deps", "-u",
"--prefix={}".format(self.base_paths["prefix"]), "--no-warn-script-location" "-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: for key in install_keys:
install_args.append( install_args.append(f"--install-{key}={self.base_paths[key]}")
f"--install-{key}={self.base_paths[key]}"
)
return install_args return install_args
def install(self, requirements): def install(self, requirements):
@@ -944,28 +1040,33 @@ class Environment:
requirements = [requirements] requirements = [requirements]
with self.get_finder() as finder: with self.get_finder() as finder:
args = [] args = []
for format_control in ('no_binary', 'only_binary'): for format_control in ("no_binary", "only_binary"):
formats = getattr(finder.format_control, format_control) formats = getattr(finder.format_control, format_control)
args.extend(('--' + format_control.replace('_', '-'), args.extend(
','.join(sorted(formats or {':none:'})))) (
"--" + format_control.replace("_", "-"),
",".join(sorted(formats or {":none:"})),
)
)
if finder.index_urls: 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:]: for extra_index in finder.index_urls[1:]:
args.extend(['--extra-index-url', extra_index]) args.extend(["--extra-index-url", extra_index])
else: else:
args.append('--no-index') args.append("--no-index")
for link in finder.find_links: for link in finder.find_links:
args.extend(['--find-links', link]) args.extend(["--find-links", link])
for _, host, _ in finder.secure_origins: for _, host, _ in finder.secure_origins:
args.extend(['--trusted-host', host]) args.extend(["--trusted-host", host])
if finder.allow_all_prereleases: if finder.allow_all_prereleases:
args.append('--pre') args.append("--pre")
if finder.process_dependency_links: if finder.process_dependency_links:
args.append('--process-dependency-links') args.append("--process-dependency-links")
args.append('--') args.append("--")
args.extend(requirements) args.extend(requirements)
out, _ = vistir.misc.run(args, return_object=False, nospin=True, block=True, out, _ = vistir.misc.run(
combine_stderr=False) args, return_object=False, nospin=True, block=True, combine_stderr=False
)
@contextlib.contextmanager @contextlib.contextmanager
def uninstall(self, pkgname, *args, **kwargs): def uninstall(self, pkgname, *args, **kwargs):
@@ -983,18 +1084,21 @@ class Environment:
auto_confirm = kwargs.pop("auto_confirm", True) auto_confirm = kwargs.pop("auto_confirm", True)
verbose = kwargs.pop("verbose", False) verbose = kwargs.pop("verbose", False)
with self.activated(): with self.activated():
monkey_patch = next(iter( monkey_patch = next(
dist for dist in self.base_working_set iter(
if dist.project_name == "recursive-monkey-patch" dist
), None) for dist in self.base_working_set
if dist.project_name == "recursive-monkey-patch"
),
None,
)
if monkey_patch: if monkey_patch:
monkey_patch.activate() monkey_patch.activate()
pip_shims = self.safe_import("pip_shims") pip_shims = self.safe_import("pip_shims")
pathset_base = pip_shims.UninstallPathSet pathset_base = pip_shims.UninstallPathSet
pathset_base._permitted = PatchedUninstaller._permitted pathset_base._permitted = PatchedUninstaller._permitted
dist = next( dist = next(
iter(d for d in self.get_working_set() if d.project_name == pkgname), iter(d for d in self.get_working_set() if d.project_name == pkgname), None
None
) )
pathset = pathset_base.from_dist(dist) pathset = pathset_base.from_dist(dist)
if pathset is not None: 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._compat import fix_utf8
from pipenv.vendor.vistir.misc import _isatty, fs_str from pipenv.vendor.vistir.misc import _isatty, fs_str
# HACK: avoid resolver.py uses the wrong byte code files. # HACK: avoid resolver.py uses the wrong byte code files.
# I hope I can remove this one day. # I hope I can remove this one day.
os.environ["PYTHONDONTWRITEBYTECODE"] = fs_str("1") os.environ["PYTHONDONTWRITEBYTECODE"] = fs_str("1")
@@ -36,8 +35,7 @@ def env_to_bool(val):
def _is_env_truthy(name): 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: if name not in os.environ:
return False return False
return os.environ.get(name).lower() not in _false_values return os.environ.get(name).lower() not in _false_values
@@ -86,8 +84,8 @@ def normalize_pipfile_path(p):
except OSError: except OSError:
loc = loc.absolute() loc = loc.absolute()
# Recase the path properly on Windows. From https://stackoverflow.com/a/35229734/5043728 # Recase the path properly on Windows. From https://stackoverflow.com/a/35229734/5043728
if os.name == 'nt': if os.name == "nt":
matches = glob.glob(re.sub(r'([^:/\\])(?=[/\\]|$)', r'[\1]', str(loc))) matches = glob.glob(re.sub(r"([^:/\\])(?=[/\\]|$)", r"[\1]", str(loc)))
path_str = matches and matches[0] or str(loc) path_str = matches and matches[0] or str(loc)
else: else:
path_str = str(loc) path_str = str(loc)
@@ -99,7 +97,7 @@ def normalize_pipfile_path(p):
os.environ.pop("__PYVENV_LAUNCHER__", None) os.environ.pop("__PYVENV_LAUNCHER__", None)
# Internal, to tell whether the command line session is interactive. # Internal, to tell whether the command line session is interactive.
SESSION_IS_INTERACTIVE = _isatty(sys.stdout) 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")) PIPENV_COLORBLIND = bool(os.environ.get("PIPENV_COLORBLIND"))
"""If set, disable terminal colors. """If set, disable terminal colors.
@@ -125,14 +123,18 @@ class Setting:
def initialize(self): 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. """Location for Pipenv to store it's package cache.
Default is to use appdir's user cache directory. Default is to use appdir's user cache directory.
""" """
# Tells Pipenv which Python to default to, when none is provided. # 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. """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 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. 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. """Max number of seconds to wait for package installation.
Defaults to 900 (15 minutes), a very long arbitrary time. Defaults to 900 (15 minutes), a very long arbitrary time.
@@ -248,7 +252,7 @@ class Setting:
pipenv_pipfile = normalize_pipfile_path(pipenv_pipfile) pipenv_pipfile = normalize_pipfile_path(pipenv_pipfile)
# Overwrite environment variable so that subprocesses can get the correct path. # Overwrite environment variable so that subprocesses can get the correct path.
# See https://github.com/pypa/pipenv/issues/3584 # 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 self.PIPENV_PIPFILE = pipenv_pipfile
"""If set, this specifies a custom Pipfile location. """If set, this specifies a custom Pipfile location.
@@ -332,10 +336,9 @@ class Setting:
Defaults to ``(w)ipe`` Defaults to ``(w)ipe``
""" """
self.PIPENV_RESOLVE_VCS = ( self.PIPENV_RESOLVE_VCS = os.environ.get(
os.environ.get("PIPENV_RESOLVE_VCS") is None "PIPENV_RESOLVE_VCS"
or _is_env_truthy("PIPENV_RESOLVE_VCS") ) is None or _is_env_truthy("PIPENV_RESOLVE_VCS")
)
"""Tells Pipenv whether to resolve all VCS dependencies in full. """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'. approach, you may set this to '0', 'off', or 'false'.
""" """
self.PIPENV_PYUP_API_KEY = os.environ.get( self.PIPENV_PYUP_API_KEY = os.environ.get("PIPENV_PYUP_API_KEY", None)
"PIPENV_PYUP_API_KEY", None
)
# Internal, support running in a different Python from sys.executable. # Internal, support running in a different Python from sys.executable.
self.PIPENV_PYTHON = os.environ.get("PIPENV_PYTHON") self.PIPENV_PYTHON = os.environ.get("PIPENV_PYTHON")
@@ -396,12 +397,12 @@ class Setting:
def is_using_venv(): def is_using_venv():
# type: () -> bool # type: () -> bool
"""Check for venv-based virtual environment which sets sys.base_prefix""" """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 # virtualenv venvs
result = True result = True
else: else:
# PEP 405 venvs # PEP 405 venvs
result = sys.prefix != getattr(sys, 'base_prefix', sys.prefix) result = sys.prefix != getattr(sys, "base_prefix", sys.prefix)
return result return result
+88 -76
View File
@@ -1,30 +1,29 @@
import itertools import itertools
import re import re
import sys import sys
from collections import namedtuple from collections import namedtuple
from traceback import format_tb from traceback import format_tb
from pipenv import environments from pipenv import environments
from pipenv._compat import decode_for_output from pipenv._compat import decode_for_output
from pipenv.patched import crayons 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 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) ANSI_REMOVAL_RE = re.compile(r"\033\[((?:\d|;)*)([a-zA-Z])", re.MULTILINE)
STRING_TYPES = ((str,), crayons.ColoredString) STRING_TYPES = ((str,), crayons.ColoredString)
if sys.version_info[:2] >= (3, 7): if sys.version_info[:2] >= (3, 7):
KnownException = namedtuple( KnownException = namedtuple(
'KnownException', ['exception_name', 'match_string', 'show_from_string', 'prefix'], "KnownException",
defaults=[None, None, None, ""] ["exception_name", "match_string", "show_from_string", "prefix"],
defaults=[None, None, None, ""],
) )
else: else:
KnownException = namedtuple( 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, "") KnownException.__new__.__defaults__ = (None, None, None, "")
@@ -33,8 +32,8 @@ KNOWN_EXCEPTIONS = [
KnownException( KnownException(
"VirtualenvCreationException", "VirtualenvCreationException",
match_string="do_create_virtualenv", 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}" line = f" {line}"
else: else:
line = f" {line}" line = f" {line}"
line = "[{!s}]: {}".format( line = "[{!s}]: {}".format(exception.__class__.__name__, line)
exception.__class__.__name__, line
)
formatted_lines.append(line) formatted_lines.append(line)
# use new exception prettification rules to format exceptions according to # use new exception prettification rules to format exceptions according to
# UX rules # UX rules
@@ -102,20 +99,27 @@ class PipenvCmdError(PipenvException):
def show(self, file=None): def show(self, file=None):
if file is None: if file is None:
file = vistir.misc.get_text_stderr() file = vistir.misc.get_text_stderr()
click_echo("{} {}".format( click_echo(
crayons.red("Error running command: "), "{} {}".format(
crayons.normal(decode_for_output(f"$ {self.cmd}", file), bold=True) crayons.red("Error running command: "),
), err=True) crayons.normal(decode_for_output(f"$ {self.cmd}", file), bold=True),
),
err=True,
)
if self.out: if self.out:
click_echo("{} {}".format( click_echo(
crayons.normal("OUTPUT: "), "{} {}".format(
decode_for_output(self.out, file) crayons.normal("OUTPUT: "), decode_for_output(self.out, file)
), err=True) ),
err=True,
)
if self.err: if self.err:
click_echo("{} {}".format( click_echo(
crayons.normal("STDERR: "), "{} {}".format(
decode_for_output(self.err, file) crayons.normal("STDERR: "), decode_for_output(self.err, file)
), err=True) ),
err=True,
)
class JSONParseError(PipenvException): class JSONParseError(PipenvException):
@@ -128,18 +132,20 @@ class JSONParseError(PipenvException):
file = vistir.misc.get_text_stderr() file = vistir.misc.get_text_stderr()
message = "{}\n{}".format( message = "{}\n{}".format(
crayons.normal("Failed parsing JSON results:", bold=True), 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) click_echo(message, err=True)
if self.error_text: if self.error_text:
click_echo("{} {}".format( click_echo(
crayons.normal("ERROR TEXT:", bold=True), "{} {}".format(
decode_for_output(self.error_text, file) crayons.normal("ERROR TEXT:", bold=True),
), err=True) decode_for_output(self.error_text, file),
),
err=True,
)
class PipenvUsageError(UsageError): class PipenvUsageError(UsageError):
def __init__(self, message=None, ctx=None, **kwargs): def __init__(self, message=None, ctx=None, **kwargs):
formatted_message = "{0}: {1}" formatted_message = "{0}: {1}"
msg_prefix = crayons.red("ERROR:", bold=True) msg_prefix = crayons.red("ERROR:", bold=True)
@@ -164,29 +170,30 @@ class PipenvUsageError(UsageError):
if color: if color:
extra = getattr(crayons, color, "blue")(extra) extra = getattr(crayons, color, "blue")(extra)
click_echo(decode_for_output(extra, file), file=file) 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: if self.cmd is not None and self.cmd.get_help_option(self.ctx) is not None:
hint = ('Try "%s %s" for help.\n' hint = 'Try "%s %s" for help.\n' % (
% (self.ctx.command_path, self.ctx.help_option_names[0])) self.ctx.command_path,
self.ctx.help_option_names[0],
)
if self.ctx is not None: 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) click_echo(self.message, file=file)
class PipenvFileError(FileError): class PipenvFileError(FileError):
formatted_message = "{0} {{0}} {{1}}".format( formatted_message = "{0} {{0}} {{1}}".format(crayons.red("ERROR:", bold=True))
crayons.red("ERROR:", bold=True)
)
def __init__(self, filename, message=None, **kwargs): def __init__(self, filename, message=None, **kwargs):
extra = kwargs.pop("extra", []) extra = kwargs.pop("extra", [])
if not message: if not message:
message = crayons.normal("Please ensure that the file exists!", bold=True) message = crayons.normal("Please ensure that the file exists!", bold=True)
message = self.formatted_message.format( message = self.formatted_message.format(
crayons.normal(f"{filename} not found!", bold=True), crayons.normal(f"{filename} not found!", bold=True), message
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 self.extra = extra
def show(self, file=None): def show(self, file=None):
@@ -203,14 +210,13 @@ class PipenvFileError(FileError):
class PipfileNotFound(PipenvFileError): class PipfileNotFound(PipenvFileError):
def __init__(self, filename="Pipfile", extra=None, **kwargs): def __init__(self, filename="Pipfile", extra=None, **kwargs):
extra = kwargs.pop("extra", []) extra = kwargs.pop("extra", [])
message = ( message = "{} {}".format(
"{} {}".format( crayons.red("Aborting!", bold=True),
crayons.red("Aborting!", bold=True), crayons.normal(
crayons.normal( "Please ensure that the file exists and is located in your"
"Please ensure that the file exists and is located in your" " project root directory.",
" project root directory.", bold=True bold=True,
) ),
)
) )
super().__init__(filename, message=message, extra=extra, **kwargs) super().__init__(filename, message=message, extra=extra, **kwargs)
@@ -221,7 +227,7 @@ class LockfileNotFound(PipenvFileError):
message = "{} {} {}".format( message = "{} {} {}".format(
crayons.normal("You need to run", bold=True), crayons.normal("You need to run", bold=True),
crayons.red("$ pipenv lock", 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) super().__init__(filename, message=message, extra=extra, **kwargs)
@@ -264,7 +270,6 @@ class SetupException(PipenvException):
class VirtualenvException(PipenvException): class VirtualenvException(PipenvException):
def __init__(self, message=None, **kwargs): def __init__(self, message=None, **kwargs):
if not message: if not message:
message = ( message = (
@@ -309,7 +314,7 @@ class UninstallError(PipenvException):
extra = [ extra = [
"{} {}".format( "{} {}".format(
crayons.cyan("Attempted to run command: "), 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()]) extra.extend([crayons.cyan(line.strip()) for line in return_values.splitlines()])
@@ -317,7 +322,7 @@ class UninstallError(PipenvException):
package = " ".join(package) package = " ".join(package)
message = "{!s} {!s}...".format( message = "{!s} {!s}...".format(
crayons.normal("Failed to uninstall package(s)"), 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 self.exit_code = return_code
PipenvException.__init__(self, message=message, extra=extra) PipenvException.__init__(self, message=message, extra=extra)
@@ -332,8 +337,7 @@ class InstallError(PipenvException):
crayons.normal(f"{package!s}", bold=True) crayons.normal(f"{package!s}", bold=True)
) )
message = "{} {}".format( message = "{} {}".format(
f"{package_message}", f"{package_message}", crayons.yellow("Package installation failed...")
crayons.yellow("Package installation failed...")
) )
extra = kwargs.pop("extra", []) extra = kwargs.pop("extra", [])
PipenvException.__init__(self, message=message, extra=extra, **kwargs) PipenvException.__init__(self, message=message, extra=extra, **kwargs)
@@ -344,17 +348,21 @@ class CacheError(PipenvException):
message = "{} {}\n{}".format( message = "{} {}\n{}".format(
crayons.cyan("Corrupt cache file"), crayons.cyan("Corrupt cache file"),
crayons.normal(f"{path!s}"), 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) PipenvException.__init__(self, message=message)
class DependencyConflict(PipenvException): class DependencyConflict(PipenvException):
def __init__(self, message): def __init__(self, message):
extra = ["{} {}".format( extra = [
crayons.red("The operation failed...", bold=True), "{} {}".format(
crayons.red("A dependency conflict was detected and could not be resolved."), 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) PipenvException.__init__(self, message, extra=extra)
@@ -382,21 +390,28 @@ class ResolutionFailure(PipenvException):
crayons.cyan( crayons.cyan(
"Please check your version specifier and version number. " "Please check your version specifier and version number. "
"See PEP440 for more information." "See PEP440 for more information."
) ),
) )
PipenvException.__init__(self, message, extra=extra) PipenvException.__init__(self, message, extra=extra)
class RequirementError(PipenvException): class RequirementError(PipenvException):
def __init__(self, req=None): def __init__(self, req=None):
from .utils import VCS_LIST 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: if req is not None:
possible_display_values = [getattr(req, value, None) for value in keys] possible_display_values = [getattr(req, value, None) for value in keys]
req_value = next(iter( req_value = next(
val for val in possible_display_values if val is not None iter(val for val in possible_display_values if val is not None), None
), None) )
if not req_value: if not req_value:
getstate_fn = getattr(req, "__getstate__", None) getstate_fn = getattr(req, "__getstate__", None)
slots = getattr(req, "__slots__", None) slots = getattr(req, "__slots__", None)
@@ -405,22 +420,17 @@ class RequirementError(PipenvException):
req_value = getstate_fn() req_value = getstate_fn()
elif slots: elif slots:
slot_vals = [ slot_vals = [
(k, getattr(req, k, None)) for k in slots (k, getattr(req, k, None)) for k in slots if getattr(req, k, None)
if getattr(req, k, None)
] ]
req_value = "\n".join([ req_value = "\n".join([f" {k}: {v}" for k, v in slot_vals])
f" {k}: {v}" for k, v in slot_vals
])
elif keys_fn: elif keys_fn:
values = [(k, req.get(k)) for k in keys_fn() if req.get(k)] values = [(k, req.get(k)) for k in keys_fn() if req.get(k)]
req_value = "\n".join([ req_value = "\n".join([f" {k}: {v}" for k, v in values])
f" {k}: {v}" for k, v in values
])
else: else:
req_value = getattr(req.line_instance, "line", None) req_value = getattr(req.line_instance, "line", None)
message = "{} {}".format( message = "{} {}".format(
crayons.normal(decode_for_output("Failed creating requirement instance")), 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)] extra = [str(req)]
PipenvException.__init__(self, message, extra=extra) PipenvException.__init__(self, message, extra=extra)
@@ -432,7 +442,9 @@ def prettify_exc(error):
errors = [] errors = []
for exc in KNOWN_EXCEPTIONS: for exc in KNOWN_EXCEPTIONS:
search_string = exc.match_string if exc.match_string else exc.exception_name 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: if search_string in error:
# for known exceptions with no display rules and no prefix # for known exceptions with no display rules and no prefix
# we should simply show nothing # we should simply show nothing
+2 -4
View File
@@ -3,7 +3,6 @@ import pprint
import sys import sys
import pipenv import pipenv
from pipenv.pep508checker import lookup from pipenv.pep508checker import lookup
from pipenv.vendor import pythonfinder from pipenv.vendor import pythonfinder
@@ -69,9 +68,7 @@ def get_pipenv_diagnostics(project):
print("") print("")
if project.lockfile_exists: if project.lockfile_exists:
print("") print("")
print_utf( print_utf(f"Contents of `Pipfile.lock` ({project.lockfile_location!r}):")
f"Contents of `Pipfile.lock` ({project.lockfile_location!r}):"
)
print("") print("")
print("```json") print("```json")
with open(project.lockfile_location) as f: with open(project.lockfile_location) as f:
@@ -82,4 +79,5 @@ def get_pipenv_diagnostics(project):
if __name__ == "__main__": if __name__ == "__main__":
from pipenv.project import Project from pipenv.project import Project
get_pipenv_diagnostics(Project()) get_pipenv_diagnostics(Project())
+42 -38
View File
@@ -1,12 +1,12 @@
import os
import operator import operator
import os
import re import re
import sys import sys
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
from pipenv.vendor import attr
from pipenv.utils.processes import subprocess_run from pipenv.utils.processes import subprocess_run
from pipenv.utils.shell import find_windows_executable from pipenv.utils.shell import find_windows_executable
from pipenv.vendor import attr
@attr.s @attr.s
@@ -20,15 +20,14 @@ class Version:
parts = [self.major, self.minor] parts = [self.major, self.minor]
if self.patch is not None: if self.patch is not None:
parts.append(self.patch) parts.append(self.patch)
return '.'.join(str(p) for p in parts) return ".".join(str(p) for p in parts)
@classmethod @classmethod
def parse(cls, name): def parse(cls, name):
"""Parse an X.Y.Z or X.Y string into a version tuple. """Parse an X.Y.Z or X.Y string into a version tuple."""
""" match = re.match(r"^(\d+)\.(\d+)(?:\.(\d+))?$", name)
match = re.match(r'^(\d+)\.(\d+)(?:\.(\d+))?$', name)
if not match: if not match:
raise ValueError(f'invalid version name {name!r}') raise ValueError(f"invalid version name {name!r}")
major = int(match.group(1)) major = int(match.group(1))
minor = int(match.group(2)) minor = int(match.group(2))
patch = match.group(3) patch = match.group(3)
@@ -47,8 +46,7 @@ class Version:
return (self.major, self.minor, self.patch or 0) return (self.major, self.minor, self.patch or 0)
def matches_minor(self, other): 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) return (self.major, self.minor) == (other.major, other.minor)
@@ -64,7 +62,6 @@ class InstallerError(RuntimeError):
class Installer(metaclass=ABCMeta): class Installer(metaclass=ABCMeta):
def __init__(self, project): def __init__(self, project):
self.cmd = self._find_installer() self.cmd = self._find_installer()
self.project = project self.project = project
@@ -103,32 +100,37 @@ class Installer(metaclass=ABCMeta):
# Look for the Python installer using the equivalent of 'which'. On # Look for the Python installer using the equivalent of 'which'. On
# Homebrew-installed systems, the env var may not be set, but this # Homebrew-installed systems, the env var may not be set, but this
# strategy will work. # strategy will work.
find_windows_executable('', name), find_windows_executable("", name),
# Check for explicitly set install locations (e.g. PYENV_ROOT, ASDF_DIR). # 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 # 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 return candidate
raise InstallerNotFound() raise InstallerNotFound()
def _run(self, *args, **kwargs): def _run(self, *args, **kwargs):
timeout = kwargs.pop('timeout', 30) timeout = kwargs.pop("timeout", 30)
shell = kwargs.pop('shell', False) shell = kwargs.pop("shell", False)
if kwargs: if kwargs:
k = list(kwargs.keys())[0] 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) args = (self.cmd,) + tuple(args)
c = subprocess_run(args, timeout=timeout, shell=shell) c = subprocess_run(args, timeout=timeout, shell=shell)
if c.returncode != 0: if c.returncode != 0:
raise InstallerError(f'failed to run {args}', c) raise InstallerError(f"failed to run {args}", c)
return c return c
@abstractmethod @abstractmethod
def iter_installable_versions(self): def iter_installable_versions(self):
"""Iterate through CPython versions available for Pipenv to install. """Iterate through CPython versions available for Pipenv to install."""
"""
pass pass
def find_version_to_install(self, name): def find_version_to_install(self, name):
@@ -140,14 +142,17 @@ class Installer(metaclass=ABCMeta):
if version.patch is not None: if version.patch is not None:
return name return name
try: try:
best_match = max(( best_match = max(
inst_version (
for inst_version in self.iter_installable_versions() inst_version
if inst_version.matches_minor(version) for inst_version in self.iter_installable_versions()
), key=operator.attrgetter('cmpkey')) if inst_version.matches_minor(version)
),
key=operator.attrgetter("cmpkey"),
)
except ValueError: except ValueError:
raise ValueError( raise ValueError(
f'no installable version found for {name!r}', f"no installable version found for {name!r}",
) )
return best_match return best_match
@@ -168,17 +173,16 @@ class Pyenv(Installer):
WIN = sys.platform.startswith("win") or (sys.platform == "cli" and os.name == "nt") WIN = sys.platform.startswith("win") or (sys.platform == "cli" and os.name == "nt")
def _find_installer(self): 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): def _run(self, *args, **kwargs):
if Pyenv.WIN: if Pyenv.WIN:
kwargs['shell'] = True kwargs["shell"] = True
return super(Pyenv, self)._run(*args, **kwargs) return super(Pyenv, self)._run(*args, **kwargs)
def iter_installable_versions(self): def iter_installable_versions(self):
"""Iterate through CPython versions available for Pipenv to install. """Iterate through CPython versions available for Pipenv to install."""
""" for name in self._run("install", "--list").stdout.splitlines():
for name in self._run('install', '--list').stdout.splitlines():
try: try:
version = Version.parse(name.strip()) version = Version.parse(name.strip())
except ValueError: except ValueError:
@@ -192,7 +196,7 @@ class Pyenv(Installer):
A ValueError is raised if the given version does not have a match in A ValueError is raised if the given version does not have a match in
pyenv. A InstallerError is raised if the pyenv command fails. pyenv. A InstallerError is raised if the pyenv command fails.
""" """
args = ['install', '-s', str(version)] args = ["install", "-s", str(version)]
if Pyenv.WIN: if Pyenv.WIN:
# pyenv-win skips installed versions by default and does not support -s # pyenv-win skips installed versions by default and does not support -s
del args[1] del args[1]
@@ -200,14 +204,12 @@ class Pyenv(Installer):
class Asdf(Installer): class Asdf(Installer):
def _find_installer(self): 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): def iter_installable_versions(self):
"""Iterate through CPython versions available for asdf to install. """Iterate through CPython versions available for asdf to install."""
""" for name in self._run("list-all", "python").stdout.splitlines():
for name in self._run('list-all', 'python').stdout.splitlines():
try: try:
version = Version.parse(name.strip()) version = Version.parse(name.strip())
except ValueError: except ValueError:
@@ -222,7 +224,9 @@ class Asdf(Installer):
asdf. A InstallerError is raised if the asdf command fails. asdf. A InstallerError is raised if the asdf command fails.
""" """
c = self._run( c = self._run(
'install', 'python', str(version), "install",
"python",
str(version),
timeout=self.project.s.PIPENV_INSTALL_TIMEOUT, timeout=self.project.s.PIPENV_INSTALL_TIMEOUT,
) )
return c return c
+5 -5
View File
@@ -1058,28 +1058,28 @@ Update vendored dependencies and invocations
.INDENT 2.0 .INDENT 2.0
.IP \(bu 2 .IP \(bu 2
Update vendored and patched dependencies 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 .nf
\(ga\(ga \(ga\(ga
.fi .fi
tomlkit\(ga tomlkit\(ga
.IP \(bu 2 .IP \(bu 2
Fix invocations of dependencies Fix invocations of dependencies
\- Fix custom \- Fix custom
.nf .nf
\(ga\(ga \(ga\(ga
.fi .fi
InstallCommand\(ga instantiation InstallCommand\(ga instantiation
\- Update \- Update
.nf .nf
\(ga\(ga \(ga\(ga
.fi .fi
PackageFinder\(ga usage PackageFinder\(ga usage
\- Fix \- Fix
.nf .nf
\(ga\(ga \(ga\(ga
.fi .fi
Bool\(ga stringify attempts from Bool\(ga stringify attempts from
.nf .nf
\(ga\(ga \(ga\(ga
.fi .fi
+15 -5
View File
@@ -11,18 +11,28 @@ class PopenProcess:
"""A wrapper of subprocess.Popen that """A wrapper of subprocess.Popen that
doesn't need to worry about the Pipe buffer exceeding the limit. doesn't need to worry about the Pipe buffer exceeding the limit.
""" """
def __init__( 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.blocking = block
self.env = env self.env = env
self.script = Script.parse(args) self.script = Script.parse(args)
if env is not None: if env is not None:
env = dict(os.environ, **env) env = dict(os.environ, **env)
other_kwargs['env'] = env other_kwargs["env"] = env
other_kwargs['stdout'] = subprocess.PIPE other_kwargs["stdout"] = subprocess.PIPE
other_kwargs['stderr'] = subprocess.PIPE other_kwargs["stderr"] = subprocess.PIPE
self._process = subprocess.Popen(args, universal_newlines=True, encoding=encoding, **other_kwargs) self._process = subprocess.Popen(
args, universal_newlines=True, encoding=encoding, **other_kwargs
)
self._endtime = None self._endtime = None
if timeout is not None: if timeout is not None:
self._endtime = _time() + timeout self._endtime = _time() + timeout
-1
View File
@@ -15,7 +15,6 @@ import crayons
from pipenv.environments import PIPENV_COLORBLIND, PIPENV_HIDE_EMOJIS from pipenv.environments import PIPENV_COLORBLIND, PIPENV_HIDE_EMOJIS
STREAM = sys.stderr STREAM = sys.stderr
MILL_TEMPLATE = "%s %s %i/%i\r" MILL_TEMPLATE = "%s %s %i/%i\r"
DOTS_CHAR = "." DOTS_CHAR = "."
+86 -68
View File
@@ -6,10 +6,10 @@ import io
import json import json
import operator import operator
import os import os
from pathlib import Path
import re import re
import sys import sys
import urllib.parse import urllib.parse
from pathlib import Path
import pipfile import pipfile
import pipfile.api import pipfile.api
@@ -20,11 +20,21 @@ import vistir
from pipenv.cmdparse import Script from pipenv.cmdparse import Script
from pipenv.core import system_which from pipenv.core import system_which
from pipenv.environment import Environment from pipenv.environment import Environment
from pipenv.environments import Setting, is_type_checking, is_in_virtualenv, normalize_pipfile_path from pipenv.environments import (
from pipenv.utils.dependencies import get_canonical_names, is_editable, is_installable_file, is_star, python_version 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.internet import get_url_name, is_valid_url, proper_case
from pipenv.utils.resolver import pep423_name from pipenv.utils.resolver import pep423_name
from pipenv.utils.toml import cleanup_toml, convert_toml_outline_tables
from pipenv.utils.shell import ( from pipenv.utils.shell import (
find_requirements, find_requirements,
find_windows_executable, find_windows_executable,
@@ -32,19 +42,17 @@ from pipenv.utils.shell import (
get_workon_home, get_workon_home,
is_virtual_environment, is_virtual_environment,
looks_like_dir, 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.cached_property import cached_property
from pipenv.vendor.requirementslib.models.utils import ( from pipenv.vendor.requirementslib.models.utils import get_default_pyproject_backend
get_default_pyproject_backend
)
if is_type_checking(): if is_type_checking():
from typing import Dict, List, Optional, Set, Text, Tuple, Union from typing import Dict, List, Optional, Set, Text, Tuple, Union
import pkg_resources import pkg_resources
TSource = Dict[Text, Union[Text, bool]] TSource = Dict[Text, Union[Text, bool]]
TPackageEntry = Dict[str, Union[bool, str, List[str]]] TPackageEntry = Dict[str, Union[bool, str, List[str]]]
TPackage = Dict[str, TPackageEntry] TPackage = Dict[str, TPackageEntry]
@@ -114,22 +122,20 @@ class Project:
self._requirements_location = None self._requirements_location = None
self._original_dir = os.path.abspath(os.curdir) self._original_dir = os.path.abspath(os.curdir)
self._environment = None self._environment = None
self._build_system = { self._build_system = {"requires": ["setuptools", "wheel"]}
"requires": ["setuptools", "wheel"]
}
self.python_version = python_version self.python_version = python_version
self.s = Setting() self.s = Setting()
if self.s.PIPENV_TEST_INDEX: if self.s.PIPENV_TEST_INDEX:
self.default_source = { self.default_source = {
u"url": self.s.PIPENV_TEST_INDEX, "url": self.s.PIPENV_TEST_INDEX,
u"verify_ssl": True, "verify_ssl": True,
u"name": u"custom", "name": "custom",
} }
else: else:
self.default_source = { self.default_source = {
u"url": u"https://pypi.org/simple", "url": "https://pypi.org/simple",
u"verify_ssl": True, "verify_ssl": True,
u"name": u"pypi", "name": "pypi",
} }
pipfile.api.DEFAULT_SOURCE = self.default_source pipfile.api.DEFAULT_SOURCE = self.default_source
@@ -151,6 +157,7 @@ class Project:
def _build_package_list(self, package_section): def _build_package_list(self, package_section):
"""Returns a list of packages for pip-tools to consume.""" """Returns a list of packages for pip-tools to consume."""
from pipenv.vendor.requirementslib.utils import is_vcs from pipenv.vendor.requirementslib.utils import is_vcs
ps = {} ps = {}
# TODO: Separate the logic for showing packages from the filters for supplying pip-tools # 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(): for k, v in self.parsed_pipfile.get(package_section, {}).items():
@@ -217,9 +224,7 @@ class Project:
def required_python_version(self): def required_python_version(self):
# type: () -> str # type: () -> str
if self.pipfile_exists: if self.pipfile_exists:
required = self.parsed_pipfile.get("requires", {}).get( required = self.parsed_pipfile.get("requires", {}).get("python_full_version")
"python_full_version"
)
if not required: if not required:
required = self.parsed_pipfile.get("requires", {}).get("python_version") required = self.parsed_pipfile.get("requires", {}).get("python_version")
if required != "*": if required != "*":
@@ -293,8 +298,10 @@ class Project:
def working_set(self): def working_set(self):
# type: () -> pkg_resources.WorkingSet # type: () -> pkg_resources.WorkingSet
from pipenv.utils.shell import load_path from pipenv.utils.shell import load_path
sys_path = load_path(self.which("python")) sys_path = load_path(self.which("python"))
import pkg_resources import pkg_resources
return pkg_resources.WorkingSet(sys_path) return pkg_resources.WorkingSet(sys_path)
@property @property
@@ -314,7 +321,7 @@ class Project:
return { return {
"dev": dev_keys, "dev": dev_keys,
"default": default_keys, "default": default_keys,
"combined": dev_keys | default_keys "combined": dev_keys | default_keys,
} }
@property @property
@@ -325,7 +332,7 @@ class Project:
return { return {
"dev": dev_keys, "dev": dev_keys,
"default": default_keys, "default": default_keys,
"combined": dev_keys | default_keys "combined": dev_keys | default_keys,
} }
def get_environment(self, allow_global=False): def get_environment(self, allow_global=False):
@@ -339,8 +346,12 @@ class Project:
python = None python = None
sources = self.sources if self.sources else [self.default_source] sources = self.sources if self.sources else [self.default_source]
environment = Environment( environment = Environment(
prefix=prefix, python=python, is_venv=is_venv, sources=sources, prefix=prefix,
pipfile=self.parsed_pipfile, project=self python=python,
is_venv=is_venv,
sources=sources,
pipfile=self.parsed_pipfile,
project=self,
) )
pipenv_dist = get_pipenv_dist(pkg="pipenv") pipenv_dist = get_pipenv_dist(pkg="pipenv")
if pipenv_dist: if pipenv_dist:
@@ -442,7 +453,8 @@ class Project:
virtualenv_env = os.getenv("VIRTUAL_ENV") virtualenv_env = os.getenv("VIRTUAL_ENV")
if ( if (
"PIPENV_ACTIVE" not in os.environ "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 return virtualenv_env
@@ -491,7 +503,7 @@ class Project:
# type: (str) -> None # type: (str) -> None
"""Registers a proper name to the database.""" """Registers a proper name to the database."""
with self.proper_names_db_path.open("a") as f: with self.proper_names_db_path.open("a") as f:
f.write(u"{0}\n".format(name)) f.write("{0}\n".format(name))
@property @property
def pipfile_location(self): def pipfile_location(self):
@@ -632,8 +644,8 @@ class Project:
@property @property
def _pipfile(self): def _pipfile(self):
from .vendor.requirementslib.models.pipfile import \ from .vendor.requirementslib.models.pipfile import Pipfile as ReqLibPipfile
Pipfile as ReqLibPipfile
pf = ReqLibPipfile.load(self.pipfile_location) pf = ReqLibPipfile.load(self.pipfile_location)
return pf return pf
@@ -660,6 +672,7 @@ class Project:
def _get_vcs_packages(self, dev=False): def _get_vcs_packages(self, dev=False):
from pipenv.vendor.requirementslib.utils import is_vcs from pipenv.vendor.requirementslib.utils import is_vcs
section = "dev-packages" if dev else "packages" section = "dev-packages" if dev else "packages"
packages = { packages = {
k: v k: v
@@ -727,15 +740,13 @@ class Project:
source_name = "pip_index_{}".format(i) source_name = "pip_index_{}".format(i)
verify_ssl = index.startswith("https") verify_ssl = index.startswith("https")
sources.append( sources.append({"url": index, "verify_ssl": verify_ssl, "name": source_name})
{u"url": index, u"verify_ssl": verify_ssl, u"name": source_name}
)
data = { data = {
u"source": sources, "source": sources,
# Default packages. # Default packages.
u"packages": {}, "packages": {},
u"dev-packages": {}, "dev-packages": {},
} }
# Default requires. # Default requires.
required_python = python required_python = python
@@ -746,7 +757,7 @@ class Project:
required_python = self.which("python") required_python = self.which("python")
version = python_version(required_python) or self.s.PIPENV_DEFAULT_PYTHON_VERSION version = python_version(required_python) or self.s.PIPENV_DEFAULT_PYTHON_VERSION
if version and len(version.split(".")) > 2: 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) self.write_toml(data)
@classmethod @classmethod
@@ -762,13 +773,15 @@ class Project:
return source return source
def get_or_create_lockfile(self, from_pipfile=False): def get_or_create_lockfile(self, from_pipfile=False):
from pipenv.vendor.requirementslib.models.lockfile import \ from pipenv.vendor.requirementslib.models.lockfile import (
Lockfile as Req_Lockfile Lockfile as Req_Lockfile,
)
lockfile = None lockfile = None
if from_pipfile and self.pipfile_exists: if from_pipfile and self.pipfile_exists:
lockfile_dict = { lockfile_dict = {
"default": self._lockfile["default"].copy(), "default": self._lockfile["default"].copy(),
"develop": self._lockfile["develop"].copy() "develop": self._lockfile["develop"].copy(),
} }
lockfile_dict.update({"_meta": self.get_lockfile_meta()}) lockfile_dict.update({"_meta": self.get_lockfile_meta()})
lockfile = Req_Lockfile.from_data( lockfile = Req_Lockfile.from_data(
@@ -778,9 +791,13 @@ class Project:
try: try:
lockfile = Req_Lockfile.load(self.lockfile_location) lockfile = Req_Lockfile.load(self.lockfile_location)
except OSError: 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: 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: if lockfile._lockfile is not None:
return lockfile return lockfile
if self.lockfile_exists and self.lockfile_content: if self.lockfile_exists and self.lockfile_content:
@@ -790,9 +807,7 @@ class Project:
sources = self.pipfile_sources sources = self.pipfile_sources
elif not isinstance(sources, list): elif not isinstance(sources, list):
sources = [sources] sources = [sources]
lockfile_dict["_meta"]["sources"] = [ lockfile_dict["_meta"]["sources"] = [self.populate_source(s) for s in sources]
self.populate_source(s) for s in sources
]
_created_lockfile = Req_Lockfile.from_data( _created_lockfile = Req_Lockfile.from_data(
path=self.lockfile_location, data=lockfile_dict, meta_from_project=False path=self.lockfile_location, data=lockfile_dict, meta_from_project=False
) )
@@ -803,6 +818,7 @@ class Project:
def get_lockfile_meta(self): def get_lockfile_meta(self):
from .vendor.plette.lockfiles import PIPFILE_SPEC_CURRENT from .vendor.plette.lockfiles import PIPFILE_SPEC_CURRENT
if self.lockfile_exists: if self.lockfile_exists:
sources = self.lockfile_content.get("_meta", {}).get("sources", []) sources = self.lockfile_content.get("_meta", {}).get("sources", [])
elif "source" in self.parsed_pipfile: elif "source" in self.parsed_pipfile:
@@ -815,7 +831,7 @@ class Project:
"hash": {"sha256": self.calculate_pipfile_hash()}, "hash": {"sha256": self.calculate_pipfile_hash()},
"pipfile-spec": PIPFILE_SPEC_CURRENT, "pipfile-spec": PIPFILE_SPEC_CURRENT,
"sources": [self.populate_source(s) for s in sources], "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): def write_toml(self, data, path=None):
@@ -836,13 +852,12 @@ class Project:
table.update(data[section][package]) table.update(data[section][package])
document[section][package] = table document[section][package] = table
else: else:
document[section][package] = tomlkit.string(data[section][package]) document[section][package] = tomlkit.string(
data[section][package]
)
formatted_data = tomlkit.dumps(document).rstrip() formatted_data = tomlkit.dumps(document).rstrip()
if ( if Path(path).absolute() == Path(self.pipfile_location).absolute():
Path(path).absolute()
== Path(self.pipfile_location).absolute()
):
newlines = self._pipfile_newlines newlines = self._pipfile_newlines
else: else:
newlines = DEFAULT_NEWLINES newlines = DEFAULT_NEWLINES
@@ -853,8 +868,7 @@ class Project:
self.clear_pipfile_cache() self.clear_pipfile_cache()
def write_lockfile(self, content): def write_lockfile(self, content):
"""Write out the lockfile. """Write out the lockfile."""
"""
s = self._lockfile_encoder.encode(content) s = self._lockfile_encoder.encode(content)
open_kwargs = {"newline": self._lockfile_newlines, "encoding": "utf-8"} open_kwargs = {"newline": self._lockfile_newlines, "encoding": "utf-8"}
with vistir.contextmanagers.atomic_open_for_write( with vistir.contextmanagers.atomic_open_for_write(
@@ -863,8 +877,8 @@ class Project:
f.write(s) f.write(s)
# Write newline at end of document. GH-319. # Write newline at end of document. GH-319.
# Only need '\n' here; the file object handles the rest. # Only need '\n' here; the file object handles the rest.
if not s.endswith(u"\n"): if not s.endswith("\n"):
f.write(u"\n") f.write("\n")
@property @property
def pipfile_sources(self): def pipfile_sources(self):
@@ -914,14 +928,18 @@ class Project:
def find_source(sources, name=None, url=None): def find_source(sources, name=None, url=None):
source = None source = None
if name: if name:
source = next(iter( source = next(
s for s in sources if "name" in s and s["name"] == name iter(s for s in sources if "name" in s and s["name"] == name), None
), None) )
elif url: elif url:
source = next(iter( source = next(
s for s in sources iter(
if "url" in s and is_url_equal(url, s.get("url", "")) s
), None) for s in sources
if "url" in s and is_url_equal(url, s.get("url", ""))
),
None,
)
if source is not None: if source is not None:
return source return source
@@ -961,9 +979,9 @@ class Project:
packages = set([pep423_name(pkg) for pkg in packages]) packages = set([pep423_name(pkg) for pkg in packages])
for section in ("dev-packages", "packages"): for section in ("dev-packages", "packages"):
pipfile_section = parsed.get(section, {}) pipfile_section = parsed.get(section, {})
pipfile_packages = set([ pipfile_packages = set(
pep423_name(pkg_name) for pkg_name in pipfile_section.keys() [pep423_name(pkg_name) for pkg_name in pipfile_section.keys()]
]) )
to_remove = packages & pipfile_packages to_remove = packages & pipfile_packages
# The normal toml parser can't handle deleting packages with preceding newlines # The normal toml parser can't handle deleting packages with preceding newlines
is_dev = section == "dev-packages" is_dev = section == "dev-packages"
@@ -995,9 +1013,7 @@ class Project:
self.write_toml(p) self.write_toml(p)
def src_name_from_url(self, index_url): 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(".", "") src_name = name.replace(".", "")
try: try:
self.get_source(name=src_name) self.get_source(name=src_name)
@@ -1005,6 +1021,7 @@ class Project:
name = src_name name = src_name
else: else:
from random import randint from random import randint
name = "{0}-{1}".format(src_name, randint(1, 1000)) name = "{0}-{1}".format(src_name, randint(1, 1000))
return name return name
@@ -1109,6 +1126,7 @@ class Project:
@cached_property @cached_property
def finders(self): def finders(self):
from .vendor.pythonfinder import Finder from .vendor.pythonfinder import Finder
scripts_dirname = "Scripts" if os.name == "nt" else "bin" scripts_dirname = "Scripts" if os.name == "nt" else "bin"
scripts_dir = os.path.join(self.virtualenv_location, scripts_dirname) scripts_dir = os.path.join(self.virtualenv_location, scripts_dirname)
finders = [ finders = [
+161 -71
View File
@@ -3,12 +3,12 @@ import logging
import os import os
import sys import sys
os.environ["PIP_PYTHON_PATH"] = str(sys.executable) os.environ["PIP_PYTHON_PATH"] = str(sys.executable)
def find_site_path(pkg, site_dir=None): def find_site_path(pkg, site_dir=None):
import pkg_resources import pkg_resources
if site_dir is None: if site_dir is None:
site_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) site_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
working_set = pkg_resources.WorkingSet([site_dir] + sys.path[:]) 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 base_name = dist.project_name if dist.project_name else dist.key
name = None name = None
if "top_level.txt" in dist.metadata_listdir(""): 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: if name is None:
name = pkg_resources.safe_name(base_name).replace("-", "_") name = pkg_resources.safe_name(base_name).replace("-", "_")
if not any(pkg == _ for _ in [base_name, name]): 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_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) path = next(iter(p for p in path_options if os.path.exists(p)), None)
if path is not None: if path is not None:
return (dist, path) return dist, path
return (None, None) return None, None
def _patch_path(pipenv_site=None): def _patch_path(pipenv_site=None):
import site import site
pipenv_libdir = os.path.dirname(os.path.abspath(__file__)) pipenv_libdir = os.path.dirname(os.path.abspath(__file__))
pipenv_site_dir = os.path.dirname(pipenv_libdir) pipenv_site_dir = os.path.dirname(pipenv_libdir)
pipenv_dist = None
if pipenv_site is not None: if pipenv_site is not None:
pipenv_dist, pipenv_path = find_site_path("pipenv", site_dir=pipenv_site) pipenv_dist, pipenv_path = find_site_path("pipenv", site_dir=pipenv_site)
else: else:
@@ -42,10 +51,16 @@ def _patch_path(pipenv_site=None):
if pipenv_dist is not None: if pipenv_dist is not None:
pipenv_dist.activate() pipenv_dist.activate()
else: else:
site.addsitedir(next(iter( site.addsitedir(
sitedir for sitedir in (pipenv_site, pipenv_site_dir) next(
if sitedir is not None iter(
), None)) sitedir
for sitedir in (pipenv_site, pipenv_site_dir)
if sitedir is not None
),
None,
)
)
if pipenv_path is not None: if pipenv_path is not None:
pipenv_libdir = pipenv_path pipenv_libdir = pipenv_path
for _dir in ("vendor", "patched", pipenv_libdir): for _dir in ("vendor", "patched", pipenv_libdir):
@@ -54,6 +69,7 @@ def _patch_path(pipenv_site=None):
def get_parser(): def get_parser():
from argparse import ArgumentParser from argparse import ArgumentParser
parser = ArgumentParser("pipenv-resolver") parser = ArgumentParser("pipenv-resolver")
parser.add_argument("--pre", action="store_true", default=False) parser.add_argument("--pre", action="store_true", default=False)
parser.add_argument("--clear", 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("--debug", action="store_true", default=False)
parser.add_argument("--system", 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("--parse-only", action="store_true", default=False)
parser.add_argument("--pipenv-site", metavar="pipenv_site_dir", action="store", parser.add_argument(
default=os.environ.get("PIPENV_SITE_DIR")) "--pipenv-site",
parser.add_argument("--requirements-dir", metavar="requirements_dir", action="store", metavar="pipenv_site_dir",
default=os.environ.get("PIPENV_REQ_DIR")) action="store",
parser.add_argument("--write", metavar="write", action="store", default=os.environ.get("PIPENV_SITE_DIR"),
default=os.environ.get("PIPENV_RESOLVER_FILE")) )
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="*") parser.add_argument("packages", nargs="*")
return parser return parser
@@ -103,6 +131,7 @@ class Entry:
def __init__(self, name, entry_dict, project, resolver, reverse_deps=None, dev=False): def __init__(self, name, entry_dict, project, resolver, reverse_deps=None, dev=False):
super().__init__() super().__init__()
from pipenv.vendor.requirementslib.models.utils import tomlkit_value_to_python from pipenv.vendor.requirementslib.models.utils import tomlkit_value_to_python
self.name = name self.name = name
if isinstance(entry_dict, dict): if isinstance(entry_dict, dict):
self.entry_dict = self.clean_initial_dict(entry_dict) self.entry_dict = self.clean_initial_dict(entry_dict)
@@ -137,6 +166,7 @@ class Entry:
@staticmethod @staticmethod
def make_requirement(name=None, entry=None, from_ireq=False): def make_requirement(name=None, entry=None, from_ireq=False):
from pipenv.vendor.requirementslib.models.requirements import Requirement from pipenv.vendor.requirementslib.models.requirements import Requirement
if from_ireq: if from_ireq:
return Requirement.from_ireq(entry) return Requirement.from_ireq(entry)
return Requirement.from_pipfile(name, entry) return Requirement.from_pipfile(name, entry)
@@ -152,12 +182,11 @@ class Entry:
@classmethod @classmethod
def parse_pyparsing_exprs(cls, expr_iterable): def parse_pyparsing_exprs(cls, expr_iterable):
from pipenv.vendor.pyparsing import Literal, MatchFirst from pipenv.vendor.pyparsing import Literal, MatchFirst
keys = [] keys = []
expr_list = [] expr_list = []
expr = expr_iterable.copy() expr = expr_iterable.copy()
if isinstance(expr, Literal) or ( if isinstance(expr, Literal) or (expr.__class__.__name__ == Literal.__name__):
expr.__class__.__name__ == Literal.__name__
):
keys.append(expr.match) keys.append(expr.match)
elif isinstance(expr, MatchFirst) or ( elif isinstance(expr, MatchFirst) or (
expr.__class__.__name__ == MatchFirst.__name__ expr.__class__.__name__ == MatchFirst.__name__
@@ -174,13 +203,11 @@ class Entry:
def get_markers_from_dict(cls, entry_dict): def get_markers_from_dict(cls, entry_dict):
from pipenv.vendor.packaging import markers as packaging_markers from pipenv.vendor.packaging import markers as packaging_markers
from pipenv.vendor.requirementslib.models.markers import normalize_marker_str from pipenv.vendor.requirementslib.models.markers import normalize_marker_str
marker_keys = cls.parse_pyparsing_exprs(packaging_markers.VARIABLE) marker_keys = cls.parse_pyparsing_exprs(packaging_markers.VARIABLE)
markers = set() markers = set()
keys_in_dict = [k for k in marker_keys if k in entry_dict] keys_in_dict = [k for k in marker_keys if k in entry_dict]
markers = { markers = {normalize_marker_str(f"{k} {entry_dict.pop(k)}") for k in keys_in_dict}
normalize_marker_str(f"{k} {entry_dict.pop(k)}")
for k in keys_in_dict
}
if "markers" in entry_dict: if "markers" in entry_dict:
markers.add(normalize_marker_str(entry_dict["markers"])) markers.add(normalize_marker_str(entry_dict["markers"]))
if None in markers: if None in markers:
@@ -209,9 +236,7 @@ class Entry:
@property @property
def original_markers(self): def original_markers(self):
original_markers, lockfile_dict = self.get_markers_from_dict( original_markers, lockfile_dict = self.get_markers_from_dict(self.lockfile_dict)
self.lockfile_dict
)
self.lockfile_dict = lockfile_dict self.lockfile_dict = lockfile_dict
self._original_markers = self.marker_to_str(original_markers) self._original_markers = self.marker_to_str(original_markers)
return self._original_markers return self._original_markers
@@ -219,9 +244,11 @@ class Entry:
@staticmethod @staticmethod
def marker_to_str(marker): def marker_to_str(marker):
from pipenv.vendor.requirementslib.models.markers import normalize_marker_str from pipenv.vendor.requirementslib.models.markers import normalize_marker_str
if not marker: if not marker:
return None return None
from pipenv.vendor.vistir.compat import Mapping from pipenv.vendor.vistir.compat import Mapping
marker_str = None marker_str = None
if isinstance(marker, Mapping): if isinstance(marker, Mapping):
marker_dict, _ = Entry.get_markers_from_dict(marker) marker_dict, _ = Entry.get_markers_from_dict(marker)
@@ -274,7 +301,9 @@ class Entry:
@property @property
def pipfile_entry(self): def pipfile_entry(self):
if self._pipfile_entry is None: 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 return self._pipfile_entry
@property @property
@@ -300,8 +329,9 @@ class Entry:
return self.project.pipfile_package_names["dev" if self.dev else "default"] return self.project.pipfile_package_names["dev" if self.dev else "default"]
def create_parent(self, name, specifier="*"): def create_parent(self, name, specifier="*"):
parent = self.create(name, specifier, self.project, self.resolver, parent = self.create(
self.reverse_deps, self.dev) name, specifier, self.project, self.resolver, self.reverse_deps, self.dev
)
parent._deptree = self.deptree parent._deptree = self.deptree
return parent return parent
@@ -318,6 +348,7 @@ class Entry:
@staticmethod @staticmethod
def clean_specifier(specifier): def clean_specifier(specifier):
from pipenv.vendor.packaging.specifiers import Specifier from pipenv.vendor.packaging.specifiers import Specifier
if not any(specifier.startswith(k) for k in Specifier._operators.keys()): if not any(specifier.startswith(k) for k in Specifier._operators.keys()):
if specifier.strip().lower() in ["any", "<any>", "*"]: if specifier.strip().lower() in ["any", "<any>", "*"]:
return "*" return "*"
@@ -329,17 +360,19 @@ class Entry:
@staticmethod @staticmethod
def strip_version(specifier): def strip_version(specifier):
from pipenv.vendor.packaging.specifiers import Specifier from pipenv.vendor.packaging.specifiers import Specifier
op = next(iter(
k for k in Specifier._operators.keys() if specifier.startswith(k) op = next(
), None) iter(k for k in Specifier._operators.keys() if specifier.startswith(k)), None
)
if op: if op:
specifier = specifier[len(op):] specifier = specifier[len(op) :]
while op: while op:
op = next(iter( op = next(
k for k in Specifier._operators.keys() if specifier.startswith(k) iter(k for k in Specifier._operators.keys() if specifier.startswith(k)),
), None) None,
)
if op: if op:
specifier = specifier[len(op):] specifier = specifier[len(op) :]
return specifier return specifier
@property @property
@@ -358,7 +391,8 @@ class Entry:
def parents_in_pipfile(self): def parents_in_pipfile(self):
if not self._parents_in_pipfile: if not self._parents_in_pipfile:
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 if p.normalized_name in self.pipfile_packages
] ]
return self._parents_in_pipfile return self._parents_in_pipfile
@@ -370,9 +404,9 @@ class Entry:
@property @property
def requirements(self): def requirements(self):
if not self._requires: if not self._requires:
self._requires = next(iter( self._requires = next(
self.project.environment.get_package_requirements(self.name) iter(self.project.environment.get_package_requirements(self.name)), {}
), {}) )
return self._requires return self._requires
@property @property
@@ -403,14 +437,19 @@ class Entry:
def get_dependency(self, name): def get_dependency(self, name):
if self.requirements: if self.requirements:
return next(iter( return next(
dep for dep in self.requirements.get("dependencies", []) iter(
if dep and dep.get("package_name", "") == name dep
), {}) for dep in self.requirements.get("dependencies", [])
if dep and dep.get("package_name", "") == name
),
{},
)
return {} return {}
def get_parent_deps(self, unnest=False): def get_parent_deps(self, unnest=False):
from pipenv.vendor.packaging.specifiers import Specifier from pipenv.vendor.packaging.specifiers import Specifier
parents = [] parents = []
for spec in self.reverse_deps.get(self.normalized_name, {}).get("parents", set()): 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) spec_match = next(iter(c for c in Specifier._operators if c in spec), None)
@@ -418,7 +457,9 @@ class Entry:
parent = None parent = None
if spec_match is not None: if spec_match is not None:
spec_index = spec.index(spec_match) 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_start = spec_index + len(spec_match)
name = spec[name_start:].strip() name = spec[name_start:].strip()
parent = self.create_parent(name, specifier) parent = self.create_parent(name, specifier)
@@ -460,13 +501,16 @@ class Entry:
self.entry_dict = self.lockfile_dict.copy() self.entry_dict = self.lockfile_dict.copy()
elif can_use_updated: elif can_use_updated:
if len(satisfied_by_versions) == 1: if len(satisfied_by_versions) == 1:
self.entry_dict["version"] = next(iter( self.entry_dict["version"] = next(
sat_by for sat_by in satisfied_by_versions if sat_by iter(sat_by for sat_by in satisfied_by_versions if sat_by), None
), None) )
hashes = None hashes = None
if self.lockfile_entry.specifiers == satisfied_by: if self.lockfile_entry.specifiers == satisfied_by:
ireq = self.lockfile_entry.as_ireq() 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) hashes = self.resolver.get_hash(ireq)
else: else:
hashes = self.lockfile_entry.hashes hashes = self.lockfile_entry.hashes
@@ -491,11 +535,12 @@ class Entry:
:rtype: Set :rtype: Set
""" """
constraints = { constraints = {
c for c in self.resolver.parsed_constraints c for c in self.resolver.parsed_constraints if c and c.name == self.entry.name
if c and c.name == self.entry.name
} }
pipfile_constraint = self.get_pipfile_constraint() 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) constraints.add(pipfile_constraint)
return constraints return constraints
@@ -532,8 +577,10 @@ class Entry:
msg = ( msg = (
"Cannot resolve conflicting version {}{} while {}{} is " "Cannot resolve conflicting version {}{} while {}{} is "
"locked.".format( "locked.".format(
self.name, constraint.req.specifier, self.name,
self.name, self.updated_specifier constraint.req.specifier,
self.name,
self.updated_specifier,
) )
) )
raise DependencyConflict(msg) raise DependencyConflict(msg)
@@ -545,12 +592,15 @@ class Entry:
continue continue
if not parent.validate_specifiers(): if not parent.validate_specifiers():
from pipenv.exceptions import DependencyConflict from pipenv.exceptions import DependencyConflict
msg = ( msg = (
"Cannot resolve conflicting versions: (Root: {}) {}{} (Pipfile) " "Cannot resolve conflicting versions: (Root: {}) {}{} (Pipfile) "
"Incompatible with {}{} (resolved)\n".format( "Incompatible with {}{} (resolved)\n".format(
self.name, parent.pipfile_name, self.name,
parent.pipfile_entry.requirement.specifiers, parent.name, parent.pipfile_name,
parent.updated_specifiers parent.pipfile_entry.requirement.specifiers,
parent.name,
parent.updated_specifiers,
) )
) )
raise DependencyConflict(msg) raise DependencyConflict(msg)
@@ -586,6 +636,7 @@ class Entry:
def clean_results(results, resolver, project, dev=False): def clean_results(results, resolver, project, dev=False):
from pipenv.utils.dependencies import translate_markers from pipenv.utils.dependencies import translate_markers
if not project.lockfile_exists: if not project.lockfile_exists:
return results return results
lockfile = project.lockfile_content lockfile = project.lockfile_content
@@ -595,7 +646,9 @@ def clean_results(results, resolver, project, dev=False):
for result in results: for result in results:
name = result.get("name") name = result.get("name")
entry_dict = result.copy() 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)) entry_dict = translate_markers(entry.get_cleaned_dict(keep_outdated=False))
new_results.append(entry_dict) new_results.append(entry_dict)
return new_results return new_results
@@ -611,7 +664,9 @@ def clean_outdated(results, resolver, project, dev=False):
for result in results: for result in results:
name = result.get("name") name = result.get("name")
entry_dict = result.copy() 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 # The old entry was editable but this one isnt; prefer the old one
# TODO: Should this be the case for all locking? # TODO: Should this be the case for all locking?
if entry.was_editable and not entry.is_editable: 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]: if name in lockfile[alternate_section]:
lockfile_entry = lockfile[alternate_section][name] lockfile_entry = lockfile[alternate_section][name]
if lockfile_entry and not entry.is_updated: if lockfile_entry and not entry.is_updated:
old_markers = next(iter(m for m in ( old_markers = next(
entry.lockfile_entry.markers, lockfile_entry.get("markers", None) iter(
) if m is not None), None) 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) new_markers = entry_dict.get("markers", None)
if old_markers: if old_markers:
old_markers = Entry.marker_to_str(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): 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.requirementslib.models.requirements import Requirement
from pipenv.vendor.vistir.contextmanagers import cd, temp_path from pipenv.vendor.vistir.contextmanagers import cd, temp_path
from pipenv.utils.indexes import parse_indexes
parsed_packages = [] parsed_packages = []
for package in packages: for package in packages:
*_, line = parse_indexes(package) *_, line = parse_indexes(package)
line = " ".join(line) line = " ".join(line)
pf = dict() pf = {}
req = Requirement.from_line(line) req = Requirement.from_line(line)
if not req.name: if not req.name:
with temp_path(), cd(req.req.setup_info.base_dir): 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): 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.internet import create_mirror_source, replace_pypi_sources
from pipenv.utils.resolver import resolve_deps from pipenv.utils.resolver import resolve_deps
pypi_mirror_source = ( pypi_mirror_source = (
create_mirror_source(os.environ["PIPENV_PYPI_MIRROR"]) create_mirror_source(os.environ["PIPENV_PYPI_MIRROR"])
if "PIPENV_PYPI_MIRROR" in os.environ if "PIPENV_PYPI_MIRROR" in os.environ
@@ -691,10 +756,11 @@ def resolve_packages(pre, clear, verbose, system, write, requirements_dir, packa
sources=sources, sources=sources,
clear=clear, clear=clear,
allow_global=system, allow_global=system,
req_dir=requirements_dir req_dir=requirements_dir,
) )
from pipenv.project import Project from pipenv.project import Project
project = Project() project = Project()
sources = ( sources = (
replace_pypi_sources(project.pipfile_sources, pypi_mirror_source) 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([])) print(json.dumps([]))
def _main(pre, clear, verbose, system, write, requirements_dir, packages, parse_only=False, dev=False): def _main(
os.environ["PIPENV_REQUESTED_PYTHON_VERSION"] = ".".join([str(s) for s in sys.version_info[:3]]) 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) os.environ["PIP_PYTHON_PATH"] = str(sys.executable)
if parse_only: if parse_only:
parse_packages( parse_packages(
@@ -741,7 +819,9 @@ def _main(pre, clear, verbose, system, write, requirements_dir, packages, parse_
requirements_dir=requirements_dir, requirements_dir=requirements_dir,
) )
else: 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): def main(argv=None):
@@ -749,8 +829,10 @@ def main(argv=None):
parsed, remaining = parser.parse_known_args(argv) parsed, remaining = parser.parse_known_args(argv)
_patch_path(pipenv_site=parsed.pipenv_site) _patch_path(pipenv_site=parsed.pipenv_site)
import warnings import warnings
from pipenv.vendor.vistir.compat import ResourceWarning from pipenv.vendor.vistir.compat import ResourceWarning
from pipenv.vendor.vistir.misc import replace_with_text_stream from pipenv.vendor.vistir.misc import replace_with_text_stream
warnings.simplefilter("ignore", category=ResourceWarning) warnings.simplefilter("ignore", category=ResourceWarning)
replace_with_text_stream("stdout") replace_with_text_stream("stdout")
replace_with_text_stream("stderr") replace_with_text_stream("stderr")
@@ -758,9 +840,17 @@ def main(argv=None):
os.environ["PYTHONIOENCODING"] = "utf-8" os.environ["PYTHONIOENCODING"] = "utf-8"
os.environ["PYTHONUNBUFFERED"] = "1" os.environ["PYTHONUNBUFFERED"] = "1"
parsed = handle_parsed_args(parsed) parsed = handle_parsed_args(parsed)
_main(parsed.pre, parsed.clear, parsed.verbose, parsed.system, parsed.write, _main(
parsed.requirements_dir, parsed.packages, parse_only=parsed.parse_only, parsed.pre,
dev=parsed.dev) parsed.clear,
parsed.verbose,
parsed.system,
parsed.write,
parsed.requirements_dir,
parsed.packages,
parse_only=parsed.parse_only,
dev=parsed.dev,
)
if __name__ == "__main__": 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.compat import Path, get_terminal_size
from pipenv.vendor.vistir.contextmanagers import temp_environ from pipenv.vendor.vistir.contextmanagers import temp_environ
ShellDetectionFailure = shellingham.ShellDetectionFailure ShellDetectionFailure = shellingham.ShellDetectionFailure
@@ -49,7 +48,7 @@ def _get_activate_script(cmd, venv):
command = "." command = "."
# Escape any special characters located within the virtualenv path to allow # Escape any special characters located within the virtualenv path to allow
# for proper activation. # 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. # The leading space can make history cleaner in some shells.
return f" {command} {venv_location}/bin/activate{suffix}" return f" {command} {venv_location}/bin/activate{suffix}"
@@ -68,7 +67,7 @@ class Shell:
self.args = [] self.args = []
def __repr__(self): def __repr__(self):
return '{type}(cmd={cmd!r})'.format( return "{type}(cmd={cmd!r})".format(
type=type(self).__name__, type=type(self).__name__,
cmd=self.cmd, cmd=self.cmd,
) )
@@ -149,10 +148,9 @@ class Bash(Shell):
base_rc_src = f'source "{bashrc_path.as_posix()}"\n' base_rc_src = f'source "{bashrc_path.as_posix()}"\n'
rcfile.write(base_rc_src) rcfile.write(base_rc_src)
export_path = 'export PATH="{}:$PATH"\n'.format(":".join( export_path = 'export PATH="{}:$PATH"\n'.format(
self._format_path(python) ":".join(self._format_path(python) for python in _iter_python(venv))
for python in _iter_python(venv) )
))
rcfile.write(export_path) rcfile.write(export_path)
rcfile.flush() rcfile.flush()
self.args.extend(["--rcfile", rcfile.name]) self.args.extend(["--rcfile", rcfile.name])
@@ -165,7 +163,7 @@ class MsysBash(Bash):
if not python.drive: if not python.drive:
return s return s
# Convert "C:/something" to "/c/something". # Convert "C:/something" to "/c/something".
return f'/{s[0].lower()}{s[2:]}' return f"/{s[0].lower()}{s[2:]}"
class CmderEmulatedShell(Shell): class CmderEmulatedShell(Shell):
@@ -207,16 +205,20 @@ SHELL_LOOKUP = collections.defaultdict(
lambda: collections.defaultdict(lambda: Shell), lambda: collections.defaultdict(lambda: Shell),
{ {
"bash": collections.defaultdict( "bash": collections.defaultdict(
lambda: Bash, {"msys": MsysBash}, lambda: Bash,
{"msys": MsysBash},
), ),
"cmd": collections.defaultdict( "cmd": collections.defaultdict(
lambda: Shell, {"cmder": CmderCommandPrompt}, lambda: Shell,
{"cmder": CmderCommandPrompt},
), ),
"powershell": collections.defaultdict( "powershell": collections.defaultdict(
lambda: Shell, {"cmder": CmderPowershell}, lambda: Shell,
{"cmder": CmderPowershell},
), ),
"pwsh": collections.defaultdict( "pwsh": collections.defaultdict(
lambda: Shell, {"cmder": CmderPowershell}, lambda: Shell,
{"cmder": CmderPowershell},
), ),
}, },
) )
+1
View File
@@ -1,2 +1,3 @@
import logging import logging
logging.basicConfig(level=logging.ERROR) logging.basicConfig(level=logging.ERROR)
+21 -19
View File
@@ -1,13 +1,13 @@
import os import os
import re
from contextlib import contextmanager from contextlib import contextmanager
from pathlib import Path from pathlib import Path
from typing import Mapping, Sequence
from typing import Sequence, Mapping
from urllib.parse import urlparse from urllib.parse import urlparse
from packaging.markers import Marker from packaging.markers import Marker
from pipenv import fs_str from pipenv import fs_str
from .constants import SCHEME_LIST, VCS_LIST from .constants import SCHEME_LIST, VCS_LIST
from .shell import temp_path from .shell import temp_path
@@ -80,13 +80,7 @@ def pep423_name(name):
return name return name
def get_vcs_deps( def get_vcs_deps(project=None, dev=False, pypi_mirror=None, packages=None, reqs=None):
project=None,
dev=False,
pypi_mirror=None,
packages=None,
reqs=None
):
from pipenv.vendor.requirementslib.models.requirements import Requirement from pipenv.vendor.requirementslib.models.requirements import Requirement
section = "vcs_dev_packages" if dev else "vcs_packages" section = "vcs_dev_packages" if dev else "vcs_packages"
@@ -112,7 +106,7 @@ def get_vcs_deps(
try: try:
with temp_path(), locked_repository(requirement) as repo: with temp_path(), locked_repository(requirement) as repo:
from pipenv.vendor.requirementslib.models.requirements import ( from pipenv.vendor.requirementslib.models.requirements import (
Requirement Requirement,
) )
# from distutils.sysconfig import get_python_lib # from distutils.sysconfig import get_python_lib
@@ -120,7 +114,7 @@ def get_vcs_deps(
commit_hash = repo.get_commit_hash() commit_hash = repo.get_commit_hash()
name = requirement.normalized_name name = requirement.normalized_name
lockfile[name] = requirement.pipfile_entry[1] lockfile[name] = requirement.pipfile_entry[1]
lockfile[name]['ref'] = commit_hash lockfile[name]["ref"] = commit_hash
result.append(requirement) result.append(requirement)
except OSError: except OSError:
continue continue
@@ -152,7 +146,7 @@ def translate_markers(pipfile_entry):
marker_str = new_pipfile.pop("markers") marker_str = new_pipfile.pop("markers")
if marker_str: if marker_str:
marker = str(Marker(marker_str)) marker = str(Marker(marker_str))
if 'extra' not in marker: if "extra" not in marker:
marker_set.add(marker) marker_set.add(marker)
for m in pipfile_markers: for m in pipfile_markers:
entry = f"{pipfile_entry[m]}" entry = f"{pipfile_entry[m]}"
@@ -160,15 +154,19 @@ def translate_markers(pipfile_entry):
marker_set.add(str(Marker(f"{m} {entry}"))) marker_set.add(str(Marker(f"{m} {entry}")))
new_pipfile.pop(m) new_pipfile.pop(m)
if marker_set: if marker_set:
new_pipfile["markers"] = str(Marker(" or ".join( new_pipfile["markers"] = str(
f"{s}" if " and " in s else s Marker(
for s in sorted(dedup(marker_set)) " or ".join(
))).replace('"', "'") f"{s}" if " and " in s else s for s in sorted(dedup(marker_set))
)
)
).replace('"', "'")
return new_pipfile return new_pipfile
def clean_resolved_dep(dep, is_top_level=False, pipfile_entry=None): def clean_resolved_dep(dep, is_top_level=False, pipfile_entry=None):
from pipenv.vendor.requirementslib.utils import is_vcs from pipenv.vendor.requirementslib.utils import is_vcs
name = pep423_name(dep["name"]) name = pep423_name(dep["name"])
lockfile = {} lockfile = {}
# We use this to determine if there are any markers on top level packages # 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) fs_key = next(iter(k for k in ["path", "file"] if k in dep), None)
pipfile_fs_key = None pipfile_fs_key = None
if pipfile_entry: 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: if fs_key and pipfile_fs_key and fs_key != pipfile_fs_key:
lockfile[pipfile_fs_key] = pipfile_entry[pipfile_fs_key] lockfile[pipfile_fs_key] = pipfile_entry[pipfile_fs_key]
elif fs_key is not None: 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): 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 from pipenv.vendor.requirementslib.models.requirements import Requirement
dependencies = [] dependencies = []
@@ -266,6 +266,7 @@ def convert_deps_to_pip(deps, project=None, r=True, include_index=True):
# Write requirements.txt to tmp directory. # Write requirements.txt to tmp directory.
from pipenv.vendor.vistir.path import create_tracked_tempfile from pipenv.vendor.vistir.path import create_tracked_tempfile
f = create_tracked_tempfile(suffix="-requirements.txt", delete=False) f = create_tracked_tempfile(suffix="-requirements.txt", delete=False)
f.write("\n".join(dependencies).encode("utf-8")) f.write("\n".join(dependencies).encode("utf-8"))
f.close() f.close()
@@ -333,6 +334,7 @@ def is_installable_file(path):
@contextmanager @contextmanager
def locked_repository(requirement): def locked_repository(requirement):
from pipenv.vendor.vistir.path import create_tracked_tempdir from pipenv.vendor.vistir.path import create_tracked_tempdir
if not requirement.is_vcs: if not requirement.is_vcs:
return return
original_base = os.environ.pop("PIP_SHIMS_BASE_MODULE", None) original_base = os.environ.pop("PIP_SHIMS_BASE_MODULE", None)
+20 -22
View File
@@ -1,18 +1,18 @@
import re import re
from urllib3.util import parse_url
from pipenv import environments 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.exceptions import PipenvUsageError
from pipenv.vendor.vistir.compat import Mapping
from .internet import create_mirror_source, is_pypi_url 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): def prepare_pip_source_args(sources, pip_args=None):
if pip_args is 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]) pip_args.extend(["-i", package_url])
# Trust the host if it's not verified. # Trust the host if it's not verified.
if not sources[0].get("verify_ssl", True): 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 "" url_port = f":{url_parts.port}" if url_parts.port else ""
pip_args.extend( pip_args.extend(["--trusted-host", f"{url_parts.host}{url_port}"])
["--trusted-host", f"{url_parts.host}{url_port}"]
)
# Add additional sources as extra indexes. # Add additional sources as extra indexes.
if len(sources) > 1: if len(sources) > 1:
for source in 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]) pip_args.extend(["--extra-index-url", url])
# Trust the host if it's not verified. # Trust the host if it's not verified.
if not source.get("verify_ssl", True): 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 "" url_port = f":{url_parts.port}" if url_parts.port else ""
pip_args.extend( pip_args.extend(["--trusted-host", f"{url_parts.host}{url_port}"])
["--trusted-host", f"{url_parts.host}{url_port}"]
)
return pip_args return pip_args
def get_project_index(project, index=None, trusted_hosts=None): def get_project_index(project, index=None, trusted_hosts=None):
# type: (Optional[Union[str, TSource]], Optional[List[str]], Optional[Project]) -> TSource # type: (Optional[Union[str, TSource]], Optional[List[str]], Optional[Project]) -> TSource
from pipenv.project import SourceNotFound from pipenv.project import SourceNotFound
if trusted_hosts is None: if trusted_hosts is None:
trusted_hosts = [] trusted_hosts = []
if isinstance(index, Mapping): if isinstance(index, Mapping):
@@ -57,7 +54,7 @@ def get_project_index(project, index=None, trusted_hosts=None):
try: try:
source = project.find_source(index) source = project.find_source(index)
except SourceNotFound: except SourceNotFound:
index_url = urllib3_util.parse_url(index) index_url = parse_url(index)
src_name = project.src_name_from_url(index) src_name = project.src_name_from_url(index)
verify_ssl = index_url.host not in trusted_hosts verify_ssl = index_url.host not in trusted_hosts
source = {"url": index, "verify_ssl": verify_ssl, "name": src_name} source = {"url": index, "verify_ssl": verify_ssl, "name": src_name}
@@ -109,8 +106,9 @@ def parse_indexes(line, strict=False):
index = args.index index = args.index
extra_index = args.extra_index extra_index = args.extra_index
trusted_host = args.trusted_host trusted_host = args.trusted_host
if strict and sum( if (
bool(arg) for arg in (index, extra_index, trusted_host, remainder) strict
) > 1: and sum(bool(arg) for arg in (index, extra_index, trusted_host, remainder)) > 1
):
raise ValueError("Index arguments must be on their own lines.") raise ValueError("Index arguments must be on their own lines.")
return index, extra_index, trusted_host, remainder return index, extra_index, trusted_host, remainder
+3 -4
View File
@@ -1,5 +1,6 @@
import re import re
from urllib.parse import urlparse from urllib.parse import urlparse
from urllib3 import util as urllib3_util from urllib3 import util as urllib3_util
from pipenv.vendor import parse 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 :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) 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): 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 f"https://pypi.org/pypi/{package_name}/json", timeout=0.3, stream=True
) )
if not r.ok: if not r.ok:
raise OSError( raise OSError(f"Unable to find package {package_name} in PyPI repository.")
f"Unable to find package {package_name} in PyPI repository."
)
r = parse.parse("https://pypi.org/pypi/{name}/json", r.url) r = parse.parse("https://pypi.org/pypi/{name}/json", r.url)
good_name = r["name"] good_name = r["name"]
+11 -10
View File
@@ -1,6 +1,6 @@
from typing import Mapping 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): 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 # it now for development purposes
# TODO: Is this implementation clear? How can it be improved? # TODO: Is this implementation clear? How can it be improved?
entry = None entry = None
cleaner_kwargs = { cleaner_kwargs = {"is_top_level": False, "pipfile_entry": None}
"is_top_level": False,
"pipfile_entry": None
}
if isinstance(dep, Mapping) and dep.get("name", ""): if isinstance(dep, Mapping) and dep.get("name", ""):
dep_name = pep423_name(dep["name"]) dep_name = pep423_name(dep["name"])
name = next(iter( name = next(
k for k in pipfile_section.keys() iter(k for k in pipfile_section.keys() if pep423_name(k) == dep_name), None
if pep423_name(k) == dep_name )
), None)
entry = pipfile_section[name] if name else None entry = pipfile_section[name] if name else None
if entry: 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_name, lockfile_dict = lockfile_entry.copy().popitem()
lockfile_version = lockfile_dict.get("version", "") lockfile_version = lockfile_dict.get("version", "")
# Keep pins from the lockfile # 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_dict["version"] = version
lockfile_entry[lockfile_name] = lockfile_dict lockfile_entry[lockfile_name] = lockfile_dict
return lockfile_entry return lockfile_entry
+19 -15
View File
@@ -4,11 +4,11 @@ import subprocess
import crayons import crayons
from click import echo as click_echo from click import echo as click_echo
from pipenv.exceptions import PipenvCmdError
from pipenv import environments from pipenv import environments
from pipenv.exceptions import PipenvCmdError
if environments.MYPY_RUNNING: if environments.MYPY_RUNNING:
from typing import Tuple from typing import Tuple # noqa
def run_command(cmd, *args, is_verbose=False, **kwargs): 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._compat import decode_for_output
from pipenv.cmdparse import Script from pipenv.cmdparse import Script
catch_exceptions = kwargs.pop("catch_exceptions", True) catch_exceptions = kwargs.pop("catch_exceptions", True)
if isinstance(cmd, ((str,), list, tuple)): if isinstance(cmd, ((str,), list, tuple)):
cmd = Script.parse(cmd) cmd = Script.parse(cmd)
@@ -38,17 +39,24 @@ def run_command(cmd, *args, is_verbose=False, **kwargs):
click_echo(f"Running command: $ {cmd.cmdify()}") click_echo(f"Running command: $ {cmd.cmdify()}")
c = subprocess_run(command, *args, **kwargs) c = subprocess_run(command, *args, **kwargs)
if is_verbose: if is_verbose:
click_echo("Command output: {}".format( click_echo(
crayons.cyan(decode_for_output(c.stdout)) "Command output: {}".format(crayons.cyan(decode_for_output(c.stdout))),
), err=True) err=True,
)
if c.returncode and catch_exceptions: if c.returncode and catch_exceptions:
raise PipenvCmdError(cmd.cmdify(), c.stdout, c.stderr, c.returncode) raise PipenvCmdError(cmd.cmdify(), c.stdout, c.stderr, c.returncode)
return c return c
def subprocess_run( def subprocess_run(
args, *, block=True, text=True, capture_output=True, args,
encoding="utf-8", env=None, **other_kwargs *,
block=True,
text=True,
capture_output=True,
encoding="utf-8",
env=None,
**other_kwargs,
): ):
"""A backward compatible version of subprocess.run(). """A backward compatible version of subprocess.run().
@@ -61,17 +69,13 @@ def subprocess_run(
_env.update(env) _env.update(env)
other_kwargs["env"] = _env other_kwargs["env"] = _env
if capture_output: if capture_output:
other_kwargs['stdout'] = subprocess.PIPE other_kwargs["stdout"] = subprocess.PIPE
other_kwargs['stderr'] = subprocess.PIPE other_kwargs["stderr"] = subprocess.PIPE
if block: if block:
return subprocess.run( return subprocess.run(
args, universal_newlines=text, args, universal_newlines=text, encoding=encoding, **other_kwargs
encoding=encoding, **other_kwargs
) )
else: else:
return subprocess.Popen( return subprocess.Popen(
args, universal_newlines=text, args, universal_newlines=text, encoding=encoding, **other_kwargs
encoding=encoding, **other_kwargs
) )
+204 -97
View File
@@ -7,27 +7,36 @@ import warnings
from functools import lru_cache from functools import lru_cache
import crayons import crayons
from pipenv import environments
from click import echo as click_echo 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, \ from pipenv import environments
is_pinned_requirement, convert_deps_to_pip, HackedPythonVersion 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 .indexes import parse_indexes, prepare_pip_source_args
from .internet import _get_requests_session from .internet import _get_requests_session
from .locking import format_requirement_for_lockfile, prepare_lockfile 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 from .spinner import create_spinner
if environments.MYPY_RUNNING:
from typing import Any, Dict, List, Optional, Tuple, Union
from pipenv.project import Project if environments.MYPY_RUNNING:
from pipenv.vendor.requirementslib.models.pipfile import Pipfile from typing import Any, Dict, List, Optional, Set, Tuple, Union # noqa
from pipenv.vendor.requirementslib.models.requirements import (
Line, Requirement 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: class HashCacheMixin:
@@ -38,6 +47,7 @@ class HashCacheMixin:
cache key includes the hash value returned from the server). This ought to cache key includes the hash value returned from the server). This ought to
avoid issues where the location on the server changes. avoid issues where the location on the server changes.
""" """
def __init__(self, directory, session): def __init__(self, directory, session):
self.session = session self.session = session
if not os.path.isdir(directory): if not os.path.isdir(directory):
@@ -65,8 +75,16 @@ class HashCacheMixin:
class Resolver: class Resolver:
def __init__( def __init__(
self, constraints, req_dir, project, sources, index_lookup=None, self,
markers_lookup=None, skipped=None, clear=False, pre=False constraints,
req_dir,
project,
sources,
index_lookup=None,
markers_lookup=None,
skipped=None,
clear=False,
pre=False,
): ):
self.initial_constraints = constraints self.initial_constraints = constraints
self.req_dir = req_dir self.req_dir = req_dir
@@ -113,9 +131,9 @@ class Resolver:
from pipenv.vendor.pip_shims import shims from pipenv.vendor.pip_shims import shims
if not self._hash_cache: if not self._hash_cache:
self._hash_cache = type("HashCache", (HashCacheMixin, shims.SafeFileCache), {})( self._hash_cache = type(
os.path.join(self.project.s.PIPENV_CACHE_DIR, "hashes"), self.session "HashCache", (HashCacheMixin, shims.SafeFileCache), {}
) )(os.path.join(self.project.s.PIPENV_CACHE_DIR, "hashes"), self.session)
return self._hash_cache return self._hash_cache
@classmethod @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]] # type: (...) -> Tuple[Set[str], Dict[str, Dict[str, Union[str, bool, List[str]]]], Dict[str, str], Dict[str, str]]
constraints = set() # type: Set[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: if index_lookup is None:
index_lookup = {} index_lookup = {}
if markers_lookup is None: if markers_lookup is None:
markers_lookup = {} markers_lookup = {}
if not req_dir: if not req_dir:
from pipenv.vendor.vistir.path import create_tracked_tempdir
req_dir = create_tracked_tempdir(prefix="pipenv-", suffix="-reqdir") req_dir = create_tracked_tempdir(prefix="pipenv-", suffix="-reqdir")
transient_resolver = cls( 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: for dep in deps:
if not dep: if not dep:
continue continue
req, req_idx, markers_idx = cls.parse_line( 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) index_lookup.update(req_idx)
markers_lookup.update(markers_idx) markers_lookup.update(markers_idx)
@@ -158,12 +184,20 @@ class Resolver:
# to the lockfile directly # to the lockfile directly
use_sources = None use_sources = None
if req.name in index_lookup: 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: if not use_sources:
use_sources = sources use_sources = sources
transient_resolver = cls( 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( constraint_update, lockfile_update = cls.get_deps_from_req(
req, resolver=transient_resolver, resolve_vcs=project.s.PIPENV_RESOLVE_VCS req, resolver=transient_resolver, resolve_vcs=project.s.PIPENV_RESOLVE_VCS
@@ -178,17 +212,17 @@ class Resolver:
line, # type: str line, # type: str
index_lookup=None, # type: Dict[str, str] index_lookup=None, # type: Dict[str, str]
markers_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]] # 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: if index_lookup is None:
index_lookup = {} index_lookup = {}
if markers_lookup is None: if markers_lookup is None:
markers_lookup = {} markers_lookup = {}
if project is None: if project is None:
from pipenv.project import Project from pipenv.project import Project
project = Project() project = Project()
index, extra_index, trust_host, remainder = parse_indexes(line) index, extra_index, trust_host, remainder = parse_indexes(line)
line = " ".join(remainder) line = " ".join(remainder)
@@ -202,13 +236,18 @@ class Resolver:
try: try:
req = Requirement.from_line(line) req = Requirement.from_line(line)
except ValueError: 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: 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: if index:
try: try:
index_lookup[req.normalized_name] = project.get_source( index_lookup[req.normalized_name] = project.get_source(
url=index, refresh=True).get("name") url=index, refresh=True
).get("name")
except TypeError: except TypeError:
pass pass
try: try:
@@ -227,13 +266,13 @@ class Resolver:
# type: (Requirement, Optional["Resolver"], bool) -> Tuple[Set[str], Dict[str, Dict[str, Union[str, bool, List[str]]]]] # 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.requirements import Requirement
from pipenv.vendor.requirementslib.models.utils import ( 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 from pipenv.vendor.requirementslib.utils import is_installable_dir
# TODO: this is way too complex, refactor this # TODO: this is way too complex, refactor this
constraints = set() # type: Set[str] 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: 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: # for local packages with setup.py files and potential direct url deps:
if req.is_vcs: if req.is_vcs:
@@ -243,7 +282,6 @@ class Resolver:
else: else:
_, entry = req.pipfile_entry _, entry = req.pipfile_entry
parsed_line = req.req.parsed_line # type: Line parsed_line = req.req.parsed_line # type: Line
setup_info = None # type: Any
try: try:
name = req.normalized_name name = req.normalized_name
except TypeError: except TypeError:
@@ -255,8 +293,11 @@ class Resolver:
# Allow users to toggle resolution off for non-editable VCS packages # Allow users to toggle resolution off for non-editable VCS packages
# but leave it on for local, installable folders on the filesystem # but leave it on for local, installable folders on the filesystem
if resolve_vcs or ( if resolve_vcs or (
req.editable or parsed_line.is_wheel or ( req.editable
req.is_file_or_url and parsed_line.is_local or parsed_line.is_wheel
or (
req.is_file_or_url
and parsed_line.is_local
and is_installable_dir(parsed_line.path) and is_installable_dir(parsed_line.path)
) )
): ):
@@ -271,9 +312,7 @@ class Resolver:
if r.marker and not r.marker.evaluate(): if r.marker and not r.marker.evaluate():
new_constraints = {} new_constraints = {}
_, new_entry = req.pipfile_entry _, new_entry = req.pipfile_entry
new_lock = { new_lock = {pep423_name(new_req.normalized_name): new_entry}
pep423_name(new_req.normalized_name): new_entry
}
else: else:
new_constraints, new_lock = cls.get_deps_from_req( new_constraints, new_lock = cls.get_deps_from_req(
new_req, resolver new_req, resolver
@@ -298,14 +337,22 @@ class Resolver:
else: else:
# if the dependency isn't installable, don't add it to constraints # if the dependency isn't installable, don't add it to constraints
# and instead add it directly to the lock # and instead add it directly to the lock
if req and req.requirement and ( if (
req.requirement.marker and not req.requirement.marker.evaluate() req
and req.requirement
and (req.requirement.marker and not req.requirement.marker.evaluate())
): ):
pypi = resolver.finder if resolver else None pypi = resolver.finder if resolver else None
ireq = req.ireq 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: 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 [] hashes = resolver.collect_hashes(ireq) if resolver else []
new_req = Requirement.from_ireq(ireq) new_req = Requirement.from_ireq(ireq)
new_req = new_req.add_hashes(hashes) new_req = new_req.add_hashes(hashes)
@@ -314,13 +361,13 @@ class Resolver:
click_echo( click_echo(
"{} doesn't match your environment, " "{} doesn't match your environment, "
"its dependencies won't be resolved.".format(req.as_line()), "its dependencies won't be resolved.".format(req.as_line()),
err=True err=True,
) )
else: else:
click_echo( click_echo(
"Could not find a version of {} that matches your environment, " "Could not find a version of {} that matches your environment, "
"it will be skipped.".format(req.as_line()), "it will be skipped.".format(req.as_line()),
err=True err=True,
) )
return constraints, locked_deps return constraints, locked_deps
constraints.add(req.constraint_line) constraints.add(req.constraint_line)
@@ -337,10 +384,10 @@ class Resolver:
sources=None, # type: List[str] sources=None, # type: List[str]
req_dir=None, # type: str req_dir=None, # type: str
clear=False, # type: bool clear=False, # type: bool
pre=False # type: bool pre=False, # type: bool
): ):
# type: (...) -> "Resolver" # type: (...) -> "Resolver"
from pipenv.vendor.vistir.path import create_tracked_tempdir
if not req_dir: if not req_dir:
req_dir = create_tracked_tempdir(suffix="-requirements", prefix="pipenv-") req_dir = create_tracked_tempdir(suffix="-requirements", prefix="pipenv-")
if index_lookup is None: if index_lookup is None:
@@ -350,18 +397,31 @@ class Resolver:
if sources is None: if sources is None:
sources = project.sources sources = project.sources
constraints, skipped, index_lookup, markers_lookup = cls.get_metadata( constraints, skipped, index_lookup, markers_lookup = cls.get_metadata(
deps, index_lookup, markers_lookup, project, sources, req_dir=req_dir, deps,
pre=pre, clear=clear index_lookup,
markers_lookup,
project,
sources,
req_dir=req_dir,
pre=pre,
clear=clear,
) )
return Resolver( return Resolver(
constraints, req_dir, project, sources, index_lookup=index_lookup, constraints,
markers_lookup=markers_lookup, skipped=skipped, clear=clear, pre=pre req_dir,
project,
sources,
index_lookup=index_lookup,
markers_lookup=markers_lookup,
skipped=skipped,
clear=clear,
pre=pre,
) )
@classmethod @classmethod
def from_pipfile(cls, project, pipfile=None, dev=False, pre=False, clear=False): def from_pipfile(cls, project, pipfile=None, dev=False, pre=False, clear=False):
# type: (Optional[Project], Optional[Pipfile], bool, bool, bool) -> "Resolver" # type: (Optional[Project], Optional[Pipfile], bool, bool, bool) -> "Resolver"
from pipenv.vendor.vistir.path import create_tracked_tempdir
if not pipfile: if not pipfile:
pipfile = project._pipfile pipfile = project._pipfile
req_dir = create_tracked_tempdir(suffix="-requirements", prefix="pipenv-") 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.dev_packages})
deps.update({req.as_line() for req in pipfile.packages}) deps.update({req.as_line() for req in pipfile.packages})
constraints, skipped, index_lookup, markers_lookup = cls.get_metadata( constraints, skipped, index_lookup, markers_lookup = cls.get_metadata(
list(deps), index_lookup, markers_lookup, project, project.sources, list(deps),
req_dir=req_dir, pre=pre, clear=clear index_lookup,
markers_lookup,
project,
project.sources,
req_dir=req_dir,
pre=pre,
clear=clear,
) )
return Resolver( return Resolver(
constraints, req_dir, project, project.sources, index_lookup=index_lookup, constraints,
markers_lookup=markers_lookup, skipped=skipped, clear=clear, pre=pre req_dir,
project,
project.sources,
index_lookup=index_lookup,
markers_lookup=markers_lookup,
skipped=skipped,
clear=clear,
pre=pre,
) )
@property @property
@@ -410,6 +483,7 @@ class Resolver:
def prepare_constraint_file(self): def prepare_constraint_file(self):
from pipenv.vendor.vistir.path import create_tracked_tempfile from pipenv.vendor.vistir.path import create_tracked_tempfile
constraints_file = create_tracked_tempfile( constraints_file = create_tracked_tempfile(
mode="w", mode="w",
prefix="pipenv-", prefix="pipenv-",
@@ -419,7 +493,8 @@ class Resolver:
) )
skip_args = ("build-isolation", "use-pep517", "cache-dir") skip_args = ("build-isolation", "use-pep517", "cache-dir")
args_to_add = [ 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 not any(bad_arg in arg for bad_arg in skip_args)
] ]
if self.sources: if self.sources:
@@ -446,7 +521,9 @@ class Resolver:
pip_options.no_input = True pip_options.no_input = True
pip_options.progress_bar = "off" pip_options.progress_bar = "off"
pip_options.ignore_requires_python = True 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 self._pip_options = pip_options
return self._pip_options return self._pip_options
@@ -459,16 +536,17 @@ class Resolver:
@property @property
def finder(self): def finder(self):
from pipenv.vendor.pip_shims import shims from pipenv.vendor.pip_shims import shims
if self._finder is None: if self._finder is None:
self._finder = shims.get_package_finder( self._finder = shims.get_package_finder(
install_cmd=self.pip_command, install_cmd=self.pip_command,
options=self.pip_options, options=self.pip_options,
session=self.session session=self.session,
) )
index_mapping = {} index_mapping = {}
for source in self.sources: for source in self.sources:
if source.get('name'): if source.get("name"):
index_mapping[source['name']] = source['url'] index_mapping[source["name"]] = source["url"]
alt_index_lookup = {} alt_index_lookup = {}
for req_name, index in self.index_lookup.items(): for req_name, index in self.index_lookup.items():
if index_mapping.get(index): if index_mapping.get(index):
@@ -480,6 +558,7 @@ class Resolver:
@property @property
def ignore_compatibility_finder(self): def ignore_compatibility_finder(self):
from pipenv.vendor.pip_shims import shims from pipenv.vendor.pip_shims import shims
if self._ignore_compatibility_finder is None: if self._ignore_compatibility_finder is None:
ignore_compatibility_finder = shims.get_package_finder( ignore_compatibility_finder = shims.get_package_finder(
install_cmd=self.pip_command, install_cmd=self.pip_command,
@@ -501,20 +580,26 @@ class Resolver:
pip_options.extra_index_urls = [] pip_options.extra_index_urls = []
if self._parsed_constraints is None: if self._parsed_constraints is None:
self._parsed_constraints = shims.parse_requirements( self._parsed_constraints = shims.parse_requirements(
self.constraint_file, finder=self.finder, session=self.session, self.constraint_file,
options=pip_options finder=self.finder,
session=self.session,
options=pip_options,
) )
return self._parsed_constraints return self._parsed_constraints
@property @property
def constraints(self): 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: if self._constraints is None:
self._constraints = [ self._constraints = [
install_req_from_parsed_requirement( install_req_from_parsed_requirement(
c, isolated=self.pip_options.build_isolation, c,
use_pep517=self.pip_options.use_pep517, user_supplied=True isolated=self.pip_options.build_isolation,
use_pep517=self.pip_options.use_pep517,
user_supplied=True,
) )
for c in self.parsed_constraints for c in self.parsed_constraints
] ]
@@ -523,10 +608,14 @@ class Resolver:
@contextlib.contextmanager @contextlib.contextmanager
def get_resolver(self, clear=False): def get_resolver(self, clear=False):
from pipenv.vendor.pip_shims.shims import ( 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 pip_options = self.pip_options
finder = self.finder finder = self.finder
wheel_cache = WheelCache(pip_options.cache_dir, pip_options.format_control) wheel_cache = WheelCache(pip_options.cache_dir, pip_options.format_control)
@@ -554,8 +643,8 @@ class Resolver:
yield resolver yield resolver
def resolve(self): def resolve(self):
from pipenv.vendor.pip_shims.shims import InstallationError
from pipenv.exceptions import ResolutionFailure 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 self.constraints # For some reason it is important to evaluate constraints before resolver context
with temp_environ(), self.get_resolver() as resolver: with temp_environ(), self.get_resolver() as resolver:
@@ -570,12 +659,15 @@ class Resolver:
def resolve_constraints(self): def resolve_constraints(self):
from pipenv.vendor.requirementslib.models.markers import marker_from_specifier from pipenv.vendor.requirementslib.models.markers import marker_from_specifier
new_tree = set() new_tree = set()
for result in self.resolved_tree: for result in self.resolved_tree:
if result.markers: if result.markers:
self.markers[result.name] = result.markers self.markers[result.name] = result.markers
else: 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: if candidate:
requires_python = candidate.link.requires_python requires_python = candidate.link.requires_python
if requires_python: if requires_python:
@@ -625,7 +717,8 @@ class Resolver:
click_echo( click_echo(
"{}: Error generating hash for {}".format( "{}: Error generating hash for {}".format(
crayons.red("Warning", bold=True), ireq.name crayons.red("Warning", bold=True), ireq.name
), err=True ),
err=True,
) )
return None return None
@@ -688,13 +781,12 @@ class Resolver:
return req.name, entry return req.name, entry
def clean_results(self): def clean_results(self):
from pipenv.vendor.requirementslib.models.requirements import ( from pipenv.vendor.requirementslib.models.requirements import Requirement
Requirement
)
reqs = [(Requirement.from_ireq(ireq), ireq) for ireq in self.resolved_tree] reqs = [(Requirement.from_ireq(ireq), ireq) for ireq in self.resolved_tree]
results = {} results = {}
for req, ireq in reqs: 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 continue
elif req.normalized_name in self.skipped.keys(): elif req.normalized_name in self.skipped.keys():
continue continue
@@ -723,8 +815,14 @@ class Resolver:
def _show_warning(message, category, filename, lineno, line): def _show_warning(message, category, filename, lineno, line):
warnings.showwarning(message=message, category=category, filename=filename, warnings.showwarning(
lineno=lineno, file=sys.stderr, line=line) message=message,
category=category,
filename=filename,
lineno=lineno,
file=sys.stderr,
line=line,
)
sys.stderr.flush() sys.stderr.flush()
@@ -738,8 +836,6 @@ def actually_resolve_deps(
pre, pre,
req_dir=None, req_dir=None,
): ):
from pipenv.vendor.vistir.path import create_tracked_tempdir
if not req_dir: if not req_dir:
req_dir = create_tracked_tempdir(suffix="-requirements", prefix="pipenv-") req_dir = create_tracked_tempdir(suffix="-requirements", prefix="pipenv-")
warning_list = [] warning_list = []
@@ -753,8 +849,13 @@ def actually_resolve_deps(
resolver.resolve_constraints() resolver.resolve_constraints()
results = resolver.clean_results() results = resolver.clean_results()
for warning in warning_list: for warning in warning_list:
_show_warning(warning.message, warning.category, warning.filename, warning.lineno, _show_warning(
warning.line) warning.message,
warning.category,
warning.filename,
warning.lineno,
warning.line,
)
return (results, hashes, resolver.markers_lookup, resolver, resolver.skipped) 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._compat import decode_output
from pipenv.cmdparse import Script from pipenv.cmdparse import Script
from pipenv.vendor.vistir.misc import echo from pipenv.vendor.vistir.misc import echo
c = subprocess_run(Script.parse(cmd).cmd_args, block=False, env=os.environ.copy()) c = subprocess_run(Script.parse(cmd).cmd_args, block=False, env=os.environ.copy())
is_verbose = project.s.is_verbose() is_verbose = project.s.is_verbose()
err = "" err = ""
@@ -777,9 +879,7 @@ def resolve(cmd, sp, project):
returncode = c.poll() returncode = c.poll()
out = c.stdout.read() out = c.stdout.read()
if returncode != 0: if returncode != 0:
sp.red.fail(environments.PIPENV_SPINNER_FAIL_TEXT.format( sp.red.fail(environments.PIPENV_SPINNER_FAIL_TEXT.format("Locking Failed!"))
"Locking Failed!"
))
echo(out.strip(), err=True) echo(out.strip(), err=True)
if not is_verbose: if not is_verbose:
echo(err, err=True) echo(err, err=True)
@@ -800,7 +900,7 @@ def venv_resolve_deps(
dev=False, dev=False,
pipfile=None, pipfile=None,
lockfile=None, lockfile=None,
keep_outdated=False keep_outdated=False,
): ):
""" """
Resolve dependencies for a pipenv project, acts as a portal to the target environment. 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._compat import decode_for_output
from pipenv.vendor.vistir.compat import JSONDecodeError, NamedTemporaryFile, Path from pipenv.vendor.vistir.compat import JSONDecodeError, NamedTemporaryFile, Path
from pipenv.vendor.vistir.misc import fs_str from pipenv.vendor.vistir.misc import fs_str
from pipenv.vendor.vistir.path import create_tracked_tempdir
results = [] results = []
pipfile_section = "dev-packages" if dev else "packages" 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") req_dir = create_tracked_tempdir(prefix="pipenv", suffix="requirements")
cmd = [ cmd = [
which("python", allow_global=allow_global), which("python", allow_global=allow_global),
Path(resolver.__file__.rstrip("co")).as_posix() Path(resolver.__file__.rstrip("co")).as_posix(),
] ]
if pre: if pre:
cmd.append("--pre") cmd.append("--pre")
@@ -879,15 +978,15 @@ def venv_resolve_deps(
os.environ.pop("PIPENV_SITE_DIR", None) os.environ.pop("PIPENV_SITE_DIR", None)
if keep_outdated: if keep_outdated:
os.environ["PIPENV_KEEP_OUTDATED"] = fs_str("1") 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 # This conversion is somewhat slow on local and file-type requirements since
# we now download those requirements / make temporary folders to perform # we now download those requirements / make temporary folders to perform
# dependency resolution on them, so we are including this step inside the # dependency resolution on them, so we are including this step inside the
# spinner context manager for the UX improvement # spinner context manager for the UX improvement
sp.write(decode_for_output("Building requirements...")) sp.write(decode_for_output("Building requirements..."))
deps = convert_deps_to_pip( deps = convert_deps_to_pip(deps, project, r=False, include_index=True)
deps, project, r=False, include_index=True
)
constraints = set(deps) constraints = set(deps)
os.environ["PIPENV_PACKAGES"] = str("\n".join(constraints)) os.environ["PIPENV_PACKAGES"] = str("\n".join(constraints))
sp.write(decode_for_output("Resolving dependencies...")) 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(): if not project.s.is_verbose() and c.stderr.strip():
click_echo(crayons.yellow(f"Warning: {c.stderr.strip()}"), err=True) click_echo(crayons.yellow(f"Warning: {c.stderr.strip()}"), err=True)
else: 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"Output: {c.stdout.strip()}", err=True)
click_echo(f"Error: {c.stderr.strip()}", err=True) click_echo(f"Error: {c.stderr.strip()}", err=True)
try: try:
@@ -926,7 +1027,7 @@ def resolve_deps(
clear=False, clear=False,
pre=False, pre=False,
allow_global=False, allow_global=False,
req_dir=None req_dir=None,
): ):
"""Given a list of dependencies, return a resolved list of dependencies, """Given a list of dependencies, return a resolved list of dependencies,
using pip-tools -- and their hashes, using the warehouse API / pip. using pip-tools -- and their hashes, using the warehouse API / pip.
@@ -944,7 +1045,6 @@ def resolve_deps(
# First (proper) attempt: # First (proper) attempt:
req_dir = req_dir if req_dir else os.environ.get("req_dir", None) req_dir = req_dir if req_dir else os.environ.get("req_dir", None)
if not req_dir: if not req_dir:
from pipenv.vendor.vistir.path import create_tracked_tempdir
req_dir = create_tracked_tempdir(prefix="pipenv-", suffix="-requirements") req_dir = create_tracked_tempdir(prefix="pipenv-", suffix="-requirements")
with HackedPythonVersion(python_version=python, python_path=python_path): with HackedPythonVersion(python_version=python, python_path=python_path):
try: try:
@@ -970,7 +1070,13 @@ def resolve_deps(
try: try:
# Attempt to resolve again, with different Python version information, # Attempt to resolve again, with different Python version information,
# particularly for particularly particular packages. # particularly for particularly particular packages.
results, hashes, markers_lookup, resolver, skipped = actually_resolve_deps( (
results,
hashes,
markers_lookup,
resolver,
skipped,
) = actually_resolve_deps(
deps, deps,
index_lookup, index_lookup,
markers_lookup, markers_lookup,
@@ -989,6 +1095,7 @@ def resolve_deps(
def get_pipenv_sitedir(): def get_pipenv_sitedir():
# type: () -> Optional[str] # type: () -> Optional[str]
import pkg_resources import pkg_resources
site_dir = next( site_dir = next(
iter(d for d in pkg_resources.working_set if d.key.lower() == "pipenv"), None 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 errno
import os import os
import posixpath
import re import re
import shlex import shlex
import shutil import shutil
@@ -8,17 +9,16 @@ import sys
import warnings import warnings
from contextlib import contextmanager from contextlib import contextmanager
from functools import lru_cache from functools import lru_cache
import posixpath
from pathlib import Path from pathlib import Path
from .constants import SCHEME_LIST
from .processes import subprocess_run
from pipenv import environments from pipenv import environments
from pipenv.vendor.vistir.compat import ResourceWarning from pipenv.vendor.vistir.compat import ResourceWarning
from .constants import SCHEME_LIST
from .processes import subprocess_run
if environments.MYPY_RUNNING: if environments.MYPY_RUNNING:
from typing import Text from typing import Text # noqa
@lru_cache() @lru_cache()
@@ -51,6 +51,7 @@ def make_posix(path):
def get_pipenv_dist(pkg="pipenv", pipenv_site=None): def get_pipenv_dist(pkg="pipenv", pipenv_site=None):
from pipenv.resolver import find_site_path from pipenv.resolver import find_site_path
pipenv_libdir = os.path.dirname(os.path.abspath(__file__)) pipenv_libdir = os.path.dirname(os.path.abspath(__file__))
if pipenv_site is None: if pipenv_site is None:
pipenv_site = os.path.dirname(pipenv_libdir) pipenv_site = os.path.dirname(pipenv_libdir)
@@ -80,8 +81,8 @@ def looks_like_dir(path):
def load_path(python): def load_path(python):
import json import json
from pathlib import Path from pathlib import Path
python = Path(python).as_posix() python = Path(python).as_posix()
json_dump_commmand = '"import json, sys; print(json.dumps(sys.path));"' json_dump_commmand = '"import json, sys; print(json.dumps(sys.path));"'
c = subprocess_run([python, "-c", json_dump_commmand]) c = subprocess_run([python, "-c", json_dump_commmand])
@@ -96,9 +97,9 @@ def path_to_url(path):
def normalize_path(path): def normalize_path(path):
return os.path.expandvars(os.path.expanduser( return os.path.expandvars(
os.path.normcase(os.path.normpath(os.path.abspath(str(path)))) os.path.expanduser(os.path.normcase(os.path.normpath(os.path.abspath(str(path)))))
)) )
def get_windows_path(*args): def get_windows_path(*args):
@@ -157,7 +158,7 @@ def walk_up(bottom):
def find_requirements(max_depth=3): def find_requirements(max_depth=3):
"""Returns the path of a requirements.txt file in parent directories.""" """Returns the path of a requirements.txt file in parent directories."""
i = 0 i = 0
for c, d, f in walk_up(os.getcwd()): for c, _, _ in walk_up(os.getcwd()):
i += 1 i += 1
if i < max_depth: if i < max_depth:
r = os.path.join(c, "requirements.txt") r = os.path.join(c, "requirements.txt")
@@ -183,13 +184,12 @@ def temp_environ():
def escape_cmd(cmd): def escape_cmd(cmd):
if any(special_char in cmd for special_char in ["<", ">", "&", ".", "^", "|", "?"]): if any(special_char in cmd for special_char in ["<", ">", "&", ".", "^", "|", "?"]):
cmd = f'\"{cmd}\"' cmd = f'"{cmd}"'
return cmd return cmd
def safe_expandvars(value): 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): if isinstance(value, str):
return os.path.expandvars(value) return os.path.expandvars(value)
return value return value
@@ -239,8 +239,8 @@ def is_virtual_environment(path):
""" """
if not path.is_dir(): if not path.is_dir():
return False return False
for bindir_name in ('bin', 'Scripts'): for bindir_name in ("bin", "Scripts"):
for python in path.joinpath(bindir_name).glob('python*'): for python in path.joinpath(bindir_name).glob("python*"):
try: try:
exeness = python.is_file() and os.access(str(python), os.X_OK) exeness = python.is_file() and os.access(str(python), os.X_OK)
except OSError: except OSError:
@@ -252,10 +252,17 @@ def is_virtual_environment(path):
def mkdir_p(newdir): def mkdir_p(newdir):
"""works the way a good mkdir should :) """works the way a good mkdir should :)
<<<<<<< HEAD
- already exists, silently complete - already exists, silently complete
- regular file in the way, raise an exception - regular file in the way, raise an exception
- parent directory(ies) does not exist, make them as well - parent directory(ies) does not exist, make them as well
From: http://code.activestate.com/recipes/82465-a-friendly-mkdir/ 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): if os.path.isdir(newdir):
pass pass
@@ -295,19 +302,18 @@ def find_python(finder, line=None):
""" """
if line and not isinstance(line, str): if line and not isinstance(line, str):
raise TypeError( raise TypeError(f"Invalid python search type: expected string, received {line!r}")
f"Invalid python search type: expected string, received {line!r}"
)
if line and os.path.isabs(line): if line and os.path.isabs(line):
if os.name == "nt": if os.name == "nt":
line = make_posix(line) line = make_posix(line)
return line return line
if not finder: if not finder:
from pipenv.vendor.pythonfinder import Finder from pipenv.vendor.pythonfinder import Finder
finder = Finder(global_search=True) finder = Finder(global_search=True)
if not line: if not line:
result = next(iter(finder.find_all_python_versions()), None) 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) result = finder.find_python_version(line)
else: else:
result = finder.find_python_version(name=line) 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}") raise TypeError(f"Not a valid command to check: {line!r}")
from pipenv.vendor.pythonfinder.utils import PYTHON_IMPLEMENTATIONS from pipenv.vendor.pythonfinder.utils import PYTHON_IMPLEMENTATIONS
is_version = re.match(r'\d+(\.\d+)*', line)
if (line.startswith("python") or is_version is_version = re.match(r"\d+(\.\d+)*", line)
or any(line.startswith(v) for v in PYTHON_IMPLEMENTATIONS)): if (
line.startswith("python")
or is_version
or any(line.startswith(v) for v in PYTHON_IMPLEMENTATIONS)
):
return True return True
# we are less sure about this but we can guess # we are less sure about this but we can guess
if line.startswith("py"): 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 Windows source repo folders are read-only by default, so this error handler
attempts to set them as writeable and then proceed with deletion.""" attempts to set them as writeable and then proceed with deletion."""
# Check for read-only attribute # Check for read-only attribute
default_warning_message = ( default_warning_message = "Unable to remove file due to permissions restriction: {!r}"
"Unable to remove file due to permissions restriction: {!r}"
)
# split the initial exception out into its type, exception, and traceback # split the initial exception out into its type, exception, and traceback
exc_type, exc_exception, exc_tb = exc exc_type, exc_exception, exc_tb = exc
if is_readonly_path(path): 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): def create_spinner(text, setting, nospin=None, spinner_name=None):
from pipenv.vendor.vistir import spin from pipenv.vendor.vistir import spin
from pipenv.vendor.vistir.misc import fs_str from pipenv.vendor.vistir.misc import fs_str
if not spinner_name: if not spinner_name:
spinner_name = setting.PIPENV_SPINNER spinner_name = setting.PIPENV_SPINNER
if nospin is None: if nospin is None:
@@ -12,6 +13,7 @@ def create_spinner(text, setting, nospin=None, spinner_name=None):
with spin.create_spinner( with spin.create_spinner(
spinner_name=spinner_name, spinner_name=spinner_name,
start_text=fs_str(text), start_text=fs_str(text),
nospin=nospin, write_to_stdout=False nospin=nospin,
write_to_stdout=False,
) as sp: ) as sp:
yield sp yield sp
+7 -2
View File
@@ -27,6 +27,7 @@ def cleanup_toml(tml):
def convert_toml_outline_tables(parsed): def convert_toml_outline_tables(parsed):
"""Converts all outline tables to inline tables.""" """Converts all outline tables to inline tables."""
def convert_tomlkit_table(section): def convert_tomlkit_table(section):
if isinstance(section, tomlkit.items.Table): if isinstance(section, tomlkit.items.Table):
body = section.value._body body = section.value._body
@@ -35,14 +36,18 @@ def convert_toml_outline_tables(parsed):
for key, value in body: for key, value in body:
if not key: if not key:
continue 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 = tomlkit.inline_table()
table.update(value.value) table.update(value.value)
section[key.key] = table section[key.key] = table
def convert_toml_table(section): def convert_toml_table(section):
for package, value in section.items(): 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 = toml.TomlDecoder().get_empty_inline_table()
table.update(value) table.update(value)
section[package] = table section[package] = table
+5
View File
@@ -70,3 +70,8 @@ template = "news/towncrier_template.rst"
directory = "removal" directory = "removal"
name = "Removals and Deprecations" name = "Removals and Deprecations"
showcontent = true 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 import sys
from shutil import rmtree 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__)) here = os.path.abspath(os.path.dirname(__file__))
@@ -25,7 +25,7 @@ required = [
"certifi", "certifi",
"setuptools>=36.2.1", "setuptools>=36.2.1",
"virtualenv-clone>=0.2.5", "virtualenv-clone>=0.2.5",
"virtualenv" "virtualenv",
] ]
extras = { extras = {
"dev": [ "dev": [
@@ -113,7 +113,7 @@ setup(
version=about["__version__"], version=about["__version__"],
description="Python Development Workflow for Humans.", description="Python Development Workflow for Humans.",
long_description=long_description, long_description=long_description,
long_description_content_type='text/markdown', long_description_content_type="text/markdown",
author="Pipenv maintainer team", author="Pipenv maintainer team",
author_email="distutils-sig@python.org", author_email="distutils-sig@python.org",
url="https://github.com/pypa/pipenv", url="https://github.com/pypa/pipenv",
-1
View File
@@ -6,7 +6,6 @@ import invoke
from . import release, vendoring from . import release, vendoring
ROOT = Path(".").parent.parent.absolute() ROOT = Path(".").parent.parent.absolute()
ns = invoke.Collection(vendoring, release, release.clean_mdchangelog) ns = invoke.Collection(vendoring, release, release.clean_mdchangelog)
+25 -22
View File
@@ -2,11 +2,10 @@ import datetime
import os import os
import pathlib import pathlib
import re import re
import sys
import subprocess import subprocess
import sys
import invoke import invoke
from parver import Version from parver import Version
from pipenv.__version__ 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 from .vendoring import _get_git_root, drop_dir
VERSION_FILE = "pipenv/__version__.py" VERSION_FILE = "pipenv/__version__.py"
ROOT = pathlib.Path(".").parent.parent.absolute() ROOT = pathlib.Path(".").parent.parent.absolute()
PACKAGE_NAME = "pipenv" PACKAGE_NAME = "pipenv"
@@ -49,8 +47,7 @@ def get_build_dir(ctx):
def _render_log(): 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") rendered = subprocess.check_output(["towncrier", "--draft"]).decode("utf-8")
return rendered return rendered
@@ -66,7 +63,9 @@ release_help = {
@invoke.task(help=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 trunc_month = False
if pre: if pre:
trunc_month = True trunc_month = True
@@ -77,7 +76,7 @@ def release(ctx, manual=False, local=False, dry_run=False, pre=False, tag=None,
pre=pre, pre=pre,
tag=tag, tag=tag,
month_offset=month_offset, month_offset=month_offset,
trunc_month=trunc_month trunc_month=trunc_month,
) )
tag_content = _render_log() tag_content = _render_log()
if dry_run: 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()}") ctx.run(f"git add {get_version_file(ctx).as_posix()}")
else: else:
ctx.run("towncrier") ctx.run("towncrier")
ctx.run( ctx.run(f"git add CHANGELOG.rst news/ {get_version_file(ctx).as_posix()}")
f"git add CHANGELOG.rst news/ {get_version_file(ctx).as_posix()}"
)
log("removing changelog draft if present") log("removing changelog draft if present")
draft_changelog = pathlib.Path("CHANGELOG.draft.rst") draft_changelog = pathlib.Path("CHANGELOG.draft.rst")
if draft_changelog.exists(): if draft_changelog.exists():
@@ -150,11 +147,9 @@ def build_dists(ctx):
log("Building sdist using %s ...." % executable) log("Building sdist using %s ...." % executable)
os.environ["PIPENV_PYTHON"] = py_version os.environ["PIPENV_PYTHON"] = py_version
ctx.run("pipenv install --dev", env=env) ctx.run("pipenv install --dev", env=env)
ctx.run( ctx.run("pipenv run pip install -e . --upgrade --upgrade-strategy=eager", env=env)
"pipenv run pip install -e . --upgrade --upgrade-strategy=eager", env=env
)
log("Building wheel using python %s ...." % py_version) 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) @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, "month": dt.month + month_offset,
"year": dt.year + year_offset, "year": dt.year + year_offset,
} }
log("Getting updated date from date: {} using month offset: {} and year offset {}".format( log(
dt, new_month, replace_args["year"] "Getting updated date from date: {} using month offset: {} and year offset {}".format(
)) dt, new_month, replace_args["year"]
)
)
if day_offset: if day_offset:
dt = dt + datetime.timedelta(days=day_offset) dt = dt + datetime.timedelta(days=day_offset)
log(f"updated date using day offset: {day_offset} => {dt}") 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 @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_version = Version.parse(__version__)
current_date = datetime.date(*current_version.release) current_date = datetime.date(*current_version.release)
today = datetime.date.today() 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 day_offset = 1
target_day = date_offset( target_day = date_offset(
today, today, month_offset=month_offset, day_offset=day_offset, truncate=trunc_month
month_offset=month_offset,
day_offset=day_offset,
truncate=trunc_month
) )
log(f"target_day: {target_day}") log(f"target_day: {target_day}")
target_timetuple = target_day.timetuple()[:3] target_timetuple = target_day.timetuple()[:3]
+27 -32
View File
@@ -3,26 +3,20 @@
""""Vendoring script, python 3.5 needed""" """"Vendoring script, python 3.5 needed"""
import itertools import itertools
import os
import re import re
import shutil import shutil
# from tempfile import TemporaryDirectory
import tarfile import tarfile
import zipfile import zipfile
from pathlib import Path from pathlib import Path
from tempfile import TemporaryDirectory
import bs4 import bs4
import invoke import invoke
import requests import requests
from urllib3.util import parse_url as urllib3_parse 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 import pipenv.vendor.parse as parse
from pipenv.vendor.vistir.contextmanagers import open_file
TASK_NAME = "update" TASK_NAME = "update"
@@ -190,10 +184,16 @@ def rewrite_file_imports(item, vendored_libs):
for lib, to_lib in vendored_libs.items(): for lib, to_lib in vendored_libs.items():
text = re.sub( 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*)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: for pattern, sub in GLOBAL_REPLACEMENT:
text = re.sub(pattern, sub, text) text = re.sub(pattern, sub, text)
item.write_text(text, encoding="utf-8") item.write_text(text, encoding="utf-8")
@@ -209,7 +209,7 @@ def _recursive_write_to_zip(zf, path, root=None):
return return
if root is None: if root is None:
if not path.is_dir(): 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 root = path
if not path.is_dir(): if not path.is_dir():
zf.write(str(path), str(path.relative_to(root))) 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): def _ensure_package_in_requirements(ctx, requirements_file, package):
requirement = None requirement = None
log("using requirements file: %s" % requirements_file) 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: if package:
match = [r for r in req_file_lines if r.strip().lower().startswith(package)] match = [r for r in req_file_lines if r.strip().lower().startswith(package)]
matched_req = None matched_req = None
@@ -274,8 +274,10 @@ def _ensure_package_in_requirements(ctx, requirements_file, package):
def install_pyyaml(ctx, vendor_dir): def install_pyyaml(ctx, vendor_dir):
with TemporaryDirectory(prefix="pipenv-", suffix="-yaml") as download_dir: with TemporaryDirectory(prefix="pipenv-", suffix="-yaml") as download_dir:
pip_command = "pip download --no-binary=:all: --no-clean --no-deps -d {} pyyaml".format( pip_command = (
download_dir "pip download --no-binary=:all: --no-clean --no-deps -d {} pyyaml".format(
download_dir
)
) )
log(f"downloading deps via pip: {pip_command}") log(f"downloading deps via pip: {pip_command}")
ctx.run(pip_command) ctx.run(pip_command)
@@ -291,7 +293,9 @@ def install_pyyaml(ctx, vendor_dir):
if yaml_dir.exists(): if yaml_dir.exists():
drop_dir(yaml_dir) drop_dir(yaml_dir)
path_dict["current_path"].rename(path_dict["destination"]) 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): def install(ctx, vendor_dir, package=None):
@@ -305,7 +309,8 @@ def install(ctx, vendor_dir, package=None):
# the chain. # the chain.
ctx.run( ctx.run(
"pip install -t {} --no-compile --no-deps --upgrade {}".format( "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 # read licenses from distinfo files if possible
@@ -319,22 +324,16 @@ def install(ctx, vendor_dir, package=None):
license_file.read_text() license_file.read_text()
) )
elif vendor_dir.joinpath(f"{pkg}.py").exists(): elif vendor_dir.joinpath(f"{pkg}.py").exists():
vendor_dir.joinpath(f"{pkg}.LICENSE").write_text( vendor_dir.joinpath(f"{pkg}.LICENSE").write_text(license_file.read_text())
license_file.read_text()
)
else: else:
pkg = pkg.replace("-", "?").replace("_", "?") pkg = pkg.replace("-", "?").replace("_", "?")
matched_path = next( matched_path = next(iter(pth for pth in vendor_dir.glob(f"{pkg}*")), None)
iter(pth for pth in vendor_dir.glob(f"{pkg}*")), None
)
if matched_path is not None: if matched_path is not None:
if matched_path.is_dir(): if matched_path.is_dir():
target = vendor_dir.joinpath(matched_path).joinpath("LICENSE") target = vendor_dir.joinpath(matched_path).joinpath("LICENSE")
else: else:
target = vendor_dir.joinpath(f"{matched_path}.LICENSE") target = vendor_dir.joinpath(f"{matched_path}.LICENSE")
target.write_text( target.write_text(license_file.read_text())
license_file.read_text()
)
def post_install_cleanup(ctx, vendor_dir): def post_install_cleanup(ctx, vendor_dir):
@@ -460,7 +459,7 @@ def packages_missing_licenses(
".".join(lic) ".".join(lic)
for lic in itertools.product(("LICENSE", "LICENSE-MIT"), LICENSE_EXTS) 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+"): if req.startswith("git+"):
pkg = req.strip().split("#egg=")[1] pkg = req.strip().split("#egg=")[1]
else: else:
@@ -549,9 +548,7 @@ def download_licenses(
if "." in backend: if "." in backend:
backend, _, _ = backend.partition(".") backend, _, _ = backend.partition(".")
ctx.run(f"pip install {backend}") ctx.run(f"pip install {backend}")
ctx.run( ctx.run(f"{cmd} --no-build-isolation -d {tmp_dir.as_posix()} {req}")
f"{cmd} --no-build-isolation -d {tmp_dir.as_posix()} {req}"
)
for sdist in tmp_dir.iterdir(): for sdist in tmp_dir.iterdir():
extract_license(vendor_dir, sdist) extract_license(vendor_dir, sdist)
drop_dir(tmp_dir) 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) ctx.run("pipenv --rm", env=env, hide=True)
result = list(sorted(line.strip() for line in result.splitlines()[1:])) result = list(sorted(line.strip() for line in result.splitlines()[1:]))
new_requirements = requirement_file.parent.joinpath(name) new_requirements = requirement_file.parent.joinpath(name)
requirement_file.rename( requirement_file.rename(requirement_file.parent.joinpath(f"{name}.bak"))
requirement_file.parent.joinpath(f"{name}.bak")
)
new_requirements.write_text("\n".join(result)) new_requirements.write_text("\n".join(result))
return result return result