зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1602271 - Update vendored copy of 'pip-tools' to 4.5.1, r=rstewart
Differential Revision: https://phabricator.services.mozilla.com/D64755 --HG-- rename : third_party/python/pip-tools/tests/test_data/fake_package/setup.py => third_party/python/pip-tools/tests/test_data/packages/fake_with_deps/setup.py extra : moz-landing-system : lando
This commit is contained in:
Родитель
f01ae26843
Коммит
161d14b474
|
@ -13,30 +13,21 @@ environment:
|
|||
PIP: 10.0.1
|
||||
- TOXENV: py27-pip18.0
|
||||
PIP: 18.0
|
||||
- TOXENV: py27-pip19.0
|
||||
PIP: 19.0
|
||||
- TOXENV: py27-pip19.0.3
|
||||
PIP: 19.0.3
|
||||
- TOXENV: py27-pip19.1
|
||||
PIP: 19.1
|
||||
- TOXENV: py27-pip19.2.3
|
||||
PIP: 19.2.3
|
||||
- TOXENV: py27-pip19.3
|
||||
PIP: 19.3
|
||||
- TOXENV: py27-pip20.0
|
||||
PIP: 20.0
|
||||
- TOXENV: py27-pipmaster
|
||||
PIP: master
|
||||
- TOXENV: py27-piplatest-coverage
|
||||
PIP: latest
|
||||
|
||||
- TOXENV: py34-pip8.1.1
|
||||
PIP: 8.1.1
|
||||
- TOXENV: py34-pip9.0.1
|
||||
PIP: 9.0.1
|
||||
- TOXENV: py34-pip9.0.3-coverage
|
||||
PIP: 9.0.3
|
||||
- TOXENV: py34-pip10.0.1
|
||||
PIP: 10.0.1
|
||||
- TOXENV: py34-pip18.0
|
||||
PIP: 18.0
|
||||
- TOXENV: py34-pip19.0
|
||||
PIP: 19.0
|
||||
- TOXENV: py34-pipmaster
|
||||
PIP: master
|
||||
- TOXENV: py34-piplatest
|
||||
PIP: latest
|
||||
|
||||
- TOXENV: py35-pip8.1.1
|
||||
PIP: 8.1.1
|
||||
- TOXENV: py35-pip9.0.1
|
||||
|
@ -47,8 +38,16 @@ environment:
|
|||
PIP: 10.0.1
|
||||
- TOXENV: py35-pip18.0-coverage
|
||||
PIP: 18.0
|
||||
- TOXENV: py35-pip19.0
|
||||
PIP: 19.0
|
||||
- TOXENV: py35-pip19.0.3
|
||||
PIP: 19.0.3
|
||||
- TOXENV: py35-pip19.1
|
||||
PIP: 19.1
|
||||
- TOXENV: py35-pip19.2.3
|
||||
PIP: 19.2.3
|
||||
- TOXENV: py35-pip19.3
|
||||
PIP: 19.3
|
||||
- TOXENV: py35-pip20.0
|
||||
PIP: 20.0
|
||||
- TOXENV: py35-pipmaster
|
||||
PIP: master
|
||||
- TOXENV: py35-piplatest
|
||||
|
@ -64,8 +63,16 @@ environment:
|
|||
PIP: 10.0.1
|
||||
- TOXENV: py36-pip18.0
|
||||
PIP: 18.0
|
||||
- TOXENV: py36-pip19.0-coverage
|
||||
PIP: 19.0
|
||||
- TOXENV: py36-pip19.0.3-coverage
|
||||
PIP: 19.0.3
|
||||
- TOXENV: py36-pip19.1
|
||||
PIP: 19.1
|
||||
- TOXENV: py36-pip19.2.3
|
||||
PIP: 19.2.3
|
||||
- TOXENV: py36-pip19.3
|
||||
PIP: 19.3
|
||||
- TOXENV: py36-pip20.0
|
||||
PIP: 20.0
|
||||
- TOXENV: py36-pipmaster
|
||||
PIP: master
|
||||
- TOXENV: py36-piplatest
|
||||
|
@ -81,20 +88,60 @@ environment:
|
|||
PIP: 10.0.1
|
||||
- TOXENV: py37-pip18.0
|
||||
PIP: 18.0
|
||||
- TOXENV: py37-pip19.0
|
||||
PIP: 19.0
|
||||
- TOXENV: py37-pip19.0.3
|
||||
PIP: 19.0.3
|
||||
- TOXENV: py37-pip19.1-coverage
|
||||
PIP: 19.1
|
||||
- TOXENV: py37-pip19.2.3
|
||||
PIP: 19.2.3
|
||||
- TOXENV: py37-pip19.3
|
||||
PIP: 19.3
|
||||
- TOXENV: py37-pip20.0
|
||||
PIP: 20.0
|
||||
- TOXENV: py37-pipmaster-coverage
|
||||
PIP: master
|
||||
- TOXENV: py37-piplatest-coverage
|
||||
PIP: latest
|
||||
|
||||
- TOXENV: py38-pip9.0.1
|
||||
PIP: 9.0.1
|
||||
- TOXENV: py38-pip9.0.3
|
||||
PIP: 9.0.3
|
||||
- TOXENV: py38-pip10.0.1
|
||||
PIP: 10.0.1
|
||||
- TOXENV: py38-pip18.0
|
||||
PIP: 18.0
|
||||
- TOXENV: py38-pip19.0.3
|
||||
PIP: 19.0.3
|
||||
- TOXENV: py38-pip19.1
|
||||
PIP: 19.1
|
||||
- TOXENV: py38-pip19.2.3-coverage
|
||||
PIP: 19.2.3
|
||||
- TOXENV: py38-pip19.3-coverage
|
||||
PIP: 19.3
|
||||
- TOXENV: py38-pip20.0
|
||||
PIP: 20.0
|
||||
- TOXENV: py38-pipmaster-coverage
|
||||
PIP: master
|
||||
- TOXENV: py38-piplatest-coverage
|
||||
PIP: latest
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- PIP: master
|
||||
exclude:
|
||||
# platform.linux_distribution() is removed in Python 3.8 (bpo-28167).
|
||||
- TOXENV: py38-pip8.1.1
|
||||
|
||||
install:
|
||||
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
|
||||
|
||||
# Temporary workaround to prevent tests failing.
|
||||
# See GH-983 for the details.
|
||||
# TODO remove this after tox>3.14.0 being released
|
||||
- pip install "virtualenv>=16.0.0"
|
||||
|
||||
- pip install tox
|
||||
|
||||
build: false
|
||||
|
@ -108,5 +155,5 @@ after_test:
|
|||
- IF NOT "x%TOXENV:-coverage=%"=="x%TOXENV%" (
|
||||
pip install codecov &&
|
||||
coverage xml &&
|
||||
codecov --required -X gcov pycov search -f coverage.xml -n %TOXENV%-windows
|
||||
appveyor-retry codecov --required -X gcov pycov search -f coverage.xml -n %TOXENV%-windows
|
||||
)
|
||||
|
|
|
@ -3,7 +3,6 @@ branch = True
|
|||
source = .
|
||||
omit =
|
||||
piptools/_compat/*
|
||||
piptools/_vendored/*
|
||||
|
||||
[report]
|
||||
include = piptools/*, tests/*
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
Describe the issue briefly here.
|
||||
|
||||
##### Environment Versions
|
||||
|
||||
1. OS Type
|
||||
1. Python version: `$ python -V`
|
||||
1. pip version: `$ pip --version`
|
||||
1. pip-tools version: `$ pip-compile --version`
|
||||
|
||||
##### Steps to replicate
|
||||
|
||||
1. ...
|
||||
2. ...
|
||||
3. ...
|
||||
|
||||
##### Expected result
|
||||
|
||||
...
|
||||
|
||||
##### Actual result
|
||||
|
||||
...
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
|
||||
---
|
||||
|
||||
<!-- Describe the issue briefly here. -->
|
||||
|
||||
#### Environment Versions
|
||||
|
||||
1. OS Type
|
||||
1. Python version: `$ python -V`
|
||||
1. pip version: `$ pip --version`
|
||||
1. pip-tools version: `$ pip-compile --version`
|
||||
|
||||
#### Steps to replicate
|
||||
|
||||
1. ...
|
||||
2. ...
|
||||
3. ...
|
||||
|
||||
#### Expected result
|
||||
|
||||
...
|
||||
|
||||
#### Actual result
|
||||
|
||||
...
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
|
||||
---
|
||||
|
||||
#### What's the problem this feature will solve?
|
||||
<!-- What are you trying to do, that you are unable to achieve with pip-tools as it currently stands? -->
|
||||
|
||||
#### Describe the solution you'd like
|
||||
<!-- A clear and concise description of what you want to happen. -->
|
||||
|
||||
<!-- Provide examples of real-world use cases that this would enable and how it solves the problem described above. -->
|
||||
|
||||
#### Alternative Solutions
|
||||
<!-- Have you tried to workaround the problem using pip-tools or other tools? Or a different approach to solving this issue? Please elaborate here. -->
|
||||
|
||||
#### Additional context
|
||||
<!-- Add any other context, links, etc. about the feature here. -->
|
|
@ -1,10 +1,9 @@
|
|||
<!--- Describe the changes here. --->
|
||||
|
||||
**Changelog-friendly one-liner**: <!--- One-liner description here --->
|
||||
**Changelog-friendly one-liner**: <!-- One-liner description here -->
|
||||
|
||||
##### Contributor checklist
|
||||
|
||||
- [ ] Provided the tests for the changes.
|
||||
- [ ] Requested a review from another contributor.
|
||||
- [ ] Gave a clear one-line description in the PR (that the maintainers can add to CHANGELOG.md on release).
|
||||
- [ ] Assign the PR to an existing or new milestone for the target version (following [Semantic Versioning](https://blog.versioneye.com/2014/01/16/semantic-versioning/)).
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
name: cron
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Run every day at 00:00 UTC
|
||||
- cron: 0 0 * * *
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [2.7, 3.5, 3.6, 3.7, 3.8]
|
||||
env:
|
||||
- TOXENV: pipmaster
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install tox
|
||||
run: pip install tox
|
||||
- name: Test with tox ${{ matrix.env.TOXENV }}
|
||||
run: tox
|
||||
env: ${{ matrix.env }}
|
|
@ -7,6 +7,8 @@
|
|||
# Virtualenvs
|
||||
.envrc
|
||||
.direnv
|
||||
.venv
|
||||
venv/
|
||||
|
||||
# Testing
|
||||
.pytest_cache/
|
||||
|
@ -22,7 +24,6 @@ dist
|
|||
|
||||
# IDE
|
||||
.idea
|
||||
venv/
|
||||
|
||||
# Test files
|
||||
requirements.in
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
repos:
|
||||
- repo: https://github.com/python/black
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 19.3b0
|
||||
hooks:
|
||||
- id: black
|
||||
language_version: python3.7
|
||||
language_version: python3
|
||||
- repo: https://github.com/pre-commit/mirrors-isort
|
||||
rev: v4.3.16
|
||||
hooks:
|
||||
- id: isort
|
||||
language_version: python3.7
|
||||
language_version: python3
|
||||
- repo: https://gitlab.com/pycqa/flake8
|
||||
rev: 3.7.7
|
||||
hooks:
|
||||
- id: flake8
|
||||
language_version: python3.7
|
||||
language_version: python3
|
||||
- repo: https://github.com/PyCQA/bandit
|
||||
rev: 1.6.0
|
||||
hooks:
|
||||
- id: bandit
|
||||
language_version: python3.7
|
||||
exclude: ^(tests|\.tox|\.eggs|\.venv|\.git)
|
||||
language_version: python3
|
||||
exclude: ^tests/
|
||||
|
|
|
@ -6,6 +6,7 @@ python:
|
|||
- "3.5"
|
||||
- "3.6"
|
||||
- "3.7"
|
||||
- "3.8"
|
||||
|
||||
env:
|
||||
# NOTE: keep this in sync with envlist in tox.ini for tox-travis.
|
||||
|
@ -14,11 +15,15 @@ env:
|
|||
- PIP=9.0.3
|
||||
- PIP=10.0.1
|
||||
- PIP=18.0
|
||||
- PIP=19.0
|
||||
- PIP=19.0.3
|
||||
- PIP=19.1
|
||||
- PIP=19.2.3
|
||||
- PIP=19.3
|
||||
- PIP=20.0
|
||||
- PIP=latest
|
||||
- PIP=master
|
||||
|
||||
cache: pip
|
||||
cache: false
|
||||
install:
|
||||
- travis_retry pip install tox-travis
|
||||
script:
|
||||
|
@ -34,8 +39,13 @@ jobs:
|
|||
exclude:
|
||||
- python: "pypy3.5-6.0"
|
||||
env: PIP=8.1.1
|
||||
# platform.linux_distribution() is removed in Python 3.8 (bpo-28167).
|
||||
- python: "3.8"
|
||||
env: PIP=8.1.1
|
||||
|
||||
# Baseline jobs (included there/below).
|
||||
- env: PIP=latest
|
||||
python: "3.8"
|
||||
- env: PIP=latest
|
||||
python: "3.7"
|
||||
- env: PIP=latest
|
||||
|
@ -45,6 +55,8 @@ jobs:
|
|||
# Baseline stage to abort early.
|
||||
- stage: baseline
|
||||
env: PIP=latest
|
||||
python: "3.8"
|
||||
- env: PIP=latest
|
||||
python: "3.7"
|
||||
- env: PIP=latest
|
||||
python: "2.7"
|
||||
|
@ -60,11 +72,6 @@ jobs:
|
|||
python: 2.7
|
||||
after_success: skip # No need coverage
|
||||
|
||||
# Default stage (used with matrix).
|
||||
- stage: test
|
||||
python: 3.8-dev
|
||||
env: PIP=latest
|
||||
|
||||
# Only test pypy/pypy3 with latest pip.
|
||||
- env: PIP=latest
|
||||
python: "pypy2.7-6.0"
|
||||
|
@ -89,11 +96,10 @@ jobs:
|
|||
repo: jazzband/pip-tools
|
||||
allow_failures:
|
||||
- env: PIP=master
|
||||
- python: 3.8-dev
|
||||
|
||||
after_success:
|
||||
- travis_retry pip install codecov coveralls
|
||||
- codecov -n "py${TRAVIS_PYTHON_VERSION}-pip${PIP}-${TRAVIS_OS_NAME}"
|
||||
- travis_retry codecov --required -n "py${TRAVIS_PYTHON_VERSION}-pip${PIP}-${TRAVIS_OS_NAME}"
|
||||
- "COVERALLS_PARALLEL=true coveralls"
|
||||
|
||||
notifications:
|
||||
|
|
|
@ -1,3 +1,119 @@
|
|||
# 4.5.1 (2020-02-26)
|
||||
|
||||
Bug Fixes:
|
||||
- Strip line number annotations such as "(line XX)" from file requirements, to prevent diff noise when modifying input requirement files
|
||||
([#1075](https://github.com/jazzband/pip-tools/pull/1075)). Thanks @adamchainz
|
||||
|
||||
Improved Documentation:
|
||||
- Updated `README` example outputs for primary requirement annotations
|
||||
([#1072](https://github.com/jazzband/pip-tools/pull/1072)). Thanks @richafrank
|
||||
|
||||
# 4.5.0 (2020-02-20)
|
||||
|
||||
Features:
|
||||
- Primary requirements and VCS dependencies are now get annotated with any source `.in` files and reverse dependencies
|
||||
([#1058](https://github.com/jazzband/pip-tools/pull/1058)). Thanks @AndydeCleyre
|
||||
|
||||
Bug Fixes:
|
||||
- Always use normalized path for cache directory as it is required in newer versions of `pip`
|
||||
([#1062](https://github.com/jazzband/pip-tools/pull/1062)). Thanks @kammala
|
||||
|
||||
Improved Documentation:
|
||||
- Replace outdated link in the `README` with rationale for pinning
|
||||
([#1053](https://github.com/jazzband/pip-tools/pull/1053)). Thanks @m-aciek
|
||||
|
||||
# 4.4.1 (2020-01-31)
|
||||
|
||||
Bug Fixes:
|
||||
- Fix a bug where `pip-compile` would keep outdated options from `requirements.txt`
|
||||
([#1029](https://github.com/jazzband/pip-tools/pull/1029)). Thanks @atugushev
|
||||
- Fix the `No handlers could be found for logger "pip.*"` error by configuring the builtin logging module
|
||||
([#1035](https://github.com/jazzband/pip-tools/pull/1035)). Thanks @vphilippon
|
||||
- Fix a bug where dependencies of relevant constraints may be missing from output file
|
||||
([#1037](https://github.com/jazzband/pip-tools/pull/1037)). Thanks @jeevb
|
||||
- Upgrade the minimal version of `click` from `6.0` to `7.0` version in `setup.py`
|
||||
([#1039](https://github.com/jazzband/pip-tools/pull/1039)). Thanks @hramezani
|
||||
- Ensure that depcache considers the python implementation such that (for example) `cpython3.6` does not poison the results of `pypy3.6`
|
||||
([#1050](https://github.com/jazzband/pip-tools/pull/1050)). Thanks @asottile
|
||||
|
||||
Improved Documentation:
|
||||
- Make the `README` more imperative about installing into a project's virtual environment to avoid confusion
|
||||
([#1023](https://github.com/jazzband/pip-tools/pull/1023)). Thanks @tekumara
|
||||
- Add a note to the `README` about how to install requirements on different stages to [Workflow for layered requirements](https://github.com/jazzband/pip-tools#workflow-for-layered-requirements) section
|
||||
([#1044](https://github.com/jazzband/pip-tools/pull/1044)). Thanks @hramezani
|
||||
|
||||
# 4.4.0 (2020-01-21)
|
||||
|
||||
Features:
|
||||
- Add `--cache-dir` option to `pip-compile`
|
||||
([#1022](https://github.com/jazzband/pip-tools/pull/1022)). Thanks @richafrank
|
||||
- Add `pip>=20.0` support
|
||||
([#1024](https://github.com/jazzband/pip-tools/pull/1024)). Thanks @atugushev
|
||||
|
||||
Bug Fixes:
|
||||
- Fix a bug where `pip-compile --upgrade-package` would upgrade those passed packages not already required according to the `*.in` and `*.txt` files
|
||||
([#1031](https://github.com/jazzband/pip-tools/pull/1031)). Thanks @AndydeCleyre
|
||||
|
||||
# 4.3.0 (2019-11-25)
|
||||
|
||||
Features:
|
||||
- Add Python 3.8 support
|
||||
([#956](https://github.com/jazzband/pip-tools/pull/956)). Thanks @hramezani
|
||||
- Unpin commented out unsafe packages in `requirements.txt`
|
||||
([#975](https://github.com/jazzband/pip-tools/pull/975)). Thanks @atugushev
|
||||
|
||||
Bug Fixes:
|
||||
- Fix `pip-compile` doesn't copy `--trusted-host` from `requirements.in` to `requirements.txt`
|
||||
([#964](https://github.com/jazzband/pip-tools/pull/964)). Thanks @atugushev
|
||||
- Add compatibility with `pip>=20.0`
|
||||
([#953](https://github.com/jazzband/pip-tools/pull/953) and [#978](https://github.com/jazzband/pip-tools/pull/978)). Thanks @atugushev
|
||||
- Fix a bug where the resolver wouldn't clean up the ephemeral wheel cache
|
||||
([#968](https://github.com/jazzband/pip-tools/pull/968)). Thanks @atugushev
|
||||
|
||||
Improved Documentation:
|
||||
- Add a note to `README` about `requirements.txt` file, which would possibly interfere if you're compiling from scratch
|
||||
([#959](https://github.com/jazzband/pip-tools/pull/959)). Thanks @hramezani
|
||||
|
||||
# 4.2.0 (2019-10-12)
|
||||
|
||||
Features:
|
||||
- Add `--ask` option to `pip-sync`
|
||||
([#913](https://github.com/jazzband/pip-tools/pull/913)). Thanks @georgek
|
||||
|
||||
Bug Fixes:
|
||||
- Add compatibility with `pip>=19.3`
|
||||
([#864](https://github.com/jazzband/pip-tools/pull/864), [#904](https://github.com/jazzband/pip-tools/pull/904), [#910](https://github.com/jazzband/pip-tools/pull/910), [#912](https://github.com/jazzband/pip-tools/pull/912) and [#915](https://github.com/jazzband/pip-tools/pull/915)). Thanks @atugushev
|
||||
- Ensure `pip-compile --no-header <blank requirements.in>` creates/overwrites `requirements.txt`
|
||||
([#909](https://github.com/jazzband/pip-tools/pull/909)). Thanks @AndydeCleyre
|
||||
- Fix `pip-compile --upgrade-package` removes «via» annotation
|
||||
([#931](https://github.com/jazzband/pip-tools/pull/931)). Thanks @hramezani
|
||||
|
||||
Improved Documentation:
|
||||
- Add info to `README` about layered requirements files and `-c` flag
|
||||
([#905](https://github.com/jazzband/pip-tools/pull/905)). Thanks @jamescooke
|
||||
|
||||
# 4.1.0 (2019-08-26)
|
||||
|
||||
Features:
|
||||
- Add `--no-emit-find-links` option to `pip-compile`
|
||||
([#873](https://github.com/jazzband/pip-tools/pull/873)). Thanks @jacobtolar
|
||||
|
||||
Bug Fixes:
|
||||
- Prevent `--dry-run` log message from being printed with `--quiet` option in `pip-compile`
|
||||
([#861](https://github.com/jazzband/pip-tools/pull/861)). Thanks @ddormer
|
||||
- Fix resolution of requirements from Git URLs without `-e`
|
||||
([#879](https://github.com/jazzband/pip-tools/pull/879)). Thanks @andersk
|
||||
|
||||
# 4.0.0 (2019-07-25)
|
||||
|
||||
Backwards Incompatible Changes:
|
||||
- Drop support for EOL Python 3.4
|
||||
([#803](https://github.com/jazzband/pip-tools/pull/803)). Thanks @auvipy
|
||||
|
||||
Bug Fixes:
|
||||
- Fix `pip>=19.2` compatibility
|
||||
([#857](https://github.com/jazzband/pip-tools/pull/857)). Thanks @atugushev
|
||||
|
||||
# 3.9.0 (2019-07-17)
|
||||
|
||||
Features:
|
||||
|
|
|
@ -25,9 +25,12 @@ Jazzband aims to give full access to all members, including performing releases,
|
|||
|
||||
To help keeping track of the releases and their changes, here's the current release process:
|
||||
- Check to see if any recently merged PRs are missing from the milestone of the version about to be released.
|
||||
- Push an update to the [CHANGELOG](CHANGELOG.md) with the version, date and using the one-line descriptions
|
||||
- Create a branch for the release. *Ex: release-3.4.0*.
|
||||
- Update the [CHANGELOG](CHANGELOG.md) with the version, date and using the one-line descriptions
|
||||
from the PRs included in the milestone of the version.
|
||||
Check the previous release changelog format for an example. Don't forget the "Thanks @contributor" mentions.
|
||||
- Push the branch to your fork and create a pull request.
|
||||
- Merge the pull request after the changes being approved.
|
||||
- Make sure that the tests/CI still pass.
|
||||
- Once ready, go to `Github pip-tools Homepage > releases tab > Draft a new release` and type in:
|
||||
- *Tag version:* The exact version number, following [Semantic Versioning](https://blog.versioneye.com/2014/01/16/semantic-versioning/). *Ex: 3.4.0*
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
Metadata-Version: 2.1
|
||||
Name: pip-tools
|
||||
Version: 3.9.0
|
||||
Version: 4.5.1
|
||||
Summary: pip-tools keeps your pinned dependencies fresh.
|
||||
Home-page: https://github.com/jazzband/pip-tools/
|
||||
Author: Vincent Driessen
|
||||
|
@ -13,7 +13,7 @@ Description: |jazzband| |pypi| |pyversions| |buildstatus-travis| |buildstatus-ap
|
|||
==================================
|
||||
|
||||
A set of command line tools to help you keep your ``pip``-based packages fresh,
|
||||
even when you've pinned them. `You do pin them, right?`_
|
||||
even when you've pinned them. You do pin them, right? (In building your Python application and its dependencies for production, you want to make sure that your builds are predictable and deterministic.)
|
||||
|
||||
.. image:: https://github.com/jazzband/pip-tools/raw/master/img/pip-tools-overview.png
|
||||
:alt: pip-tools overview for phase II
|
||||
|
@ -42,8 +42,8 @@ Description: |jazzband| |pypi| |pyversions| |buildstatus-travis| |buildstatus-ap
|
|||
Installation
|
||||
============
|
||||
|
||||
As part of a Python project's environment tooling (similar to ``pip``), it's
|
||||
recommended to install ``pip-tools`` in each project's `virtual environment`_:
|
||||
Similar to ``pip``, ``pip-tools`` must be installed in each of your project's
|
||||
`virtual environments`_:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
|
@ -53,7 +53,7 @@ Description: |jazzband| |pypi| |pyversions| |buildstatus-travis| |buildstatus-ap
|
|||
**Note**: all of the remaining example commands assume you've activated your
|
||||
project's virtual environment.
|
||||
|
||||
.. _virtual environment: https://packaging.python.org/tutorials/installing-packages/#creating-virtual-environments
|
||||
.. _virtual environments: https://packaging.python.org/tutorials/installing-packages/#creating-virtual-environments
|
||||
|
||||
Example usage for ``pip-compile``
|
||||
=================================
|
||||
|
@ -65,11 +65,20 @@ Description: |jazzband| |pypi| |pyversions| |buildstatus-travis| |buildstatus-ap
|
|||
multiple Python versions, you can run ``pip-compile`` as ``py -X.Y -m piptools
|
||||
compile`` on Windows and ``pythonX.Y -m piptools compile`` on other systems.
|
||||
|
||||
``pip-compile`` should be run from the same virtual environment as your
|
||||
project so conditional dependencies that require a specific Python version,
|
||||
or other environment markers, resolve relative to your project's
|
||||
environment.
|
||||
|
||||
**Note**: ensure you don't have ``requirements.txt`` if you compile
|
||||
``setup.py`` or ``requirements.in`` from scratch, otherwise, it might
|
||||
interfere.
|
||||
|
||||
Requirements from ``setup.py``
|
||||
------------------------------
|
||||
|
||||
Suppose you have a Flask project, and want to pin it for production.
|
||||
If you have a ``setup.py`` with ``install_requires=['Flask']``, then run
|
||||
Suppose you have a Django project, and want to pin it for production.
|
||||
If you have a ``setup.py`` with ``install_requires=['django']``, then run
|
||||
``pip-compile`` without any arguments:
|
||||
|
||||
.. code-block:: bash
|
||||
|
@ -79,16 +88,14 @@ Description: |jazzband| |pypi| |pyversions| |buildstatus-travis| |buildstatus-ap
|
|||
# This file is autogenerated by pip-compile
|
||||
# To update, run:
|
||||
#
|
||||
# pip-compile --output-file requirements.txt setup.py
|
||||
# pip-compile
|
||||
#
|
||||
click==6.7 # via flask
|
||||
flask==0.12.2
|
||||
itsdangerous==0.24 # via flask
|
||||
jinja2==2.9.6 # via flask
|
||||
markupsafe==1.0 # via jinja2
|
||||
werkzeug==0.12.2 # via flask
|
||||
asgiref==3.2.3 # via django
|
||||
django==3.0.3 # via my_django_project (setup.py)
|
||||
pytz==2019.3 # via django
|
||||
sqlparse==0.3.0 # via django
|
||||
|
||||
``pip-compile`` will produce your ``requirements.txt``, with all the Flask
|
||||
``pip-compile`` will produce your ``requirements.txt``, with all the Django
|
||||
dependencies (and all underlying dependencies) pinned. You should put
|
||||
``requirements.txt`` under version control.
|
||||
|
||||
|
@ -96,12 +103,12 @@ Description: |jazzband| |pypi| |pyversions| |buildstatus-travis| |buildstatus-ap
|
|||
--------------------
|
||||
|
||||
If you don't use ``setup.py`` (`it's easy to write one`_), you can create a
|
||||
``requirements.in`` file to declare the Flask dependency:
|
||||
``requirements.in`` file to declare the Django dependency:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
# requirements.in
|
||||
Flask
|
||||
django
|
||||
|
||||
Now, run ``pip-compile requirements.in``:
|
||||
|
||||
|
@ -112,16 +119,14 @@ Description: |jazzband| |pypi| |pyversions| |buildstatus-travis| |buildstatus-ap
|
|||
# This file is autogenerated by pip-compile
|
||||
# To update, run:
|
||||
#
|
||||
# pip-compile --output-file requirements.txt requirements.in
|
||||
# pip-compile requirements.in
|
||||
#
|
||||
click==6.7 # via flask
|
||||
flask==0.12.2
|
||||
itsdangerous==0.24 # via flask
|
||||
jinja2==2.9.6 # via flask
|
||||
markupsafe==1.0 # via jinja2
|
||||
werkzeug==0.12.2 # via flask
|
||||
asgiref==3.2.3 # via django
|
||||
django==3.0.3 # via -r requirements.in
|
||||
pytz==2019.3 # via django
|
||||
sqlparse==0.3.0 # via django
|
||||
|
||||
And it will produce your ``requirements.txt``, with all the Flask dependencies
|
||||
And it will produce your ``requirements.txt``, with all the Django dependencies
|
||||
(and all underlying dependencies) pinned. You should put both
|
||||
``requirements.in`` and ``requirements.txt`` under version control.
|
||||
|
||||
|
@ -140,29 +145,24 @@ Description: |jazzband| |pypi| |pyversions| |buildstatus-travis| |buildstatus-ap
|
|||
# This file is autogenerated by pip-compile
|
||||
# To update, run:
|
||||
#
|
||||
# pip-compile --generate-hashes --output-file requirements.txt requirements.in
|
||||
# pip-compile --generate-hashes requirements.in
|
||||
#
|
||||
click==6.7 \
|
||||
--hash=sha256:29f99fc6125fbc931b758dc053b3114e55c77a6e4c6c3a2674a2dc986016381d \
|
||||
--hash=sha256:f15516df478d5a56180fbf80e68f206010e6d160fc39fa508b65e035fd75130b \
|
||||
# via flask
|
||||
flask==0.12.2 \
|
||||
--hash=sha256:0749df235e3ff61ac108f69ac178c9770caeaccad2509cb762ce1f65570a8856 \
|
||||
--hash=sha256:49f44461237b69ecd901cc7ce66feea0319b9158743dd27a2899962ab214dac1
|
||||
itsdangerous==0.24 \
|
||||
--hash=sha256:cbb3fcf8d3e33df861709ecaf89d9e6629cff0a217bc2848f1b41cd30d360519 \
|
||||
# via flask
|
||||
jinja2==2.9.6 \
|
||||
--hash=sha256:2231bace0dfd8d2bf1e5d7e41239c06c9e0ded46e70cc1094a0aa64b0afeb054 \
|
||||
--hash=sha256:ddaa01a212cd6d641401cb01b605f4a4d9f37bfc93043d7f760ec70fb99ff9ff \
|
||||
# via flask
|
||||
markupsafe==1.0 \
|
||||
--hash=sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665 \
|
||||
# via jinja2
|
||||
werkzeug==0.12.2 \
|
||||
--hash=sha256:903a7b87b74635244548b30d30db4c8947fe64c5198f58899ddcd3a13c23bb26 \
|
||||
--hash=sha256:e8549c143af3ce6559699a01e26fa4174f4c591dbee0a499f3cd4c3781cdec3d \
|
||||
# via flask
|
||||
asgiref==3.2.3 \
|
||||
--hash=sha256:7e06d934a7718bf3975acbf87780ba678957b87c7adc056f13b6215d610695a0 \
|
||||
--hash=sha256:ea448f92fc35a0ef4b1508f53a04c4670255a3f33d22a81c8fc9c872036adbe5 \
|
||||
# via django
|
||||
django==3.0.3 \
|
||||
--hash=sha256:2f1ba1db8648484dd5c238fb62504777b7ad090c81c5f1fd8d5eb5ec21b5f283 \
|
||||
--hash=sha256:c91c91a7ad6ef67a874a4f76f58ba534f9208412692a840e1d125eb5c279cb0a \
|
||||
# via -r requirements.in
|
||||
pytz==2019.3 \
|
||||
--hash=sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d \
|
||||
--hash=sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be \
|
||||
# via django
|
||||
sqlparse==0.3.0 \
|
||||
--hash=sha256:40afe6b8d4b1117e7dff5504d7a8ce07d9a1b15aeeade8a2d10f130a834f8177 \
|
||||
--hash=sha256:7c3dca29c022744e95b547e867cee89f4fce4373f3549ccd8797d8eb52cdb873 \
|
||||
# via django
|
||||
|
||||
Updating requirements
|
||||
---------------------
|
||||
|
@ -174,9 +174,14 @@ Description: |jazzband| |pypi| |pyversions| |buildstatus-travis| |buildstatus-ap
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip-compile --upgrade-package flask # only update the flask package
|
||||
$ pip-compile --upgrade-package flask --upgrade-package requests # update both the flask and requests packages
|
||||
$ pip-compile -P flask -P requests==2.0.0 # update the flask package to the latest, and requests to v2.0.0
|
||||
# only update the django package
|
||||
$ pip-compile --upgrade-package django
|
||||
|
||||
# update both the django and requests packages
|
||||
$ pip-compile --upgrade-package django --upgrade-package requests
|
||||
|
||||
# update the django package to the latest, and requests to v2.0.0
|
||||
$ pip-compile --upgrade-package django --upgrade-package requests==2.0.0
|
||||
|
||||
You can combine ``--upgrade`` and ``--upgrade-package`` in one command, to
|
||||
provide constraints on the allowed upgrades. For example to upgrade all
|
||||
|
@ -191,13 +196,13 @@ Description: |jazzband| |pypi| |pyversions| |buildstatus-travis| |buildstatus-ap
|
|||
|
||||
To output the pinned requirements in a filename other than
|
||||
``requirements.txt``, use ``--output-file``. This might be useful for compiling
|
||||
multiple files, for example with different constraints on flask to test a
|
||||
multiple files, for example with different constraints on django to test a
|
||||
library with both versions using `tox <https://tox.readthedocs.io/en/latest/>`__:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip-compile --upgrade-package 'flask<1.0' --output-file requirements-flask0x.txt
|
||||
$ pip-compile --upgrade-package 'flask<2.0' --output-file requirements-flask1x.txt
|
||||
$ pip-compile --upgrade-package 'django<1.0' --output-file requirements-django0x.txt
|
||||
$ pip-compile --upgrade-package 'django<2.0' --output-file requirements-django1x.txt
|
||||
|
||||
Or to output to standard output, use ``--output-file=-``:
|
||||
|
||||
|
@ -223,11 +228,86 @@ Description: |jazzband| |pypi| |pyversions| |buildstatus-travis| |buildstatus-ap
|
|||
#
|
||||
# ./pipcompilewrapper
|
||||
#
|
||||
flask==0.10.1
|
||||
itsdangerous==0.24 # via flask
|
||||
jinja2==2.7.3 # via flask
|
||||
markupsafe==0.23 # via jinja2
|
||||
werkzeug==0.10.4 # via flask
|
||||
asgiref==3.2.3 # via django
|
||||
django==3.0.3 # via -r requirements.in
|
||||
pytz==2019.3 # via django
|
||||
sqlparse==0.3.0 # via django
|
||||
|
||||
Workflow for layered requirements
|
||||
---------------------------------
|
||||
|
||||
If you have different environments that you need to install different but
|
||||
compatible packages for, then you can create layered requirements files and use
|
||||
one layer to constrain the other.
|
||||
|
||||
For example, if you have a Django project where you want the newest ``2.1``
|
||||
release in production and when developing you want to use the Django debug
|
||||
toolbar, then you can create two ``*.in`` files, one for each layer:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
# requirements.in
|
||||
django<2.2
|
||||
|
||||
At the top of the development requirements ``dev-requirements.in`` you use ``-c
|
||||
requirements.txt`` to constrain the dev requirements to packages already
|
||||
selected for production in ``requirements.txt``.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
# dev-requirements.in
|
||||
-c requirements.txt
|
||||
django-debug-toolbar
|
||||
|
||||
First, compile ``requirements.txt`` as usual:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip-compile
|
||||
#
|
||||
# This file is autogenerated by pip-compile
|
||||
# To update, run:
|
||||
#
|
||||
# pip-compile
|
||||
#
|
||||
django==2.1.15 # via -r requirements.in
|
||||
pytz==2019.3 # via django
|
||||
|
||||
|
||||
Now compile the dev requirements and the ``requirements.txt`` file is used as
|
||||
a constraint:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip-compile dev-requirements.in
|
||||
#
|
||||
# This file is autogenerated by pip-compile
|
||||
# To update, run:
|
||||
#
|
||||
# pip-compile dev-requirements.in
|
||||
#
|
||||
django-debug-toolbar==2.2 # via -r dev-requirements.in
|
||||
django==2.1.15 # via -c requirements.txt, django-debug-toolbar
|
||||
pytz==2019.3 # via -c requirements.txt, django
|
||||
sqlparse==0.3.0 # via django-debug-toolbar
|
||||
|
||||
As you can see above, even though a ``2.2`` release of Django is available, the
|
||||
dev requirements only include a ``2.1`` version of Django because they were
|
||||
constrained. Now both compiled requirements files can be installed safely in
|
||||
the dev environment.
|
||||
|
||||
To install requirements in production stage use:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip-sync
|
||||
|
||||
You can install requirements in development stage by:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip-sync requirements.txt dev-requirements.txt
|
||||
|
||||
|
||||
Example usage for ``pip-sync``
|
||||
==============================
|
||||
|
@ -241,6 +321,10 @@ Description: |jazzband| |pypi| |pyversions| |buildstatus-travis| |buildstatus-ap
|
|||
Python versions, you can also run ``py -X.Y -m piptools sync`` on Windows and
|
||||
``pythonX.Y -m piptools sync`` on other systems.
|
||||
|
||||
``pip-sync`` must be installed into and run from the same virtual
|
||||
environment as your project to identify which packages to install
|
||||
or upgrade.
|
||||
|
||||
**Be careful**: ``pip-sync`` is meant to be used only with a
|
||||
``requirements.txt`` generated by ``pip-compile``.
|
||||
|
||||
|
@ -297,12 +381,12 @@ Classifier: Programming Language :: Python
|
|||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.4
|
||||
Classifier: Programming Language :: Python :: 3.5
|
||||
Classifier: Programming Language :: Python :: 3.6
|
||||
Classifier: Programming Language :: Python :: 3.7
|
||||
Classifier: Programming Language :: Python :: 3.8
|
||||
Classifier: Programming Language :: Python :: Implementation :: CPython
|
||||
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
||||
Classifier: Topic :: System :: Systems Administration
|
||||
Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
|
||||
Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*
|
||||
Description-Content-Type: text/x-rst
|
||||
|
|
|
@ -5,7 +5,7 @@ pip-tools = pip-compile + pip-sync
|
|||
==================================
|
||||
|
||||
A set of command line tools to help you keep your ``pip``-based packages fresh,
|
||||
even when you've pinned them. `You do pin them, right?`_
|
||||
even when you've pinned them. You do pin them, right? (In building your Python application and its dependencies for production, you want to make sure that your builds are predictable and deterministic.)
|
||||
|
||||
.. image:: https://github.com/jazzband/pip-tools/raw/master/img/pip-tools-overview.png
|
||||
:alt: pip-tools overview for phase II
|
||||
|
@ -34,8 +34,8 @@ even when you've pinned them. `You do pin them, right?`_
|
|||
Installation
|
||||
============
|
||||
|
||||
As part of a Python project's environment tooling (similar to ``pip``), it's
|
||||
recommended to install ``pip-tools`` in each project's `virtual environment`_:
|
||||
Similar to ``pip``, ``pip-tools`` must be installed in each of your project's
|
||||
`virtual environments`_:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
|
@ -45,7 +45,7 @@ recommended to install ``pip-tools`` in each project's `virtual environment`_:
|
|||
**Note**: all of the remaining example commands assume you've activated your
|
||||
project's virtual environment.
|
||||
|
||||
.. _virtual environment: https://packaging.python.org/tutorials/installing-packages/#creating-virtual-environments
|
||||
.. _virtual environments: https://packaging.python.org/tutorials/installing-packages/#creating-virtual-environments
|
||||
|
||||
Example usage for ``pip-compile``
|
||||
=================================
|
||||
|
@ -57,11 +57,20 @@ Run it with ``pip-compile`` or ``python -m piptools compile``. If you use
|
|||
multiple Python versions, you can run ``pip-compile`` as ``py -X.Y -m piptools
|
||||
compile`` on Windows and ``pythonX.Y -m piptools compile`` on other systems.
|
||||
|
||||
``pip-compile`` should be run from the same virtual environment as your
|
||||
project so conditional dependencies that require a specific Python version,
|
||||
or other environment markers, resolve relative to your project's
|
||||
environment.
|
||||
|
||||
**Note**: ensure you don't have ``requirements.txt`` if you compile
|
||||
``setup.py`` or ``requirements.in`` from scratch, otherwise, it might
|
||||
interfere.
|
||||
|
||||
Requirements from ``setup.py``
|
||||
------------------------------
|
||||
|
||||
Suppose you have a Flask project, and want to pin it for production.
|
||||
If you have a ``setup.py`` with ``install_requires=['Flask']``, then run
|
||||
Suppose you have a Django project, and want to pin it for production.
|
||||
If you have a ``setup.py`` with ``install_requires=['django']``, then run
|
||||
``pip-compile`` without any arguments:
|
||||
|
||||
.. code-block:: bash
|
||||
|
@ -71,16 +80,14 @@ If you have a ``setup.py`` with ``install_requires=['Flask']``, then run
|
|||
# This file is autogenerated by pip-compile
|
||||
# To update, run:
|
||||
#
|
||||
# pip-compile --output-file requirements.txt setup.py
|
||||
# pip-compile
|
||||
#
|
||||
click==6.7 # via flask
|
||||
flask==0.12.2
|
||||
itsdangerous==0.24 # via flask
|
||||
jinja2==2.9.6 # via flask
|
||||
markupsafe==1.0 # via jinja2
|
||||
werkzeug==0.12.2 # via flask
|
||||
asgiref==3.2.3 # via django
|
||||
django==3.0.3 # via my_django_project (setup.py)
|
||||
pytz==2019.3 # via django
|
||||
sqlparse==0.3.0 # via django
|
||||
|
||||
``pip-compile`` will produce your ``requirements.txt``, with all the Flask
|
||||
``pip-compile`` will produce your ``requirements.txt``, with all the Django
|
||||
dependencies (and all underlying dependencies) pinned. You should put
|
||||
``requirements.txt`` under version control.
|
||||
|
||||
|
@ -88,12 +95,12 @@ Without ``setup.py``
|
|||
--------------------
|
||||
|
||||
If you don't use ``setup.py`` (`it's easy to write one`_), you can create a
|
||||
``requirements.in`` file to declare the Flask dependency:
|
||||
``requirements.in`` file to declare the Django dependency:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
# requirements.in
|
||||
Flask
|
||||
django
|
||||
|
||||
Now, run ``pip-compile requirements.in``:
|
||||
|
||||
|
@ -104,16 +111,14 @@ Now, run ``pip-compile requirements.in``:
|
|||
# This file is autogenerated by pip-compile
|
||||
# To update, run:
|
||||
#
|
||||
# pip-compile --output-file requirements.txt requirements.in
|
||||
# pip-compile requirements.in
|
||||
#
|
||||
click==6.7 # via flask
|
||||
flask==0.12.2
|
||||
itsdangerous==0.24 # via flask
|
||||
jinja2==2.9.6 # via flask
|
||||
markupsafe==1.0 # via jinja2
|
||||
werkzeug==0.12.2 # via flask
|
||||
asgiref==3.2.3 # via django
|
||||
django==3.0.3 # via -r requirements.in
|
||||
pytz==2019.3 # via django
|
||||
sqlparse==0.3.0 # via django
|
||||
|
||||
And it will produce your ``requirements.txt``, with all the Flask dependencies
|
||||
And it will produce your ``requirements.txt``, with all the Django dependencies
|
||||
(and all underlying dependencies) pinned. You should put both
|
||||
``requirements.in`` and ``requirements.txt`` under version control.
|
||||
|
||||
|
@ -132,29 +137,24 @@ version 8.0, ``pip-compile`` offers ``--generate-hashes`` flag:
|
|||
# This file is autogenerated by pip-compile
|
||||
# To update, run:
|
||||
#
|
||||
# pip-compile --generate-hashes --output-file requirements.txt requirements.in
|
||||
# pip-compile --generate-hashes requirements.in
|
||||
#
|
||||
click==6.7 \
|
||||
--hash=sha256:29f99fc6125fbc931b758dc053b3114e55c77a6e4c6c3a2674a2dc986016381d \
|
||||
--hash=sha256:f15516df478d5a56180fbf80e68f206010e6d160fc39fa508b65e035fd75130b \
|
||||
# via flask
|
||||
flask==0.12.2 \
|
||||
--hash=sha256:0749df235e3ff61ac108f69ac178c9770caeaccad2509cb762ce1f65570a8856 \
|
||||
--hash=sha256:49f44461237b69ecd901cc7ce66feea0319b9158743dd27a2899962ab214dac1
|
||||
itsdangerous==0.24 \
|
||||
--hash=sha256:cbb3fcf8d3e33df861709ecaf89d9e6629cff0a217bc2848f1b41cd30d360519 \
|
||||
# via flask
|
||||
jinja2==2.9.6 \
|
||||
--hash=sha256:2231bace0dfd8d2bf1e5d7e41239c06c9e0ded46e70cc1094a0aa64b0afeb054 \
|
||||
--hash=sha256:ddaa01a212cd6d641401cb01b605f4a4d9f37bfc93043d7f760ec70fb99ff9ff \
|
||||
# via flask
|
||||
markupsafe==1.0 \
|
||||
--hash=sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665 \
|
||||
# via jinja2
|
||||
werkzeug==0.12.2 \
|
||||
--hash=sha256:903a7b87b74635244548b30d30db4c8947fe64c5198f58899ddcd3a13c23bb26 \
|
||||
--hash=sha256:e8549c143af3ce6559699a01e26fa4174f4c591dbee0a499f3cd4c3781cdec3d \
|
||||
# via flask
|
||||
asgiref==3.2.3 \
|
||||
--hash=sha256:7e06d934a7718bf3975acbf87780ba678957b87c7adc056f13b6215d610695a0 \
|
||||
--hash=sha256:ea448f92fc35a0ef4b1508f53a04c4670255a3f33d22a81c8fc9c872036adbe5 \
|
||||
# via django
|
||||
django==3.0.3 \
|
||||
--hash=sha256:2f1ba1db8648484dd5c238fb62504777b7ad090c81c5f1fd8d5eb5ec21b5f283 \
|
||||
--hash=sha256:c91c91a7ad6ef67a874a4f76f58ba534f9208412692a840e1d125eb5c279cb0a \
|
||||
# via -r requirements.in
|
||||
pytz==2019.3 \
|
||||
--hash=sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d \
|
||||
--hash=sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be \
|
||||
# via django
|
||||
sqlparse==0.3.0 \
|
||||
--hash=sha256:40afe6b8d4b1117e7dff5504d7a8ce07d9a1b15aeeade8a2d10f130a834f8177 \
|
||||
--hash=sha256:7c3dca29c022744e95b547e867cee89f4fce4373f3549ccd8797d8eb52cdb873 \
|
||||
# via django
|
||||
|
||||
Updating requirements
|
||||
---------------------
|
||||
|
@ -166,9 +166,14 @@ To update a specific package to the latest or a specific version use the
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip-compile --upgrade-package flask # only update the flask package
|
||||
$ pip-compile --upgrade-package flask --upgrade-package requests # update both the flask and requests packages
|
||||
$ pip-compile -P flask -P requests==2.0.0 # update the flask package to the latest, and requests to v2.0.0
|
||||
# only update the django package
|
||||
$ pip-compile --upgrade-package django
|
||||
|
||||
# update both the django and requests packages
|
||||
$ pip-compile --upgrade-package django --upgrade-package requests
|
||||
|
||||
# update the django package to the latest, and requests to v2.0.0
|
||||
$ pip-compile --upgrade-package django --upgrade-package requests==2.0.0
|
||||
|
||||
You can combine ``--upgrade`` and ``--upgrade-package`` in one command, to
|
||||
provide constraints on the allowed upgrades. For example to upgrade all
|
||||
|
@ -183,13 +188,13 @@ Output File
|
|||
|
||||
To output the pinned requirements in a filename other than
|
||||
``requirements.txt``, use ``--output-file``. This might be useful for compiling
|
||||
multiple files, for example with different constraints on flask to test a
|
||||
multiple files, for example with different constraints on django to test a
|
||||
library with both versions using `tox <https://tox.readthedocs.io/en/latest/>`__:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip-compile --upgrade-package 'flask<1.0' --output-file requirements-flask0x.txt
|
||||
$ pip-compile --upgrade-package 'flask<2.0' --output-file requirements-flask1x.txt
|
||||
$ pip-compile --upgrade-package 'django<1.0' --output-file requirements-django0x.txt
|
||||
$ pip-compile --upgrade-package 'django<2.0' --output-file requirements-django1x.txt
|
||||
|
||||
Or to output to standard output, use ``--output-file=-``:
|
||||
|
||||
|
@ -215,11 +220,86 @@ generated at the top of requirements files by setting the
|
|||
#
|
||||
# ./pipcompilewrapper
|
||||
#
|
||||
flask==0.10.1
|
||||
itsdangerous==0.24 # via flask
|
||||
jinja2==2.7.3 # via flask
|
||||
markupsafe==0.23 # via jinja2
|
||||
werkzeug==0.10.4 # via flask
|
||||
asgiref==3.2.3 # via django
|
||||
django==3.0.3 # via -r requirements.in
|
||||
pytz==2019.3 # via django
|
||||
sqlparse==0.3.0 # via django
|
||||
|
||||
Workflow for layered requirements
|
||||
---------------------------------
|
||||
|
||||
If you have different environments that you need to install different but
|
||||
compatible packages for, then you can create layered requirements files and use
|
||||
one layer to constrain the other.
|
||||
|
||||
For example, if you have a Django project where you want the newest ``2.1``
|
||||
release in production and when developing you want to use the Django debug
|
||||
toolbar, then you can create two ``*.in`` files, one for each layer:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
# requirements.in
|
||||
django<2.2
|
||||
|
||||
At the top of the development requirements ``dev-requirements.in`` you use ``-c
|
||||
requirements.txt`` to constrain the dev requirements to packages already
|
||||
selected for production in ``requirements.txt``.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
# dev-requirements.in
|
||||
-c requirements.txt
|
||||
django-debug-toolbar
|
||||
|
||||
First, compile ``requirements.txt`` as usual:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip-compile
|
||||
#
|
||||
# This file is autogenerated by pip-compile
|
||||
# To update, run:
|
||||
#
|
||||
# pip-compile
|
||||
#
|
||||
django==2.1.15 # via -r requirements.in
|
||||
pytz==2019.3 # via django
|
||||
|
||||
|
||||
Now compile the dev requirements and the ``requirements.txt`` file is used as
|
||||
a constraint:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip-compile dev-requirements.in
|
||||
#
|
||||
# This file is autogenerated by pip-compile
|
||||
# To update, run:
|
||||
#
|
||||
# pip-compile dev-requirements.in
|
||||
#
|
||||
django-debug-toolbar==2.2 # via -r dev-requirements.in
|
||||
django==2.1.15 # via -c requirements.txt, django-debug-toolbar
|
||||
pytz==2019.3 # via -c requirements.txt, django
|
||||
sqlparse==0.3.0 # via django-debug-toolbar
|
||||
|
||||
As you can see above, even though a ``2.2`` release of Django is available, the
|
||||
dev requirements only include a ``2.1`` version of Django because they were
|
||||
constrained. Now both compiled requirements files can be installed safely in
|
||||
the dev environment.
|
||||
|
||||
To install requirements in production stage use:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip-sync
|
||||
|
||||
You can install requirements in development stage by:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip-sync requirements.txt dev-requirements.txt
|
||||
|
||||
|
||||
Example usage for ``pip-sync``
|
||||
==============================
|
||||
|
@ -233,6 +313,10 @@ Run it with ``pip-sync`` or ``python -m piptools sync``. If you use multiple
|
|||
Python versions, you can also run ``py -X.Y -m piptools sync`` on Windows and
|
||||
``pythonX.Y -m piptools sync`` on other systems.
|
||||
|
||||
``pip-sync`` must be installed into and run from the same virtual
|
||||
environment as your project to identify which packages to install
|
||||
or upgrade.
|
||||
|
||||
**Be careful**: ``pip-sync`` is meant to be used only with a
|
||||
``requirements.txt`` generated by ``pip-compile``.
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
-e .
|
||||
mock
|
||||
pytest
|
||||
pytest!=5.1.2
|
||||
pytest-rerunfailures
|
||||
wheel
|
||||
|
|
|
@ -7,22 +7,28 @@ import six
|
|||
from .pip_compat import (
|
||||
DEV_PKGS,
|
||||
FAVORITE_HASH,
|
||||
Command,
|
||||
PIP_VERSION,
|
||||
FormatControl,
|
||||
InstallationCandidate,
|
||||
InstallCommand,
|
||||
InstallRequirement,
|
||||
Link,
|
||||
PackageFinder,
|
||||
PyPI,
|
||||
RequirementSet,
|
||||
Resolver,
|
||||
Wheel,
|
||||
WheelCache,
|
||||
cmdoptions,
|
||||
get_installed_distributions,
|
||||
get_requirement_tracker,
|
||||
global_tempdir_manager,
|
||||
install_req_from_editable,
|
||||
install_req_from_line,
|
||||
is_dir_url,
|
||||
is_file_url,
|
||||
is_vcs_url,
|
||||
normalize_path,
|
||||
parse_requirements,
|
||||
path_to_url,
|
||||
stdlib_pkgs,
|
||||
|
|
|
@ -1,8 +1,31 @@
|
|||
# -*- coding=utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
|
||||
import importlib
|
||||
from contextlib import contextmanager
|
||||
|
||||
import pip
|
||||
import pkg_resources
|
||||
from pip._vendor.packaging.version import parse as parse_version
|
||||
|
||||
PIP_VERSION = tuple(map(int, parse_version(pip.__version__).base_version.split(".")))
|
||||
|
||||
try:
|
||||
from pip._internal.req.req_tracker import RequirementTracker
|
||||
except ImportError:
|
||||
|
||||
@contextmanager
|
||||
def RequirementTracker():
|
||||
yield
|
||||
|
||||
|
||||
# Introduced in pip 20.0
|
||||
try:
|
||||
from pip._internal.utils.temp_dir import global_tempdir_manager
|
||||
except ImportError:
|
||||
|
||||
@contextmanager
|
||||
def global_tempdir_manager():
|
||||
yield
|
||||
|
||||
|
||||
def do_import(module_path, subimport=None, old_path=None):
|
||||
|
@ -32,15 +55,12 @@ parse_requirements = do_import("req.req_file", "parse_requirements")
|
|||
RequirementSet = do_import("req.req_set", "RequirementSet")
|
||||
user_cache_dir = do_import("utils.appdirs", "user_cache_dir")
|
||||
FAVORITE_HASH = do_import("utils.hashes", "FAVORITE_HASH")
|
||||
is_file_url = do_import("download", "is_file_url")
|
||||
is_dir_url = do_import("download", "is_dir_url")
|
||||
is_vcs_url = do_import("download", "is_vcs_url")
|
||||
path_to_url = do_import("download", "path_to_url")
|
||||
url_to_path = do_import("download", "url_to_path")
|
||||
PackageFinder = do_import("index", "PackageFinder")
|
||||
FormatControl = do_import("index", "FormatControl")
|
||||
Wheel = do_import("wheel", "Wheel")
|
||||
Command = do_import("cli.base_command", "Command", old_path="basecommand")
|
||||
path_to_url = do_import("utils.urls", "path_to_url", old_path="download")
|
||||
url_to_path = do_import("utils.urls", "url_to_path", old_path="download")
|
||||
PackageFinder = do_import("index.package_finder", "PackageFinder", old_path="index")
|
||||
FormatControl = do_import("models.format_control", "FormatControl", old_path="index")
|
||||
InstallCommand = do_import("commands.install", "InstallCommand")
|
||||
Wheel = do_import("models.wheel", "Wheel", old_path="wheel")
|
||||
cmdoptions = do_import("cli.cmdoptions", old_path="cmdoptions")
|
||||
get_installed_distributions = do_import(
|
||||
"utils.misc", "get_installed_distributions", old_path="utils"
|
||||
|
@ -50,9 +70,12 @@ stdlib_pkgs = do_import("utils.compat", "stdlib_pkgs", old_path="compat")
|
|||
DEV_PKGS = do_import("commands.freeze", "DEV_PKGS")
|
||||
Link = do_import("models.link", "Link", old_path="index")
|
||||
Session = do_import("_vendor.requests.sessions", "Session")
|
||||
Resolver = do_import("legacy_resolve", "Resolver", old_path="resolve")
|
||||
WheelCache = do_import("cache", "WheelCache", old_path="wheel")
|
||||
normalize_path = do_import("utils.misc", "normalize_path", old_path="utils")
|
||||
|
||||
# pip 18.1 has refactored InstallRequirement constructors use by pip-tools.
|
||||
if pkg_resources.parse_version(pip.__version__) < pkg_resources.parse_version("18.1"):
|
||||
if PIP_VERSION < (18, 1):
|
||||
install_req_from_line = InstallRequirement.from_line
|
||||
install_req_from_editable = InstallRequirement.from_editable
|
||||
else:
|
||||
|
@ -60,3 +83,36 @@ else:
|
|||
install_req_from_editable = do_import(
|
||||
"req.constructors", "install_req_from_editable"
|
||||
)
|
||||
|
||||
|
||||
def is_vcs_url(link):
|
||||
if PIP_VERSION < (19, 3):
|
||||
_is_vcs_url = do_import("download", "is_vcs_url")
|
||||
return _is_vcs_url(link)
|
||||
|
||||
return link.is_vcs
|
||||
|
||||
|
||||
def is_file_url(link):
|
||||
if PIP_VERSION < (19, 3):
|
||||
_is_file_url = do_import("download", "is_file_url")
|
||||
return _is_file_url(link)
|
||||
|
||||
return link.is_file
|
||||
|
||||
|
||||
def is_dir_url(link):
|
||||
if PIP_VERSION < (19, 3):
|
||||
_is_dir_url = do_import("download", "is_dir_url")
|
||||
return _is_dir_url(link)
|
||||
|
||||
return link.is_existing_dir()
|
||||
|
||||
|
||||
def get_requirement_tracker():
|
||||
if PIP_VERSION[:2] <= (19, 3):
|
||||
return RequirementTracker()
|
||||
|
||||
from pip._internal.req import req_tracker
|
||||
|
||||
return req_tracker.get_requirement_tracker()
|
||||
|
|
|
@ -3,14 +3,25 @@ from __future__ import absolute_import, division, print_function, unicode_litera
|
|||
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
|
||||
from pip._vendor.packaging.requirements import Requirement
|
||||
|
||||
from .exceptions import PipToolsError
|
||||
from .locations import CACHE_DIR
|
||||
from .utils import as_tuple, key_from_req, lookup_table
|
||||
|
||||
_PEP425_PY_TAGS = {"cpython": "cp", "pypy": "pp", "ironpython": "ip", "jython": "jy"}
|
||||
|
||||
|
||||
def _implementation_name():
|
||||
"""similar to PEP 425, however the minor version is separated from the
|
||||
major to differentation "3.10" and "31.0".
|
||||
"""
|
||||
implementation_name = platform.python_implementation().lower()
|
||||
implementation = _PEP425_PY_TAGS.get(implementation_name, "??")
|
||||
return "{}{}.{}".format(implementation, *sys.version_info)
|
||||
|
||||
|
||||
class CorruptCacheError(PipToolsError):
|
||||
def __init__(self, path):
|
||||
|
@ -46,16 +57,14 @@ class DependencyCache(object):
|
|||
|
||||
~/.cache/pip-tools/depcache-pyX.Y.json
|
||||
|
||||
Where py indicates the Python implementation.
|
||||
Where X.Y indicates the Python version.
|
||||
"""
|
||||
|
||||
def __init__(self, cache_dir=None):
|
||||
if cache_dir is None:
|
||||
cache_dir = CACHE_DIR
|
||||
def __init__(self, cache_dir):
|
||||
if not os.path.isdir(cache_dir):
|
||||
os.makedirs(cache_dir)
|
||||
py_version = ".".join(str(digit) for digit in sys.version_info[:2])
|
||||
cache_filename = "depcache-py{}.json".format(py_version)
|
||||
cache_filename = "depcache-{}.json".format(_implementation_name())
|
||||
|
||||
self._cache_file = os.path.join(cache_dir, cache_filename)
|
||||
self._cache = None
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
# coding: utf-8
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
import logging
|
||||
|
||||
from . import click
|
||||
|
||||
# Initialise the builtin logging module for other component using it.
|
||||
# Ex: pip
|
||||
logging.basicConfig()
|
||||
|
||||
|
||||
class LogContext(object):
|
||||
def __init__(self, verbosity=0):
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
import optparse
|
||||
|
||||
from ._compat import Command, cmdoptions
|
||||
|
||||
|
||||
class PipCommand(Command):
|
||||
name = "PipCommand"
|
||||
|
||||
|
||||
def get_pip_command():
|
||||
# Use pip's parser for pip.conf management and defaults.
|
||||
# General options (find_links, index_url, extra_index_url, trusted_host,
|
||||
# and pre) are defered to pip.
|
||||
pip_command = PipCommand()
|
||||
pip_command.parser.add_option(cmdoptions.no_binary())
|
||||
pip_command.parser.add_option(cmdoptions.only_binary())
|
||||
index_opts = cmdoptions.make_option_group(
|
||||
cmdoptions.index_group, pip_command.parser
|
||||
)
|
||||
pip_command.parser.insert_option_group(0, index_opts)
|
||||
pip_command.parser.add_option(
|
||||
optparse.Option("--pre", action="store_true", default=False)
|
||||
)
|
||||
|
||||
return pip_command
|
||||
|
||||
|
||||
pip_command = get_pip_command()
|
||||
|
||||
# Get default values of the pip's options (including options from pip.conf).
|
||||
pip_defaults = pip_command.parser.get_default_values()
|
|
@ -6,7 +6,7 @@ from contextlib import contextmanager
|
|||
from .._compat import FAVORITE_HASH
|
||||
from .base import BaseRepository
|
||||
|
||||
from piptools.utils import as_tuple, key_from_req, make_install_requirement
|
||||
from piptools.utils import as_tuple, key_from_ireq, make_install_requirement
|
||||
|
||||
|
||||
def ireq_satisfied_by_existing_pin(ireq, existing_pin):
|
||||
|
@ -33,6 +33,10 @@ class LocalRequirementsRepository(BaseRepository):
|
|||
self.repository = proxied_repository
|
||||
self.existing_pins = existing_pins
|
||||
|
||||
@property
|
||||
def options(self):
|
||||
return self.repository.options
|
||||
|
||||
@property
|
||||
def finder(self):
|
||||
return self.repository.finder
|
||||
|
@ -52,7 +56,7 @@ class LocalRequirementsRepository(BaseRepository):
|
|||
self.repository.freshen_build_caches()
|
||||
|
||||
def find_best_match(self, ireq, prereleases=None):
|
||||
key = key_from_req(ireq.req)
|
||||
key = key_from_ireq(ireq)
|
||||
existing_pin = self.existing_pins.get(key)
|
||||
if existing_pin and ireq_satisfied_by_existing_pin(ireq, existing_pin):
|
||||
project, version, _ = as_tuple(existing_pin)
|
||||
|
@ -66,7 +70,7 @@ class LocalRequirementsRepository(BaseRepository):
|
|||
return self.repository.get_dependencies(ireq)
|
||||
|
||||
def get_hashes(self, ireq):
|
||||
key = key_from_req(ireq.req)
|
||||
key = key_from_ireq(ireq)
|
||||
existing_pin = self.existing_pins.get(key)
|
||||
if existing_pin and ireq_satisfied_by_existing_pin(ireq, existing_pin):
|
||||
hashes = existing_pin.options.get("hashes", {})
|
||||
|
|
|
@ -5,31 +5,34 @@ import collections
|
|||
import hashlib
|
||||
import os
|
||||
from contextlib import contextmanager
|
||||
from functools import partial
|
||||
from shutil import rmtree
|
||||
|
||||
import pip
|
||||
import pkg_resources
|
||||
|
||||
from .._compat import (
|
||||
FAVORITE_HASH,
|
||||
PIP_VERSION,
|
||||
Link,
|
||||
PackageFinder,
|
||||
PyPI,
|
||||
RequirementSet,
|
||||
Resolver as PipResolver,
|
||||
TemporaryDirectory,
|
||||
Wheel,
|
||||
WheelCache,
|
||||
contextlib,
|
||||
get_requirement_tracker,
|
||||
global_tempdir_manager,
|
||||
is_dir_url,
|
||||
is_file_url,
|
||||
is_vcs_url,
|
||||
normalize_path,
|
||||
path_to_url,
|
||||
url_to_path,
|
||||
)
|
||||
from ..cache import CACHE_DIR
|
||||
from ..click import progressbar
|
||||
from ..exceptions import NoCandidateFound
|
||||
from ..logging import log
|
||||
from ..utils import (
|
||||
create_install_command,
|
||||
fs_str,
|
||||
is_pinned_requirement,
|
||||
is_url_requirement,
|
||||
|
@ -38,20 +41,6 @@ from ..utils import (
|
|||
)
|
||||
from .base import BaseRepository
|
||||
|
||||
try:
|
||||
from pip._internal.req.req_tracker import RequirementTracker
|
||||
except ImportError:
|
||||
|
||||
@contextmanager
|
||||
def RequirementTracker():
|
||||
yield
|
||||
|
||||
|
||||
try:
|
||||
from pip._internal.cache import WheelCache
|
||||
except ImportError:
|
||||
from pip.wheel import WheelCache
|
||||
|
||||
FILE_CHUNK_SIZE = 4096
|
||||
FileStream = collections.namedtuple("FileStream", "stream size")
|
||||
|
||||
|
@ -66,33 +55,21 @@ class PyPIRepository(BaseRepository):
|
|||
changed/configured on the Finder.
|
||||
"""
|
||||
|
||||
def __init__(self, pip_options, session, build_isolation=False):
|
||||
self.session = session
|
||||
self.pip_options = pip_options
|
||||
def __init__(self, pip_args, cache_dir, build_isolation=False):
|
||||
self.build_isolation = build_isolation
|
||||
|
||||
index_urls = [pip_options.index_url] + pip_options.extra_index_urls
|
||||
if pip_options.no_index:
|
||||
index_urls = []
|
||||
# Use pip's parser for pip.conf management and defaults.
|
||||
# General options (find_links, index_url, extra_index_url, trusted_host,
|
||||
# and pre) are deferred to pip.
|
||||
self.command = create_install_command()
|
||||
self.options, _ = self.command.parse_args(pip_args)
|
||||
if self.options.cache_dir:
|
||||
self.options.cache_dir = normalize_path(self.options.cache_dir)
|
||||
|
||||
finder_kwargs = {
|
||||
"find_links": pip_options.find_links,
|
||||
"index_urls": index_urls,
|
||||
"trusted_hosts": pip_options.trusted_hosts,
|
||||
"allow_all_prereleases": pip_options.pre,
|
||||
"session": self.session,
|
||||
}
|
||||
|
||||
# pip 19.0 has removed process_dependency_links
|
||||
# from the PackageFinder constructor
|
||||
if pkg_resources.parse_version(pip.__version__) < pkg_resources.parse_version(
|
||||
"19.0"
|
||||
):
|
||||
finder_kwargs[
|
||||
"process_dependency_links"
|
||||
] = pip_options.process_dependency_links
|
||||
|
||||
self.finder = PackageFinder(**finder_kwargs)
|
||||
self.session = self.command._build_session(self.options)
|
||||
self.finder = self.command._build_package_finder(
|
||||
options=self.options, session=self.session
|
||||
)
|
||||
|
||||
# Caches
|
||||
# stores project_name => InstallationCandidate mappings for all
|
||||
|
@ -107,8 +84,9 @@ class PyPIRepository(BaseRepository):
|
|||
|
||||
# Setup file paths
|
||||
self.freshen_build_caches()
|
||||
self._download_dir = fs_str(os.path.join(CACHE_DIR, "pkgs"))
|
||||
self._wheel_download_dir = fs_str(os.path.join(CACHE_DIR, "wheels"))
|
||||
self._cache_dir = normalize_path(cache_dir)
|
||||
self._download_dir = fs_str(os.path.join(self._cache_dir, "pkgs"))
|
||||
self._wheel_download_dir = fs_str(os.path.join(self._cache_dir, "wheels"))
|
||||
|
||||
def freshen_build_caches(self):
|
||||
"""
|
||||
|
@ -157,19 +135,31 @@ class PyPIRepository(BaseRepository):
|
|||
if not matching_candidates:
|
||||
raise NoCandidateFound(ireq, all_candidates, self.finder)
|
||||
|
||||
# pip <= 19.0.3
|
||||
if hasattr(self.finder, "_candidate_sort_key"):
|
||||
if PIP_VERSION < (19, 1):
|
||||
best_candidate = max(
|
||||
matching_candidates, key=self.finder._candidate_sort_key
|
||||
)
|
||||
# pip >= 19.1
|
||||
else:
|
||||
elif PIP_VERSION < (19, 2):
|
||||
evaluator = self.finder.candidate_evaluator
|
||||
best_candidate = evaluator.get_best_candidate(matching_candidates)
|
||||
elif PIP_VERSION < (19, 3):
|
||||
evaluator = self.finder.make_candidate_evaluator(ireq.name)
|
||||
best_candidate = evaluator.get_best_candidate(matching_candidates)
|
||||
else:
|
||||
evaluator = self.finder.make_candidate_evaluator(ireq.name)
|
||||
best_candidate_result = evaluator.compute_best_candidate(
|
||||
matching_candidates
|
||||
)
|
||||
best_candidate = best_candidate_result.best_candidate
|
||||
|
||||
if PIP_VERSION[:2] <= (19, 3):
|
||||
best_candidate_name = best_candidate.project
|
||||
else:
|
||||
best_candidate_name = best_candidate.name
|
||||
|
||||
# Turn the candidate into a pinned InstallRequirement
|
||||
return make_install_requirement(
|
||||
best_candidate.project,
|
||||
best_candidate_name,
|
||||
best_candidate.version,
|
||||
ireq.extras,
|
||||
constraint=ireq.constraint,
|
||||
|
@ -177,11 +167,8 @@ class PyPIRepository(BaseRepository):
|
|||
|
||||
def resolve_reqs(self, download_dir, ireq, wheel_cache):
|
||||
results = None
|
||||
try:
|
||||
from pip._internal.operations.prepare import RequirementPreparer
|
||||
from pip._internal.resolve import Resolver as PipResolver
|
||||
except ImportError:
|
||||
# Pip 9 and below
|
||||
|
||||
if PIP_VERSION < (10,):
|
||||
reqset = RequirementSet(
|
||||
self.build_dir,
|
||||
self.source_dir,
|
||||
|
@ -192,7 +179,8 @@ class PyPIRepository(BaseRepository):
|
|||
)
|
||||
results = reqset._prepare_file(self.finder, ireq)
|
||||
else:
|
||||
# pip >= 10
|
||||
from pip._internal.operations.prepare import RequirementPreparer
|
||||
|
||||
preparer_kwargs = {
|
||||
"build_dir": self.build_dir,
|
||||
"src_dir": self.source_dir,
|
||||
|
@ -209,24 +197,60 @@ class PyPIRepository(BaseRepository):
|
|||
"ignore_dependencies": False,
|
||||
"ignore_requires_python": False,
|
||||
"ignore_installed": True,
|
||||
"isolated": False,
|
||||
"wheel_cache": wheel_cache,
|
||||
"use_user_site": False,
|
||||
}
|
||||
make_install_req_kwargs = {"isolated": False, "wheel_cache": wheel_cache}
|
||||
|
||||
if PIP_VERSION < (19, 3):
|
||||
resolver_kwargs.update(**make_install_req_kwargs)
|
||||
else:
|
||||
from pip._internal.req.constructors import install_req_from_req_string
|
||||
|
||||
make_install_req = partial(
|
||||
install_req_from_req_string, **make_install_req_kwargs
|
||||
)
|
||||
resolver_kwargs["make_install_req"] = make_install_req
|
||||
|
||||
if PIP_VERSION >= (20,):
|
||||
del resolver_kwargs["session"]
|
||||
del preparer_kwargs["progress_bar"]
|
||||
|
||||
resolver = None
|
||||
preparer = None
|
||||
with RequirementTracker() as req_tracker:
|
||||
|
||||
if PIP_VERSION[:2] <= (19, 3):
|
||||
tmp_dir_cm = contextlib.nullcontext()
|
||||
else:
|
||||
from pip._internal.utils.temp_dir import TempDirectory
|
||||
|
||||
tmp_dir_cm = TempDirectory(kind="req-tracker")
|
||||
|
||||
with get_requirement_tracker() as req_tracker, tmp_dir_cm as temp_build_dir:
|
||||
# Pip 18 uses a requirement tracker to prevent fork bombs
|
||||
if req_tracker:
|
||||
preparer_kwargs["req_tracker"] = req_tracker
|
||||
preparer = RequirementPreparer(**preparer_kwargs)
|
||||
|
||||
if PIP_VERSION[:2] <= (19, 3):
|
||||
preparer = RequirementPreparer(**preparer_kwargs)
|
||||
else:
|
||||
preparer = self.command.make_requirement_preparer(
|
||||
temp_build_dir=temp_build_dir,
|
||||
options=self.options,
|
||||
req_tracker=req_tracker,
|
||||
session=self.session,
|
||||
finder=self.finder,
|
||||
use_user_site=self.options.use_user_site,
|
||||
)
|
||||
|
||||
resolver_kwargs["preparer"] = preparer
|
||||
reqset = RequirementSet()
|
||||
ireq.is_direct = True
|
||||
reqset.add_requirement(ireq)
|
||||
|
||||
resolver = PipResolver(**resolver_kwargs)
|
||||
resolver.require_hashes = False
|
||||
results = resolver._resolve_one(reqset, ireq)
|
||||
|
||||
reqset.cleanup_files()
|
||||
|
||||
return set(results)
|
||||
|
@ -252,7 +276,7 @@ class PyPIRepository(BaseRepository):
|
|||
# If a download_dir is passed, pip will unnecessarely
|
||||
# archive the entire source directory
|
||||
download_dir = None
|
||||
elif ireq.link and not ireq.link.is_artifact:
|
||||
elif ireq.link and is_vcs_url(ireq.link):
|
||||
# No download_dir for VCS sources. This also works around pip
|
||||
# using git-checkout-index, which gets rid of the .git dir.
|
||||
download_dir = None
|
||||
|
@ -263,22 +287,23 @@ class PyPIRepository(BaseRepository):
|
|||
if not os.path.isdir(self._wheel_download_dir):
|
||||
os.makedirs(self._wheel_download_dir)
|
||||
|
||||
wheel_cache = WheelCache(CACHE_DIR, self.pip_options.format_control)
|
||||
wheel_cache = WheelCache(self._cache_dir, self.options.format_control)
|
||||
prev_tracker = os.environ.get("PIP_REQ_TRACKER")
|
||||
try:
|
||||
self._dependencies_cache[ireq] = self.resolve_reqs(
|
||||
download_dir, ireq, wheel_cache
|
||||
)
|
||||
with global_tempdir_manager():
|
||||
self._dependencies_cache[ireq] = self.resolve_reqs(
|
||||
download_dir, ireq, wheel_cache
|
||||
)
|
||||
finally:
|
||||
if "PIP_REQ_TRACKER" in os.environ:
|
||||
if prev_tracker:
|
||||
os.environ["PIP_REQ_TRACKER"] = prev_tracker
|
||||
else:
|
||||
del os.environ["PIP_REQ_TRACKER"]
|
||||
try:
|
||||
self.wheel_cache.cleanup()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
# WheelCache.cleanup() introduced in pip==10.0.0
|
||||
if PIP_VERSION >= (10,):
|
||||
wheel_cache.cleanup()
|
||||
return self._dependencies_cache[ireq]
|
||||
|
||||
def get_hashes(self, ireq):
|
||||
|
@ -323,14 +348,20 @@ class PyPIRepository(BaseRepository):
|
|||
|
||||
log.debug(" {}".format(ireq.name))
|
||||
|
||||
def get_candidate_link(candidate):
|
||||
if PIP_VERSION < (19, 2):
|
||||
return candidate.location
|
||||
return candidate.link
|
||||
|
||||
return {
|
||||
self._get_file_hash(candidate.location) for candidate in matching_candidates
|
||||
self._get_file_hash(get_candidate_link(candidate))
|
||||
for candidate in matching_candidates
|
||||
}
|
||||
|
||||
def _get_file_hash(self, location):
|
||||
log.debug(" Hashing {}".format(location.url_without_fragment))
|
||||
def _get_file_hash(self, link):
|
||||
log.debug(" Hashing {}".format(link.url_without_fragment))
|
||||
h = hashlib.new(FAVORITE_HASH)
|
||||
with open_local_or_remote_file(location, self.session) as f:
|
||||
with open_local_or_remote_file(link, self.session) as f:
|
||||
# Chunks to iterate
|
||||
chunks = iter(lambda: f.stream.read(FILE_CHUNK_SIZE), b"")
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ from itertools import chain, count
|
|||
|
||||
from . import click
|
||||
from ._compat import install_req_from_line
|
||||
from .cache import DependencyCache
|
||||
from .logging import log
|
||||
from .utils import (
|
||||
UNSAFE_PACKAGES,
|
||||
|
@ -18,7 +17,6 @@ from .utils import (
|
|||
is_pinned_requirement,
|
||||
is_url_requirement,
|
||||
key_from_ireq,
|
||||
key_from_req,
|
||||
)
|
||||
|
||||
green = partial(click.style, fg="green")
|
||||
|
@ -32,7 +30,7 @@ class RequirementSummary(object):
|
|||
|
||||
def __init__(self, ireq):
|
||||
self.req = ireq.req
|
||||
self.key = key_from_req(ireq.req)
|
||||
self.key = key_from_ireq(ireq)
|
||||
self.extras = str(sorted(ireq.extras))
|
||||
self.specifier = str(ireq.specifier)
|
||||
|
||||
|
@ -56,7 +54,6 @@ def combine_install_requirements(ireqs):
|
|||
source_ireqs = []
|
||||
for ireq in ireqs:
|
||||
source_ireqs.extend(getattr(ireq, "_source_ireqs", [ireq]))
|
||||
source_ireqs.sort(key=str)
|
||||
|
||||
# deepcopy the accumulator so as to not modify the inputs
|
||||
combined_ireq = copy.deepcopy(source_ireqs[0])
|
||||
|
@ -96,7 +93,7 @@ class Resolver(object):
|
|||
self,
|
||||
constraints,
|
||||
repository,
|
||||
cache=None,
|
||||
cache,
|
||||
prereleases=False,
|
||||
clear_caches=False,
|
||||
allow_unsafe=False,
|
||||
|
@ -109,8 +106,6 @@ class Resolver(object):
|
|||
self.our_constraints = set(constraints)
|
||||
self.their_constraints = set()
|
||||
self.repository = repository
|
||||
if cache is None:
|
||||
cache = DependencyCache() # pragma: no cover
|
||||
self.dependency_cache = cache
|
||||
self.prereleases = prereleases
|
||||
self.clear_caches = clear_caches
|
||||
|
@ -120,7 +115,12 @@ class Resolver(object):
|
|||
@property
|
||||
def constraints(self):
|
||||
return set(
|
||||
self._group_constraints(chain(self.our_constraints, self.their_constraints))
|
||||
self._group_constraints(
|
||||
chain(
|
||||
sorted(self.our_constraints, key=str),
|
||||
sorted(self.their_constraints, key=str),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
def resolve_hashes(self, ireqs):
|
||||
|
@ -155,8 +155,8 @@ class Resolver(object):
|
|||
raise RuntimeError(
|
||||
"No stable configuration of concrete packages "
|
||||
"could be found for the given constraints after "
|
||||
"%d rounds of resolving.\n"
|
||||
"This is likely a bug." % max_rounds
|
||||
"{max_rounds} rounds of resolving.\n"
|
||||
"This is likely a bug.".format(max_rounds=max_rounds)
|
||||
)
|
||||
|
||||
log.debug("")
|
||||
|
@ -258,7 +258,7 @@ class Resolver(object):
|
|||
for best_match in best_matches:
|
||||
their_constraints.extend(self._iter_dependencies(best_match))
|
||||
# Grouping constraints to make clean diff between rounds
|
||||
theirs = set(self._group_constraints(their_constraints))
|
||||
theirs = set(self._group_constraints(sorted(their_constraints, key=str)))
|
||||
|
||||
# NOTE: We need to compare RequirementSummary objects, since
|
||||
# InstallRequirement does not define equality
|
||||
|
@ -273,12 +273,10 @@ class Resolver(object):
|
|||
if has_changed:
|
||||
log.debug("")
|
||||
log.debug("New dependencies found in this round:")
|
||||
for new_dependency in sorted(diff, key=lambda req: key_from_req(req.req)):
|
||||
for new_dependency in sorted(diff, key=key_from_ireq):
|
||||
log.debug(" adding {}".format(new_dependency))
|
||||
log.debug("Removed dependencies in this round:")
|
||||
for removed_dependency in sorted(
|
||||
removed, key=lambda req: key_from_req(req.req)
|
||||
):
|
||||
for removed_dependency in sorted(removed, key=key_from_ireq):
|
||||
log.debug(" removing {}".format(removed_dependency))
|
||||
|
||||
# Store the last round's results in the their_constraints
|
||||
|
@ -320,6 +318,8 @@ class Resolver(object):
|
|||
)
|
||||
)
|
||||
best_match.comes_from = ireq.comes_from
|
||||
if hasattr(ireq, "_source_ireqs"):
|
||||
best_match._source_ireqs = ireq._source_ireqs
|
||||
return best_match
|
||||
|
||||
def _iter_dependencies(self, ireq):
|
||||
|
@ -331,6 +331,17 @@ class Resolver(object):
|
|||
Editable requirements will never be looked up, as they may have
|
||||
changed at any time.
|
||||
"""
|
||||
# Pip does not resolve dependencies of constraints. We skip handling
|
||||
# constraints here as well to prevent the cache from being polluted.
|
||||
# Constraints that are later determined to be dependencies will be
|
||||
# marked as non-constraints in later rounds by
|
||||
# `combine_install_requirements`, and will be properly resolved.
|
||||
# See https://github.com/pypa/pip/
|
||||
# blob/6896dfcd831330c13e076a74624d95fa55ff53f4/src/pip/_internal/
|
||||
# legacy_resolve.py#L325
|
||||
if ireq.constraint:
|
||||
return
|
||||
|
||||
if ireq.editable or is_url_requirement(ireq):
|
||||
for dependency in self.repository.get_dependencies(ireq):
|
||||
yield dependency
|
||||
|
|
|
@ -9,23 +9,29 @@ from click.utils import safecall
|
|||
|
||||
from .. import click
|
||||
from .._compat import install_req_from_line, parse_requirements
|
||||
from ..cache import DependencyCache
|
||||
from ..exceptions import PipToolsError
|
||||
from ..locations import CACHE_DIR
|
||||
from ..logging import log
|
||||
from ..pip import get_pip_command, pip_defaults
|
||||
from ..repositories import LocalRequirementsRepository, PyPIRepository
|
||||
from ..resolver import Resolver
|
||||
from ..utils import (
|
||||
UNSAFE_PACKAGES,
|
||||
create_install_command,
|
||||
dedup,
|
||||
get_trusted_hosts,
|
||||
is_pinned_requirement,
|
||||
key_from_ireq,
|
||||
key_from_req,
|
||||
)
|
||||
from ..writer import OutputWriter
|
||||
|
||||
DEFAULT_REQUIREMENTS_FILE = "requirements.in"
|
||||
DEFAULT_REQUIREMENTS_OUTPUT_FILE = "requirements.txt"
|
||||
|
||||
# Get default values of the pip's options (including options from pip.conf).
|
||||
install_command = create_install_command()
|
||||
pip_defaults = install_command.parser.get_default_values()
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.version_option()
|
||||
|
@ -161,6 +167,21 @@ DEFAULT_REQUIREMENTS_OUTPUT_FILE = "requirements.txt"
|
|||
"Build dependencies specified by PEP 518 must be already installed "
|
||||
"if build isolation is disabled.",
|
||||
)
|
||||
@click.option(
|
||||
"--emit-find-links/--no-emit-find-links",
|
||||
is_flag=True,
|
||||
default=True,
|
||||
help="Add the find-links option to generated file",
|
||||
)
|
||||
@click.option(
|
||||
"--cache-dir",
|
||||
help="Store the cache data in DIRECTORY.",
|
||||
default=CACHE_DIR,
|
||||
envvar="PIP_TOOLS_CACHE_DIR",
|
||||
show_default=True,
|
||||
show_envvar=True,
|
||||
type=click.Path(file_okay=False, writable=True),
|
||||
)
|
||||
def cli(
|
||||
ctx,
|
||||
verbose,
|
||||
|
@ -186,6 +207,8 @@ def cli(
|
|||
src_files,
|
||||
max_rounds,
|
||||
build_isolation,
|
||||
emit_find_links,
|
||||
cache_dir,
|
||||
):
|
||||
"""Compiles requirements.txt from requirements.in specs."""
|
||||
log.verbosity = verbose - quiet
|
||||
|
@ -229,8 +252,6 @@ def cli(
|
|||
# Setup
|
||||
###
|
||||
|
||||
pip_command = get_pip_command()
|
||||
|
||||
pip_args = []
|
||||
if find_links:
|
||||
for link in find_links:
|
||||
|
@ -250,35 +271,42 @@ def cli(
|
|||
for host in trusted_host:
|
||||
pip_args.extend(["--trusted-host", host])
|
||||
|
||||
pip_options, _ = pip_command.parse_args(pip_args)
|
||||
|
||||
session = pip_command._build_session(pip_options)
|
||||
repository = PyPIRepository(pip_options, session, build_isolation)
|
||||
repository = PyPIRepository(
|
||||
pip_args, build_isolation=build_isolation, cache_dir=cache_dir
|
||||
)
|
||||
|
||||
# Parse all constraints coming from --upgrade-package/-P
|
||||
upgrade_reqs_gen = (install_req_from_line(pkg) for pkg in upgrade_packages)
|
||||
upgrade_install_reqs = {
|
||||
key_from_req(install_req.req): install_req for install_req in upgrade_reqs_gen
|
||||
key_from_ireq(install_req): install_req for install_req in upgrade_reqs_gen
|
||||
}
|
||||
|
||||
existing_pins_to_upgrade = set()
|
||||
|
||||
# Proxy with a LocalRequirementsRepository if --upgrade is not specified
|
||||
# (= default invocation)
|
||||
if not upgrade and os.path.exists(output_file.name):
|
||||
# Use a temporary repository to ensure outdated(removed) options from
|
||||
# existing requirements.txt wouldn't get into the current repository.
|
||||
tmp_repository = PyPIRepository(
|
||||
pip_args, build_isolation=build_isolation, cache_dir=cache_dir
|
||||
)
|
||||
ireqs = parse_requirements(
|
||||
output_file.name,
|
||||
finder=repository.finder,
|
||||
session=repository.session,
|
||||
options=pip_options,
|
||||
finder=tmp_repository.finder,
|
||||
session=tmp_repository.session,
|
||||
options=tmp_repository.options,
|
||||
)
|
||||
|
||||
# Exclude packages from --upgrade-package/-P from the existing
|
||||
# constraints
|
||||
existing_pins = {
|
||||
key_from_req(ireq.req): ireq
|
||||
for ireq in ireqs
|
||||
if is_pinned_requirement(ireq)
|
||||
and key_from_req(ireq.req) not in upgrade_install_reqs
|
||||
}
|
||||
# constraints, and separately gather pins to be upgraded
|
||||
existing_pins = {}
|
||||
for ireq in filter(is_pinned_requirement, ireqs):
|
||||
key = key_from_ireq(ireq)
|
||||
if key in upgrade_install_reqs:
|
||||
existing_pins_to_upgrade.add(key)
|
||||
else:
|
||||
existing_pins[key] = ireq
|
||||
repository = LocalRequirementsRepository(existing_pins, repository)
|
||||
|
||||
###
|
||||
|
@ -299,28 +327,42 @@ def cli(
|
|||
|
||||
dist = run_setup(src_file)
|
||||
tmpfile.write("\n".join(dist.install_requires))
|
||||
comes_from = "{name} ({filename})".format(
|
||||
name=dist.get_name(), filename=src_file
|
||||
)
|
||||
else:
|
||||
tmpfile.write(sys.stdin.read())
|
||||
comes_from = "-r -"
|
||||
tmpfile.flush()
|
||||
constraints.extend(
|
||||
reqs = list(
|
||||
parse_requirements(
|
||||
tmpfile.name,
|
||||
finder=repository.finder,
|
||||
session=repository.session,
|
||||
options=pip_options,
|
||||
options=repository.options,
|
||||
)
|
||||
)
|
||||
for req in reqs:
|
||||
req.comes_from = comes_from
|
||||
constraints.extend(reqs)
|
||||
else:
|
||||
constraints.extend(
|
||||
parse_requirements(
|
||||
src_file,
|
||||
finder=repository.finder,
|
||||
session=repository.session,
|
||||
options=pip_options,
|
||||
options=repository.options,
|
||||
)
|
||||
)
|
||||
|
||||
constraints.extend(upgrade_install_reqs.values())
|
||||
primary_packages = {
|
||||
key_from_ireq(ireq) for ireq in constraints if not ireq.constraint
|
||||
}
|
||||
|
||||
allowed_upgrades = primary_packages | existing_pins_to_upgrade
|
||||
constraints.extend(
|
||||
ireq for key, ireq in upgrade_install_reqs.items() if key in allowed_upgrades
|
||||
)
|
||||
|
||||
# Filter out pip environment markers which do not match (PEP496)
|
||||
constraints = [
|
||||
|
@ -328,15 +370,13 @@ def cli(
|
|||
]
|
||||
|
||||
log.debug("Using indexes:")
|
||||
# remove duplicate index urls before processing
|
||||
repository.finder.index_urls = list(dedup(repository.finder.index_urls))
|
||||
for index_url in repository.finder.index_urls:
|
||||
for index_url in dedup(repository.finder.index_urls):
|
||||
log.debug(" {}".format(index_url))
|
||||
|
||||
if repository.finder.find_links:
|
||||
log.debug("")
|
||||
log.debug("Configuration:")
|
||||
for find_link in repository.finder.find_links:
|
||||
for find_link in dedup(repository.finder.find_links):
|
||||
log.debug(" -f {}".format(find_link))
|
||||
|
||||
try:
|
||||
|
@ -344,6 +384,7 @@ def cli(
|
|||
constraints,
|
||||
repository,
|
||||
prereleases=repository.finder.allow_all_prereleases or pre,
|
||||
cache=DependencyCache(cache_dir),
|
||||
clear_caches=rebuild,
|
||||
allow_unsafe=allow_unsafe,
|
||||
)
|
||||
|
@ -362,33 +403,6 @@ def cli(
|
|||
# Output
|
||||
##
|
||||
|
||||
# Compute reverse dependency annotations statically, from the
|
||||
# dependency cache that the resolver has populated by now.
|
||||
#
|
||||
# TODO (1a): reverse deps for any editable package are lost
|
||||
# what SHOULD happen is that they are cached in memory, just
|
||||
# not persisted to disk!
|
||||
#
|
||||
# TODO (1b): perhaps it's easiest if the dependency cache has an API
|
||||
# that could take InstallRequirements directly, like:
|
||||
#
|
||||
# cache.set(ireq, ...)
|
||||
#
|
||||
# then, when ireq is editable, it would store in
|
||||
#
|
||||
# editables[egg_name][link_without_fragment] = deps
|
||||
# editables['pip-tools']['git+...ols.git@future'] = {
|
||||
# 'click>=3.0', 'six'
|
||||
# }
|
||||
#
|
||||
# otherwise:
|
||||
#
|
||||
# self[as_name_version_tuple(ireq)] = {'click>=3.0', 'six'}
|
||||
#
|
||||
reverse_dependencies = None
|
||||
if annotate:
|
||||
reverse_dependencies = resolver.reverse_dependencies(results)
|
||||
|
||||
writer = OutputWriter(
|
||||
src_files,
|
||||
output_file,
|
||||
|
@ -401,18 +415,15 @@ def cli(
|
|||
generate_hashes=generate_hashes,
|
||||
default_index_url=repository.DEFAULT_INDEX_URL,
|
||||
index_urls=repository.finder.index_urls,
|
||||
trusted_hosts=pip_options.trusted_hosts,
|
||||
trusted_hosts=get_trusted_hosts(repository.finder),
|
||||
format_control=repository.finder.format_control,
|
||||
allow_unsafe=allow_unsafe,
|
||||
find_links=repository.finder.find_links,
|
||||
emit_find_links=emit_find_links,
|
||||
)
|
||||
writer.write(
|
||||
results=results,
|
||||
unsafe_requirements=resolver.unsafe_constraints,
|
||||
reverse_dependencies=reverse_dependencies,
|
||||
primary_packages={
|
||||
key_from_ireq(ireq) for ireq in constraints if not ireq.constraint
|
||||
},
|
||||
markers={
|
||||
key_from_ireq(ireq): ireq.markers for ireq in constraints if ireq.markers
|
||||
},
|
||||
|
@ -420,4 +431,4 @@ def cli(
|
|||
)
|
||||
|
||||
if dry_run:
|
||||
log.warning("Dry-run, so nothing updated.")
|
||||
log.info("Dry-run, so nothing updated.")
|
||||
|
|
|
@ -15,6 +15,12 @@ DEFAULT_REQUIREMENTS_FILE = "requirements.txt"
|
|||
|
||||
@click.command()
|
||||
@click.version_option()
|
||||
@click.option(
|
||||
"-a",
|
||||
"--ask",
|
||||
is_flag=True,
|
||||
help="Show what would happen, then ask whether to continue",
|
||||
)
|
||||
@click.option(
|
||||
"-n",
|
||||
"--dry-run",
|
||||
|
@ -63,6 +69,7 @@ DEFAULT_REQUIREMENTS_FILE = "requirements.txt"
|
|||
)
|
||||
@click.argument("src_files", required=False, type=click.Path(exists=True), nargs=-1)
|
||||
def cli(
|
||||
ask,
|
||||
dry_run,
|
||||
force,
|
||||
find_links,
|
||||
|
@ -137,5 +144,6 @@ def cli(
|
|||
verbose=(not quiet),
|
||||
dry_run=dry_run,
|
||||
install_flags=install_flags,
|
||||
ask=ask,
|
||||
)
|
||||
)
|
||||
|
|
|
@ -144,7 +144,14 @@ def diff(compiled_requirements, installed_dists):
|
|||
return (to_install, to_uninstall)
|
||||
|
||||
|
||||
def sync(to_install, to_uninstall, verbose=False, dry_run=False, install_flags=None):
|
||||
def sync(
|
||||
to_install,
|
||||
to_uninstall,
|
||||
verbose=False,
|
||||
dry_run=False,
|
||||
install_flags=None,
|
||||
ask=False,
|
||||
):
|
||||
"""
|
||||
Install and uninstalls the given sets of modules.
|
||||
"""
|
||||
|
@ -157,26 +164,34 @@ def sync(to_install, to_uninstall, verbose=False, dry_run=False, install_flags=N
|
|||
if not verbose:
|
||||
pip_flags += ["-q"]
|
||||
|
||||
if to_uninstall:
|
||||
if dry_run:
|
||||
if ask:
|
||||
dry_run = True
|
||||
|
||||
if dry_run:
|
||||
if to_uninstall:
|
||||
click.echo("Would uninstall:")
|
||||
for pkg in to_uninstall:
|
||||
click.echo(" {}".format(pkg))
|
||||
else:
|
||||
|
||||
if to_install:
|
||||
click.echo("Would install:")
|
||||
for ireq in to_install:
|
||||
click.echo(" {}".format(format_requirement(ireq)))
|
||||
|
||||
if ask and click.confirm("Would you like to proceed with these changes?"):
|
||||
dry_run = False
|
||||
|
||||
if not dry_run:
|
||||
if to_uninstall:
|
||||
check_call( # nosec
|
||||
[sys.executable, "-m", "pip", "uninstall", "-y"]
|
||||
+ pip_flags
|
||||
+ sorted(to_uninstall)
|
||||
)
|
||||
|
||||
if to_install:
|
||||
if install_flags is None:
|
||||
install_flags = []
|
||||
if dry_run:
|
||||
click.echo("Would install:")
|
||||
for ireq in to_install:
|
||||
click.echo(" {}".format(format_requirement(ireq)))
|
||||
else:
|
||||
if to_install:
|
||||
if install_flags is None:
|
||||
install_flags = []
|
||||
# prepare requirement lines
|
||||
req_lines = []
|
||||
for ireq in sorted(to_install, key=key_from_ireq):
|
||||
|
|
|
@ -9,7 +9,7 @@ import six
|
|||
from click.utils import LazyFile
|
||||
from six.moves import shlex_quote
|
||||
|
||||
from ._compat import install_req_from_line
|
||||
from ._compat import PIP_VERSION, InstallCommand, install_req_from_line
|
||||
from .click import style
|
||||
|
||||
UNSAFE_PACKAGES = {"setuptools", "distribute", "pip"}
|
||||
|
@ -20,6 +20,7 @@ COMPILE_EXCLUDE_OPTIONS = {
|
|||
"--upgrade",
|
||||
"--upgrade-package",
|
||||
"--verbose",
|
||||
"--cache-dir",
|
||||
}
|
||||
|
||||
|
||||
|
@ -136,7 +137,7 @@ def as_tuple(ireq):
|
|||
if not is_pinned_requirement(ireq):
|
||||
raise TypeError("Expected a pinned InstallRequirement, got {}".format(ireq))
|
||||
|
||||
name = key_from_req(ireq.req)
|
||||
name = key_from_ireq(ireq)
|
||||
version = next(iter(ireq.specifier._specs))._spec[1]
|
||||
extras = tuple(sorted(ireq.extras))
|
||||
return name, version, extras
|
||||
|
@ -185,7 +186,7 @@ def lookup_table(values, key=None, keyval=None, unique=False, use_lists=False):
|
|||
... 'q': ['qux', 'quux']
|
||||
... }
|
||||
|
||||
The values of the resulting lookup table will be values, not sets.
|
||||
The values of the resulting lookup table will be lists, not sets.
|
||||
|
||||
For extra power, you can even change the values while building up the LUT.
|
||||
To do so, use the `keyval` function instead of the `key` arg:
|
||||
|
@ -368,3 +369,25 @@ def get_compile_command(click_ctx):
|
|||
)
|
||||
|
||||
return " ".join(["pip-compile"] + sorted(left_args) + sorted(right_args))
|
||||
|
||||
|
||||
def create_install_command():
|
||||
"""
|
||||
Return an instance of InstallCommand.
|
||||
"""
|
||||
if PIP_VERSION < (19, 3):
|
||||
return InstallCommand()
|
||||
|
||||
from pip._internal.commands import create_command
|
||||
|
||||
return create_command("install")
|
||||
|
||||
|
||||
def get_trusted_hosts(finder):
|
||||
"""
|
||||
Returns an iterable of trusted hosts from a given finder.
|
||||
"""
|
||||
if PIP_VERSION < (19, 2):
|
||||
return (host for _, host, _ in finder.secure_origins)
|
||||
|
||||
return finder.trusted_hosts
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
import re
|
||||
from itertools import chain
|
||||
|
||||
import six
|
||||
|
||||
from .click import unstyle
|
||||
from .logging import log
|
||||
from .utils import (
|
||||
|
@ -36,6 +39,15 @@ MESSAGE_UNINSTALLABLE = (
|
|||
)
|
||||
|
||||
|
||||
strip_comes_from_line_re = re.compile(r" \(line \d+\)$")
|
||||
|
||||
|
||||
def _comes_from_as_string(ireq):
|
||||
if isinstance(ireq.comes_from, six.string_types):
|
||||
return strip_comes_from_line_re.sub("", ireq.comes_from)
|
||||
return key_from_ireq(ireq.comes_from)
|
||||
|
||||
|
||||
class OutputWriter(object):
|
||||
def __init__(
|
||||
self,
|
||||
|
@ -54,6 +66,7 @@ class OutputWriter(object):
|
|||
format_control,
|
||||
allow_unsafe,
|
||||
find_links,
|
||||
emit_find_links,
|
||||
):
|
||||
self.src_files = src_files
|
||||
self.dst_file = dst_file
|
||||
|
@ -70,6 +83,7 @@ class OutputWriter(object):
|
|||
self.format_control = format_control
|
||||
self.allow_unsafe = allow_unsafe
|
||||
self.find_links = find_links
|
||||
self.emit_find_links = emit_find_links
|
||||
|
||||
def _sort_key(self, ireq):
|
||||
return (not ireq.editable, str(ireq.req).lower())
|
||||
|
@ -106,8 +120,9 @@ class OutputWriter(object):
|
|||
yield "--only-binary {}".format(ob)
|
||||
|
||||
def write_find_links(self):
|
||||
for find_link in dedup(self.find_links):
|
||||
yield "--find-links {}".format(find_link)
|
||||
if self.emit_find_links:
|
||||
for find_link in dedup(self.find_links):
|
||||
yield "--find-links {}".format(find_link)
|
||||
|
||||
def write_flags(self):
|
||||
emitted = False
|
||||
|
@ -122,19 +137,9 @@ class OutputWriter(object):
|
|||
if emitted:
|
||||
yield ""
|
||||
|
||||
def _iter_lines(
|
||||
self,
|
||||
results,
|
||||
unsafe_requirements=None,
|
||||
reverse_dependencies=None,
|
||||
primary_packages=None,
|
||||
markers=None,
|
||||
hashes=None,
|
||||
):
|
||||
def _iter_lines(self, results, unsafe_requirements=None, markers=None, hashes=None):
|
||||
# default values
|
||||
unsafe_requirements = unsafe_requirements or []
|
||||
reverse_dependencies = reverse_dependencies or {}
|
||||
primary_packages = primary_packages or []
|
||||
markers = markers or {}
|
||||
hashes = hashes or {}
|
||||
|
||||
|
@ -143,10 +148,14 @@ class OutputWriter(object):
|
|||
warn_uninstallable = False
|
||||
has_hashes = hashes and any(hash for hash in hashes.values())
|
||||
|
||||
yielded = False
|
||||
|
||||
for line in self.write_header():
|
||||
yield line
|
||||
yielded = True
|
||||
for line in self.write_flags():
|
||||
yield line
|
||||
yielded = True
|
||||
|
||||
unsafe_requirements = (
|
||||
{r for r in results if r.name in UNSAFE_PACKAGES}
|
||||
|
@ -155,24 +164,22 @@ class OutputWriter(object):
|
|||
)
|
||||
packages = {r for r in results if r.name not in UNSAFE_PACKAGES}
|
||||
|
||||
packages = sorted(packages, key=self._sort_key)
|
||||
|
||||
for ireq in packages:
|
||||
if has_hashes and not hashes.get(ireq):
|
||||
yield MESSAGE_UNHASHED_PACKAGE
|
||||
warn_uninstallable = True
|
||||
line = self._format_requirement(
|
||||
ireq,
|
||||
reverse_dependencies,
|
||||
primary_packages,
|
||||
markers.get(key_from_ireq(ireq)),
|
||||
hashes=hashes,
|
||||
)
|
||||
yield line
|
||||
if packages:
|
||||
packages = sorted(packages, key=self._sort_key)
|
||||
for ireq in packages:
|
||||
if has_hashes and not hashes.get(ireq):
|
||||
yield MESSAGE_UNHASHED_PACKAGE
|
||||
warn_uninstallable = True
|
||||
line = self._format_requirement(
|
||||
ireq, markers.get(key_from_ireq(ireq)), hashes=hashes
|
||||
)
|
||||
yield line
|
||||
yielded = True
|
||||
|
||||
if unsafe_requirements:
|
||||
unsafe_requirements = sorted(unsafe_requirements, key=self._sort_key)
|
||||
yield ""
|
||||
yielded = True
|
||||
if has_hashes and not self.allow_unsafe:
|
||||
yield MESSAGE_UNSAFE_PACKAGES_UNPINNED
|
||||
warn_uninstallable = True
|
||||
|
@ -180,56 +187,48 @@ class OutputWriter(object):
|
|||
yield MESSAGE_UNSAFE_PACKAGES
|
||||
|
||||
for ireq in unsafe_requirements:
|
||||
req = self._format_requirement(
|
||||
ireq,
|
||||
reverse_dependencies,
|
||||
primary_packages,
|
||||
marker=markers.get(key_from_ireq(ireq)),
|
||||
hashes=hashes,
|
||||
)
|
||||
ireq_key = key_from_ireq(ireq)
|
||||
if not self.allow_unsafe:
|
||||
yield comment("# {}".format(req))
|
||||
yield comment("# {}".format(ireq_key))
|
||||
else:
|
||||
yield req
|
||||
line = self._format_requirement(
|
||||
ireq, marker=markers.get(ireq_key), hashes=hashes
|
||||
)
|
||||
yield line
|
||||
|
||||
# Yield even when there's no real content, so that blank files are written
|
||||
if not yielded:
|
||||
yield ""
|
||||
|
||||
if warn_uninstallable:
|
||||
log.warning(MESSAGE_UNINSTALLABLE)
|
||||
|
||||
def write(
|
||||
self,
|
||||
results,
|
||||
unsafe_requirements,
|
||||
reverse_dependencies,
|
||||
primary_packages,
|
||||
markers,
|
||||
hashes,
|
||||
):
|
||||
def write(self, results, unsafe_requirements, markers, hashes):
|
||||
|
||||
for line in self._iter_lines(
|
||||
results,
|
||||
unsafe_requirements,
|
||||
reverse_dependencies,
|
||||
primary_packages,
|
||||
markers,
|
||||
hashes,
|
||||
):
|
||||
for line in self._iter_lines(results, unsafe_requirements, markers, hashes):
|
||||
log.info(line)
|
||||
if not self.dry_run:
|
||||
self.dst_file.write(unstyle(line).encode("utf-8"))
|
||||
self.dst_file.write(os.linesep.encode("utf-8"))
|
||||
|
||||
def _format_requirement(
|
||||
self, ireq, reverse_dependencies, primary_packages, marker=None, hashes=None
|
||||
):
|
||||
def _format_requirement(self, ireq, marker=None, hashes=None):
|
||||
ireq_hashes = (hashes if hashes is not None else {}).get(ireq)
|
||||
|
||||
line = format_requirement(ireq, marker=marker, hashes=ireq_hashes)
|
||||
|
||||
if not self.annotate or key_from_ireq(ireq) in primary_packages:
|
||||
if not self.annotate:
|
||||
return line
|
||||
|
||||
# Annotate what packages this package is required by
|
||||
required_by = reverse_dependencies.get(ireq.name.lower(), [])
|
||||
# Annotate what packages or reqs-ins this package is required by
|
||||
required_by = set()
|
||||
if hasattr(ireq, "_source_ireqs"):
|
||||
required_by |= {
|
||||
_comes_from_as_string(src_ireq)
|
||||
for src_ireq in ireq._source_ireqs
|
||||
if src_ireq.comes_from
|
||||
}
|
||||
elif ireq.comes_from:
|
||||
required_by.add(_comes_from_as_string(ireq))
|
||||
if required_by:
|
||||
annotation = ", ".join(sorted(required_by))
|
||||
line = "{:24}{}{}".format(
|
||||
|
|
|
@ -5,9 +5,13 @@ universal = 1
|
|||
license_file = LICENSE
|
||||
|
||||
[tool:pytest]
|
||||
norecursedirs = .* build dist venv test_data piptools/_compat/* piptools/_vendored/*
|
||||
norecursedirs = .* build dist venv test_data piptools/_compat/*
|
||||
testpaths = tests piptools
|
||||
filterwarnings = ignore::PendingDeprecationWarning:pip[.*]
|
||||
filterwarnings =
|
||||
ignore::PendingDeprecationWarning:pip\._vendor.+
|
||||
ignore::DeprecationWarning:pip\._vendor.+
|
||||
markers =
|
||||
network: mark tests that require internet access
|
||||
|
||||
[flake8]
|
||||
max-line-length = 88
|
||||
|
|
|
@ -24,9 +24,9 @@ setup(
|
|||
long_description_content_type="text/x-rst",
|
||||
packages=find_packages(exclude=["tests"]),
|
||||
package_data={},
|
||||
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*",
|
||||
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*",
|
||||
setup_requires=["setuptools_scm"],
|
||||
install_requires=["click>=6", "six"],
|
||||
install_requires=["click>=7", "six"],
|
||||
zip_safe=False,
|
||||
entry_points={
|
||||
"console_scripts": [
|
||||
|
@ -45,10 +45,10 @@ setup(
|
|||
"Programming Language :: Python :: 2",
|
||||
"Programming Language :: Python :: 2.7",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.4",
|
||||
"Programming Language :: Python :: 3.5",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
"Programming Language :: Python :: Implementation :: PyPy",
|
||||
"Topic :: System :: Systems Administration",
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
# Trigger an import of piptools, effectively injecting piptools/_vendored in sys.path
|
||||
import piptools # noqa
|
|
@ -1,12 +1,17 @@
|
|||
import json
|
||||
import os
|
||||
from contextlib import contextmanager
|
||||
from functools import partial
|
||||
from textwrap import dedent
|
||||
|
||||
import pytest
|
||||
from click.testing import CliRunner
|
||||
from pip._vendor.packaging.version import Version
|
||||
from pip._vendor.pkg_resources import Requirement
|
||||
from pytest import fixture
|
||||
|
||||
from .constants import MINIMAL_WHEELS_PATH
|
||||
|
||||
from piptools._compat import (
|
||||
InstallationCandidate,
|
||||
install_req_from_editable,
|
||||
|
@ -14,9 +19,16 @@ from piptools._compat import (
|
|||
)
|
||||
from piptools.cache import DependencyCache
|
||||
from piptools.exceptions import NoCandidateFound
|
||||
from piptools.repositories import PyPIRepository
|
||||
from piptools.repositories.base import BaseRepository
|
||||
from piptools.resolver import Resolver
|
||||
from piptools.utils import as_tuple, key_from_req, make_install_requirement
|
||||
from piptools.utils import (
|
||||
as_tuple,
|
||||
is_url_requirement,
|
||||
key_from_ireq,
|
||||
key_from_req,
|
||||
make_install_requirement,
|
||||
)
|
||||
|
||||
|
||||
class FakeRepository(BaseRepository):
|
||||
|
@ -40,25 +52,22 @@ class FakeRepository(BaseRepository):
|
|||
|
||||
versions = list(
|
||||
ireq.specifier.filter(
|
||||
self.index[key_from_req(ireq.req)], prereleases=prereleases
|
||||
self.index[key_from_ireq(ireq)], prereleases=prereleases
|
||||
)
|
||||
)
|
||||
if not versions:
|
||||
tried_versions = [
|
||||
InstallationCandidate(ireq.name, version, "https://fake.url.foo")
|
||||
for version in self.index[key_from_req(ireq.req)]
|
||||
for version in self.index[key_from_ireq(ireq)]
|
||||
]
|
||||
raise NoCandidateFound(ireq, tried_versions, ["https://fake.url.foo"])
|
||||
best_version = max(versions, key=Version)
|
||||
return make_install_requirement(
|
||||
key_from_req(ireq.req),
|
||||
best_version,
|
||||
ireq.extras,
|
||||
constraint=ireq.constraint,
|
||||
key_from_ireq(ireq), best_version, ireq.extras, constraint=ireq.constraint
|
||||
)
|
||||
|
||||
def get_dependencies(self, ireq):
|
||||
if ireq.editable:
|
||||
if ireq.editable or is_url_requirement(ireq):
|
||||
return self.editables[str(ireq.link)]
|
||||
|
||||
name, version, extras = as_tuple(ireq)
|
||||
|
@ -94,8 +103,12 @@ class FakeInstalledDistribution(object):
|
|||
def requires(self):
|
||||
return self.deps
|
||||
|
||||
def as_requirement(self):
|
||||
return self.req
|
||||
|
||||
def pytest_collection_modifyitems(config, items):
|
||||
for item in items:
|
||||
# Mark network tests as flaky
|
||||
if item.get_closest_marker("network") and "CI" in os.environ:
|
||||
item.add_marker(pytest.mark.flaky(reruns=3, reruns_delay=2))
|
||||
|
||||
|
||||
@fixture
|
||||
|
@ -108,6 +121,14 @@ def repository():
|
|||
return FakeRepository()
|
||||
|
||||
|
||||
@fixture
|
||||
def pypi_repository(tmpdir):
|
||||
return PyPIRepository(
|
||||
["--index-url", PyPIRepository.DEFAULT_INDEX_URL],
|
||||
cache_dir=str(tmpdir / "pypi-repo"),
|
||||
)
|
||||
|
||||
|
||||
@fixture
|
||||
def depcache(tmpdir):
|
||||
return DependencyCache(str(tmpdir / "dep-cache"))
|
||||
|
@ -147,3 +168,56 @@ def runner():
|
|||
def tmpdir_cwd(tmpdir):
|
||||
with tmpdir.as_cwd():
|
||||
yield tmpdir
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def make_pip_conf(tmpdir, monkeypatch):
|
||||
created_paths = []
|
||||
|
||||
def _make_pip_conf(content):
|
||||
pip_conf_file = "pip.conf" if os.name != "nt" else "pip.ini"
|
||||
path = (tmpdir / pip_conf_file).strpath
|
||||
|
||||
with open(path, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
monkeypatch.setenv("PIP_CONFIG_FILE", path)
|
||||
|
||||
created_paths.append(path)
|
||||
return path
|
||||
|
||||
try:
|
||||
yield _make_pip_conf
|
||||
finally:
|
||||
for path in created_paths:
|
||||
os.remove(path)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pip_conf(make_pip_conf):
|
||||
return make_pip_conf(
|
||||
dedent(
|
||||
"""\
|
||||
[global]
|
||||
no-index = true
|
||||
find-links = {wheels_path}
|
||||
""".format(
|
||||
wheels_path=MINIMAL_WHEELS_PATH
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pip_with_index_conf(make_pip_conf):
|
||||
return make_pip_conf(
|
||||
dedent(
|
||||
"""\
|
||||
[global]
|
||||
index-url = http://example.com
|
||||
find-links = {wheels_path}
|
||||
""".format(
|
||||
wheels_path=MINIMAL_WHEELS_PATH
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
import os
|
||||
|
||||
TEST_DATA_PATH = os.path.join(os.path.split(__file__)[0], "test_data")
|
||||
MINIMAL_WHEELS_PATH = os.path.join(TEST_DATA_PATH, "minimal_wheels")
|
||||
PACKAGES_PATH = os.path.join(TEST_DATA_PATH, "packages")
|
|
@ -17,9 +17,9 @@ def _read_cache_file_helper(to_write):
|
|||
:param str to_write: the content to write to the file
|
||||
:yield: the path to the temporary file
|
||||
"""
|
||||
# Create the file and write to it
|
||||
cache_file = NamedTemporaryFile(mode="w", delete=False)
|
||||
try:
|
||||
# Create the file and write to it
|
||||
cache_file = NamedTemporaryFile(mode="w", delete=False)
|
||||
cache_file.write(to_write)
|
||||
cache_file.close()
|
||||
|
||||
|
|
|
@ -5,51 +5,21 @@ from textwrap import dedent
|
|||
|
||||
import mock
|
||||
import pytest
|
||||
from click.testing import CliRunner
|
||||
from pip import __version__ as pip_version
|
||||
from pip._vendor.packaging.version import parse as parse_version
|
||||
from pytest import mark
|
||||
|
||||
from .constants import MINIMAL_WHEELS_PATH, PACKAGES_PATH
|
||||
from .utils import invoke
|
||||
|
||||
from piptools._compat.pip_compat import path_to_url
|
||||
from piptools.repositories import PyPIRepository
|
||||
from piptools._compat.pip_compat import PIP_VERSION, path_to_url
|
||||
from piptools.scripts.compile import cli
|
||||
|
||||
PIP_VERSION = parse_version(os.environ.get("PIP", pip_version))
|
||||
TEST_DATA_PATH = os.path.join(os.path.split(__file__)[0], "test_data")
|
||||
MINIMAL_WHEELS_PATH = os.path.join(TEST_DATA_PATH, "minimal_wheels")
|
||||
|
||||
fail_below_pip9 = pytest.mark.xfail(
|
||||
PIP_VERSION < parse_version("9"), reason="needs pip 9 or greater"
|
||||
)
|
||||
@pytest.fixture(autouse=True)
|
||||
def temp_dep_cache(tmpdir, monkeypatch):
|
||||
monkeypatch.setenv("PIP_TOOLS_CACHE_DIR", str(tmpdir / "cache"))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pip_conf(tmpdir, monkeypatch):
|
||||
test_conf = dedent(
|
||||
"""\
|
||||
[global]
|
||||
index-url = http://example.com
|
||||
trusted-host = example.com
|
||||
"""
|
||||
)
|
||||
|
||||
pip_conf_file = "pip.conf" if os.name != "nt" else "pip.ini"
|
||||
path = (tmpdir / pip_conf_file).strpath
|
||||
|
||||
with open(path, "w") as f:
|
||||
f.write(test_conf)
|
||||
|
||||
monkeypatch.setenv("PIP_CONFIG_FILE", path)
|
||||
|
||||
try:
|
||||
yield path
|
||||
finally:
|
||||
os.remove(path)
|
||||
|
||||
|
||||
def test_default_pip_conf_read(pip_conf, runner):
|
||||
def test_default_pip_conf_read(pip_with_index_conf, runner):
|
||||
# preconditions
|
||||
with open("requirements.in", "w"):
|
||||
pass
|
||||
|
@ -60,7 +30,7 @@ def test_default_pip_conf_read(pip_conf, runner):
|
|||
assert "--index-url http://example.com" in out.stderr
|
||||
|
||||
|
||||
def test_command_line_overrides_pip_conf(pip_conf, runner):
|
||||
def test_command_line_overrides_pip_conf(pip_with_index_conf, runner):
|
||||
# preconditions
|
||||
with open("requirements.in", "w"):
|
||||
pass
|
||||
|
@ -71,20 +41,27 @@ def test_command_line_overrides_pip_conf(pip_conf, runner):
|
|||
|
||||
|
||||
def test_command_line_setuptools_read(pip_conf, runner):
|
||||
package = open("setup.py", "w")
|
||||
package.write(
|
||||
dedent(
|
||||
"""\
|
||||
from setuptools import setup
|
||||
setup(install_requires=[])
|
||||
"""
|
||||
with open("setup.py", "w") as package:
|
||||
package.write(
|
||||
dedent(
|
||||
"""\
|
||||
from setuptools import setup
|
||||
setup(
|
||||
name="fake-setuptools-a",
|
||||
install_requires=["small-fake-a==0.1"]
|
||||
)
|
||||
"""
|
||||
)
|
||||
)
|
||||
)
|
||||
package.close()
|
||||
out = runner.invoke(cli)
|
||||
|
||||
# check that pip-compile generated a configuration
|
||||
assert "This file is autogenerated by pip-compile" in out.stderr
|
||||
assert (
|
||||
"small-fake-a==0.1 # via fake-setuptools-a (setup.py)"
|
||||
in out.stderr.splitlines()
|
||||
)
|
||||
|
||||
# check that pip-compile generated a configuration file
|
||||
assert os.path.exists("requirements.txt")
|
||||
|
||||
|
||||
|
@ -103,29 +80,28 @@ def test_command_line_setuptools_read(pip_conf, runner):
|
|||
(["setup.py", "--output-file", "output.txt"], "output.txt"),
|
||||
],
|
||||
)
|
||||
def test_command_line_setuptools_output_file(pip_conf, options, expected_output_file):
|
||||
def test_command_line_setuptools_output_file(
|
||||
pip_conf, runner, options, expected_output_file
|
||||
):
|
||||
"""
|
||||
Test the output files for setup.py as a requirement file.
|
||||
"""
|
||||
runner = CliRunner(mix_stderr=False)
|
||||
with runner.isolated_filesystem():
|
||||
package = open("setup.py", "w")
|
||||
with open("setup.py", "w") as package:
|
||||
package.write(
|
||||
dedent(
|
||||
"""\
|
||||
from setuptools import setup
|
||||
setup(install_requires=[])
|
||||
"""
|
||||
from setuptools import setup
|
||||
setup(install_requires=[])
|
||||
"""
|
||||
)
|
||||
)
|
||||
package.close()
|
||||
|
||||
out = runner.invoke(cli, options)
|
||||
assert out.exit_code == 0
|
||||
assert os.path.exists(expected_output_file)
|
||||
out = runner.invoke(cli, options)
|
||||
assert out.exit_code == 0
|
||||
assert os.path.exists(expected_output_file)
|
||||
|
||||
|
||||
def test_find_links_option(pip_conf, runner):
|
||||
def test_find_links_option(runner):
|
||||
with open("requirements.in", "w") as req_in:
|
||||
req_in.write("-f ./libs3")
|
||||
|
||||
|
@ -142,7 +118,7 @@ def test_find_links_option(pip_conf, runner):
|
|||
)
|
||||
|
||||
|
||||
def test_extra_index_option(pip_conf, runner):
|
||||
def test_extra_index_option(pip_with_index_conf, runner):
|
||||
with open("requirements.in", "w"):
|
||||
pass
|
||||
out = runner.invoke(
|
||||
|
@ -186,6 +162,14 @@ def test_trusted_host_no_emit(pip_conf, runner):
|
|||
assert "--trusted-host example.com" not in out.stderr
|
||||
|
||||
|
||||
def test_find_links_no_emit(pip_conf, runner):
|
||||
with open("requirements.in", "w"):
|
||||
pass
|
||||
out = runner.invoke(cli, ["-v", "--no-emit-find-links"])
|
||||
assert "--find-links" not in out.stderr
|
||||
|
||||
|
||||
@pytest.mark.network
|
||||
def test_realistic_complex_sub_dependencies(runner):
|
||||
wheels_dir = "wheels"
|
||||
|
||||
|
@ -197,7 +181,7 @@ def test_realistic_complex_sub_dependencies(runner):
|
|||
"--no-deps",
|
||||
"-w",
|
||||
wheels_dir,
|
||||
os.path.join(TEST_DATA_PATH, "fake_package", "."),
|
||||
os.path.join(PACKAGES_PATH, "fake_with_deps", "."),
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -221,9 +205,9 @@ def test_run_as_module_compile():
|
|||
assert status == 0
|
||||
|
||||
|
||||
def test_editable_package(runner):
|
||||
def test_editable_package(pip_conf, runner):
|
||||
""" piptools can compile an editable """
|
||||
fake_package_dir = os.path.join(TEST_DATA_PATH, "small_fake_package")
|
||||
fake_package_dir = os.path.join(PACKAGES_PATH, "small_fake_with_deps")
|
||||
fake_package_dir = path_to_url(fake_package_dir)
|
||||
with open("requirements.in", "w") as req_in:
|
||||
req_in.write("-e " + fake_package_dir) # require editable fake package
|
||||
|
@ -232,9 +216,10 @@ def test_editable_package(runner):
|
|||
|
||||
assert out.exit_code == 0
|
||||
assert fake_package_dir in out.stderr
|
||||
assert "six==1.10.0" in out.stderr
|
||||
assert "small-fake-a==0.1" in out.stderr
|
||||
|
||||
|
||||
@pytest.mark.network
|
||||
def test_editable_package_vcs(runner):
|
||||
vcs_package = (
|
||||
"git+git://github.com/jazzband/pip-tools@"
|
||||
|
@ -250,32 +235,31 @@ def test_editable_package_vcs(runner):
|
|||
|
||||
|
||||
def test_locally_available_editable_package_is_not_archived_in_cache_dir(
|
||||
tmpdir, runner
|
||||
pip_conf, tmpdir, runner
|
||||
):
|
||||
"""
|
||||
piptools will not create an archive for a locally available editable requirement
|
||||
"""
|
||||
cache_dir = tmpdir.mkdir("cache_dir")
|
||||
|
||||
fake_package_dir = os.path.join(TEST_DATA_PATH, "small_fake_package")
|
||||
fake_package_dir = os.path.join(PACKAGES_PATH, "small_fake_with_deps")
|
||||
fake_package_dir = path_to_url(fake_package_dir)
|
||||
|
||||
with mock.patch("piptools.repositories.pypi.CACHE_DIR", new=str(cache_dir)):
|
||||
with open("requirements.in", "w") as req_in:
|
||||
req_in.write("-e " + fake_package_dir) # require editable fake package
|
||||
with open("requirements.in", "w") as req_in:
|
||||
req_in.write("-e " + fake_package_dir) # require editable fake package
|
||||
|
||||
out = runner.invoke(cli, ["-n"])
|
||||
out = runner.invoke(cli, ["-n", "--rebuild", "--cache-dir", str(cache_dir)])
|
||||
|
||||
assert out.exit_code == 0
|
||||
assert fake_package_dir in out.stderr
|
||||
assert "six==1.10.0" in out.stderr
|
||||
assert out.exit_code == 0
|
||||
assert fake_package_dir in out.stderr
|
||||
assert "small-fake-a==0.1" in out.stderr
|
||||
|
||||
# we should not find any archived file in {cache_dir}/pkgs
|
||||
assert not os.listdir(os.path.join(str(cache_dir), "pkgs"))
|
||||
|
||||
|
||||
@mark.parametrize(
|
||||
("line", "dependency", "rewritten_line"),
|
||||
("line", "dependency"),
|
||||
[
|
||||
# zip URL
|
||||
# use pip-tools version prior to its use of setuptools_scm,
|
||||
|
@ -284,14 +268,12 @@ def test_locally_available_editable_package_is_not_archived_in_cache_dir(
|
|||
"https://github.com/jazzband/pip-tools/archive/"
|
||||
"7d86c8d3ecd1faa6be11c7ddc6b29a30ffd1dae3.zip",
|
||||
"\nclick==",
|
||||
None,
|
||||
),
|
||||
# scm URL
|
||||
(
|
||||
"git+git://github.com/jazzband/pip-tools@"
|
||||
"7d86c8d3ecd1faa6be11c7ddc6b29a30ffd1dae3",
|
||||
"\nclick==",
|
||||
None,
|
||||
),
|
||||
# wheel URL
|
||||
(
|
||||
|
@ -299,8 +281,24 @@ def test_locally_available_editable_package_is_not_archived_in_cache_dir(
|
|||
"89872db07ae70770fba97205b0737c17ef013d0d1c790"
|
||||
"899c16bb8bac419/pip_tools-3.6.1-py2.py3-none-any.whl",
|
||||
"\nclick==",
|
||||
None,
|
||||
),
|
||||
],
|
||||
)
|
||||
@mark.parametrize(("generate_hashes",), [(True,), (False,)])
|
||||
@pytest.mark.network
|
||||
def test_url_package(runner, line, dependency, generate_hashes):
|
||||
with open("requirements.in", "w") as req_in:
|
||||
req_in.write(line)
|
||||
out = runner.invoke(
|
||||
cli, ["-n", "--rebuild"] + (["--generate-hashes"] if generate_hashes else [])
|
||||
)
|
||||
assert out.exit_code == 0
|
||||
assert dependency in out.stderr
|
||||
|
||||
|
||||
@mark.parametrize(
|
||||
("line", "dependency", "rewritten_line"),
|
||||
[
|
||||
# file:// wheel URL
|
||||
(
|
||||
path_to_url(
|
||||
|
@ -308,13 +306,13 @@ def test_locally_available_editable_package_is_not_archived_in_cache_dir(
|
|||
MINIMAL_WHEELS_PATH, "small_fake_with_deps-0.1-py2.py3-none-any.whl"
|
||||
)
|
||||
),
|
||||
"\nsix==",
|
||||
"\nsmall-fake-a==0.1",
|
||||
None,
|
||||
),
|
||||
# file:// directory
|
||||
(
|
||||
path_to_url(os.path.join(TEST_DATA_PATH, "small_fake_package")),
|
||||
"\nsix==",
|
||||
path_to_url(os.path.join(PACKAGES_PATH, "small_fake_with_deps")),
|
||||
"\nsmall-fake-a==0.1",
|
||||
None,
|
||||
),
|
||||
# bare path
|
||||
|
@ -323,7 +321,7 @@ def test_locally_available_editable_package_is_not_archived_in_cache_dir(
|
|||
os.path.join(
|
||||
MINIMAL_WHEELS_PATH, "small_fake_with_deps-0.1-py2.py3-none-any.whl"
|
||||
),
|
||||
"\nsix==",
|
||||
"\nsmall-fake-a==0.1",
|
||||
path_to_url(
|
||||
os.path.join(
|
||||
MINIMAL_WHEELS_PATH, "small_fake_with_deps-0.1-py2.py3-none-any.whl"
|
||||
|
@ -333,7 +331,9 @@ def test_locally_available_editable_package_is_not_archived_in_cache_dir(
|
|||
],
|
||||
)
|
||||
@mark.parametrize(("generate_hashes",), [(True,), (False,)])
|
||||
def test_url_package(runner, line, dependency, rewritten_line, generate_hashes):
|
||||
def test_local_url_package(
|
||||
pip_conf, runner, line, dependency, rewritten_line, generate_hashes
|
||||
):
|
||||
if rewritten_line is None:
|
||||
rewritten_line = line
|
||||
with open("requirements.in", "w") as req_in:
|
||||
|
@ -346,22 +346,22 @@ def test_url_package(runner, line, dependency, rewritten_line, generate_hashes):
|
|||
assert dependency in out.stderr
|
||||
|
||||
|
||||
def test_input_file_without_extension(runner):
|
||||
def test_input_file_without_extension(pip_conf, runner):
|
||||
"""
|
||||
piptools can compile a file without an extension,
|
||||
and add .txt as the defaut output file extension.
|
||||
"""
|
||||
with open("requirements", "w") as req_in:
|
||||
req_in.write("six==1.10.0")
|
||||
req_in.write("small-fake-a==0.1")
|
||||
|
||||
out = runner.invoke(cli, ["requirements"])
|
||||
|
||||
assert out.exit_code == 0
|
||||
assert "six==1.10.0" in out.stderr
|
||||
assert "small-fake-a==0.1" in out.stderr
|
||||
assert os.path.exists("requirements.txt")
|
||||
|
||||
|
||||
def test_upgrade_packages_option(runner):
|
||||
def test_upgrade_packages_option(pip_conf, runner):
|
||||
"""
|
||||
piptools respects --upgrade-package/-P inline list.
|
||||
"""
|
||||
|
@ -370,14 +370,30 @@ def test_upgrade_packages_option(runner):
|
|||
with open("requirements.txt", "w") as req_in:
|
||||
req_in.write("small-fake-a==0.1\nsmall-fake-b==0.1")
|
||||
|
||||
out = runner.invoke(cli, ["-P", "small-fake-b", "-f", MINIMAL_WHEELS_PATH])
|
||||
out = runner.invoke(cli, ["--no-annotate", "-P", "small-fake-b"])
|
||||
|
||||
assert out.exit_code == 0
|
||||
assert "small-fake-a==0.1" in out.stderr
|
||||
assert "small-fake-b==0.3" in out.stderr
|
||||
assert "small-fake-a==0.1" in out.stderr.splitlines()
|
||||
assert "small-fake-b==0.3" in out.stderr.splitlines()
|
||||
|
||||
|
||||
def test_upgrade_packages_option_no_existing_file(runner):
|
||||
def test_upgrade_packages_option_irrelevant(pip_conf, runner):
|
||||
"""
|
||||
piptools ignores --upgrade-package/-P items not already constrained.
|
||||
"""
|
||||
with open("requirements.in", "w") as req_in:
|
||||
req_in.write("small-fake-a")
|
||||
with open("requirements.txt", "w") as req_in:
|
||||
req_in.write("small-fake-a==0.1")
|
||||
|
||||
out = runner.invoke(cli, ["--no-annotate", "--upgrade-package", "small-fake-b"])
|
||||
|
||||
assert out.exit_code == 0
|
||||
assert "small-fake-a==0.1" in out.stderr.splitlines()
|
||||
assert "small-fake-b==0.3" not in out.stderr.splitlines()
|
||||
|
||||
|
||||
def test_upgrade_packages_option_no_existing_file(pip_conf, runner):
|
||||
"""
|
||||
piptools respects --upgrade-package/-P inline list when the output file
|
||||
doesn't exist.
|
||||
|
@ -385,44 +401,54 @@ def test_upgrade_packages_option_no_existing_file(runner):
|
|||
with open("requirements.in", "w") as req_in:
|
||||
req_in.write("small-fake-a\nsmall-fake-b")
|
||||
|
||||
out = runner.invoke(cli, ["-P", "small-fake-b", "-f", MINIMAL_WHEELS_PATH])
|
||||
out = runner.invoke(cli, ["--no-annotate", "-P", "small-fake-b"])
|
||||
|
||||
assert out.exit_code == 0
|
||||
assert "small-fake-a==0.2" in out.stderr
|
||||
assert "small-fake-b==0.3" in out.stderr
|
||||
assert "small-fake-a==0.2" in out.stderr.splitlines()
|
||||
assert "small-fake-b==0.3" in out.stderr.splitlines()
|
||||
|
||||
|
||||
def test_upgrade_packages_version_option(runner):
|
||||
@pytest.mark.parametrize(
|
||||
"current_package, upgraded_package",
|
||||
(
|
||||
pytest.param("small-fake-b==0.1", "small-fake-b==0.3", id="upgrade"),
|
||||
pytest.param("small-fake-b==0.3", "small-fake-b==0.1", id="downgrade"),
|
||||
),
|
||||
)
|
||||
def test_upgrade_packages_version_option(
|
||||
pip_conf, runner, current_package, upgraded_package
|
||||
):
|
||||
"""
|
||||
piptools respects --upgrade-package/-P inline list with specified versions.
|
||||
"""
|
||||
with open("requirements.in", "w") as req_in:
|
||||
req_in.write("small-fake-a\nsmall-fake-b")
|
||||
with open("requirements.txt", "w") as req_in:
|
||||
req_in.write("small-fake-a==0.1\nsmall-fake-b==0.1")
|
||||
req_in.write("small-fake-a==0.1\n" + current_package)
|
||||
|
||||
out = runner.invoke(cli, ["-P", "small-fake-b==0.2", "-f", MINIMAL_WHEELS_PATH])
|
||||
out = runner.invoke(cli, ["--no-annotate", "--upgrade-package", upgraded_package])
|
||||
|
||||
assert out.exit_code == 0
|
||||
assert "small-fake-a==0.1" in out.stderr
|
||||
assert "small-fake-b==0.2" in out.stderr
|
||||
stderr_lines = out.stderr.splitlines()
|
||||
assert "small-fake-a==0.1" in stderr_lines
|
||||
assert upgraded_package in stderr_lines
|
||||
|
||||
|
||||
def test_upgrade_packages_version_option_no_existing_file(runner):
|
||||
def test_upgrade_packages_version_option_no_existing_file(pip_conf, runner):
|
||||
"""
|
||||
piptools respects --upgrade-package/-P inline list with specified versions.
|
||||
"""
|
||||
with open("requirements.in", "w") as req_in:
|
||||
req_in.write("small-fake-a\nsmall-fake-b")
|
||||
|
||||
out = runner.invoke(cli, ["-P", "small-fake-b==0.2", "-f", MINIMAL_WHEELS_PATH])
|
||||
out = runner.invoke(cli, ["-P", "small-fake-b==0.2"])
|
||||
|
||||
assert out.exit_code == 0
|
||||
assert "small-fake-a==0.2" in out.stderr
|
||||
assert "small-fake-b==0.2" in out.stderr
|
||||
|
||||
|
||||
def test_upgrade_packages_version_option_and_upgrade(runner):
|
||||
def test_upgrade_packages_version_option_and_upgrade(pip_conf, runner):
|
||||
"""
|
||||
piptools respects --upgrade-package/-P inline list with specified versions
|
||||
whilst also doing --upgrade.
|
||||
|
@ -432,16 +458,14 @@ def test_upgrade_packages_version_option_and_upgrade(runner):
|
|||
with open("requirements.txt", "w") as req_in:
|
||||
req_in.write("small-fake-a==0.1\nsmall-fake-b==0.1")
|
||||
|
||||
out = runner.invoke(
|
||||
cli, ["--upgrade", "-P", "small-fake-b==0.1", "-f", MINIMAL_WHEELS_PATH]
|
||||
)
|
||||
out = runner.invoke(cli, ["--upgrade", "-P", "small-fake-b==0.1"])
|
||||
|
||||
assert out.exit_code == 0
|
||||
assert "small-fake-a==0.2" in out.stderr
|
||||
assert "small-fake-b==0.1" in out.stderr
|
||||
|
||||
|
||||
def test_upgrade_packages_version_option_and_upgrade_no_existing_file(runner):
|
||||
def test_upgrade_packages_version_option_and_upgrade_no_existing_file(pip_conf, runner):
|
||||
"""
|
||||
piptools respects --upgrade-package/-P inline list with specified versions
|
||||
whilst also doing --upgrade and the output file doesn't exist.
|
||||
|
@ -449,9 +473,7 @@ def test_upgrade_packages_version_option_and_upgrade_no_existing_file(runner):
|
|||
with open("requirements.in", "w") as req_in:
|
||||
req_in.write("small-fake-a\nsmall-fake-b")
|
||||
|
||||
out = runner.invoke(
|
||||
cli, ["--upgrade", "-P", "small-fake-b==0.1", "-f", MINIMAL_WHEELS_PATH]
|
||||
)
|
||||
out = runner.invoke(cli, ["--upgrade", "-P", "small-fake-b==0.1"])
|
||||
|
||||
assert out.exit_code == 0
|
||||
assert "small-fake-a==0.2" in out.stderr
|
||||
|
@ -462,38 +484,53 @@ def test_quiet_option(runner):
|
|||
with open("requirements", "w"):
|
||||
pass
|
||||
out = runner.invoke(cli, ["--quiet", "-n", "requirements"])
|
||||
# Pinned requirements result has not been written to output
|
||||
assert "Dry-run, so nothing updated." == out.stderr.strip()
|
||||
# Pinned requirements result has not been written to output.
|
||||
assert not out.stderr_bytes
|
||||
|
||||
|
||||
def test_generate_hashes_with_editable(runner):
|
||||
small_fake_package_dir = os.path.join(TEST_DATA_PATH, "small_fake_package")
|
||||
def test_dry_run_noisy_option(runner):
|
||||
with open("requirements", "w"):
|
||||
pass
|
||||
out = runner.invoke(cli, ["--dry-run", "requirements"])
|
||||
# Dry-run message has been written to output
|
||||
assert "Dry-run, so nothing updated." in out.stderr.splitlines()
|
||||
|
||||
|
||||
def test_dry_run_quiet_option(runner):
|
||||
with open("requirements", "w"):
|
||||
pass
|
||||
out = runner.invoke(cli, ["--dry-run", "--quiet", "requirements"])
|
||||
# Dry-run message has not been written to output.
|
||||
assert not out.stderr_bytes
|
||||
|
||||
|
||||
def test_generate_hashes_with_editable(pip_conf, runner):
|
||||
small_fake_package_dir = os.path.join(PACKAGES_PATH, "small_fake_with_deps")
|
||||
small_fake_package_url = path_to_url(small_fake_package_dir)
|
||||
with open("requirements.in", "w") as fp:
|
||||
fp.write("-e {}\n".format(small_fake_package_url))
|
||||
fp.write("pytz==2017.2\n")
|
||||
out = runner.invoke(
|
||||
cli, ["--generate-hashes", "--index-url", PyPIRepository.DEFAULT_INDEX_URL]
|
||||
)
|
||||
out = runner.invoke(cli, ["--no-annotate", "--generate-hashes"])
|
||||
expected = (
|
||||
"-e {}\n"
|
||||
"pytz==2017.2 \\\n"
|
||||
" --hash=sha256:d1d6729c85acea542367138286"
|
||||
"8627129432fba9a89ecbb248d8d1c7a9f01c67 \\\n"
|
||||
" --hash=sha256:f5c056e8f62d45ba8215e5cb8f"
|
||||
"50dfccb198b4b9fbea8500674f3443e4689589\n"
|
||||
"small-fake-a==0.1 \\\n"
|
||||
" --hash=sha256:5e6071ee6e4c59e0d0408d366f"
|
||||
"e9b66781d2cf01be9a6e19a2433bb3c5336330\n"
|
||||
"small-fake-b==0.1 \\\n"
|
||||
" --hash=sha256:acdba8f8b8a816213c30d5310c"
|
||||
"3fe296c0107b16ed452062f7f994a5672e3b3f\n"
|
||||
).format(small_fake_package_url)
|
||||
assert out.exit_code == 0
|
||||
assert expected in out.stderr
|
||||
|
||||
|
||||
@pytest.mark.network
|
||||
def test_generate_hashes_with_url(runner):
|
||||
with open("requirements.in", "w") as fp:
|
||||
fp.write(
|
||||
"https://github.com/jazzband/pip-tools/archive/"
|
||||
"7d86c8d3ecd1faa6be11c7ddc6b29a30ffd1dae3.zip#egg=pip-tools\n"
|
||||
)
|
||||
out = runner.invoke(cli, ["--generate-hashes"])
|
||||
out = runner.invoke(cli, ["--no-annotate", "--generate-hashes"])
|
||||
expected = (
|
||||
"https://github.com/jazzband/pip-tools/archive/"
|
||||
"7d86c8d3ecd1faa6be11c7ddc6b29a30ffd1dae3.zip#egg=pip-tools \\\n"
|
||||
|
@ -504,37 +541,38 @@ def test_generate_hashes_with_url(runner):
|
|||
assert expected in out.stderr
|
||||
|
||||
|
||||
def test_generate_hashes_verbose(runner):
|
||||
def test_generate_hashes_verbose(pip_conf, runner):
|
||||
"""
|
||||
The hashes generation process should show a progress.
|
||||
"""
|
||||
with open("requirements.in", "w") as fp:
|
||||
fp.write("pytz==2017.2")
|
||||
fp.write("small-fake-a==0.1")
|
||||
|
||||
out = runner.invoke(cli, ["--generate-hashes", "-v"])
|
||||
|
||||
expected_verbose_text = "Generating hashes:\n pytz\n"
|
||||
expected_verbose_text = "Generating hashes:\n small-fake-a\n"
|
||||
assert expected_verbose_text in out.stderr
|
||||
|
||||
|
||||
@fail_below_pip9
|
||||
def test_filter_pip_markers(runner):
|
||||
@pytest.mark.skipif(PIP_VERSION < (9,), reason="needs pip 9 or greater")
|
||||
def test_filter_pip_markers(pip_conf, runner):
|
||||
"""
|
||||
Check that pip-compile works with pip environment markers (PEP496)
|
||||
"""
|
||||
with open("requirements", "w") as req_in:
|
||||
req_in.write("six==1.10.0\n" "unknown_package==0.1; python_version == '1'")
|
||||
req_in.write(
|
||||
"small-fake-a==0.1\n" "unknown_package==0.1; python_version == '1'"
|
||||
)
|
||||
|
||||
out = runner.invoke(cli, ["-n", "requirements"])
|
||||
|
||||
assert out.exit_code == 0
|
||||
assert "six==1.10.0" in out.stderr
|
||||
assert "small-fake-a==0.1" in out.stderr
|
||||
assert "unknown_package" not in out.stderr
|
||||
|
||||
|
||||
def test_no_candidates(runner):
|
||||
def test_no_candidates(pip_conf, runner):
|
||||
with open("requirements", "w") as req_in:
|
||||
req_in.write("six>1.0b0,<1.0b0")
|
||||
req_in.write("small-fake-a>0.3b1,<0.3b2")
|
||||
|
||||
out = runner.invoke(cli, ["-n", "requirements"])
|
||||
|
||||
|
@ -542,9 +580,9 @@ def test_no_candidates(runner):
|
|||
assert "Skipped pre-versions:" in out.stderr
|
||||
|
||||
|
||||
def test_no_candidates_pre(runner):
|
||||
def test_no_candidates_pre(pip_conf, runner):
|
||||
with open("requirements", "w") as req_in:
|
||||
req_in.write("six>1.0b0,<1.0b0")
|
||||
req_in.write("small-fake-a>0.3b1,<0.3b1")
|
||||
|
||||
out = runner.invoke(cli, ["-n", "requirements", "--pre"])
|
||||
|
||||
|
@ -552,7 +590,7 @@ def test_no_candidates_pre(runner):
|
|||
assert "Tried pre-versions:" in out.stderr
|
||||
|
||||
|
||||
def test_default_index_url(pip_conf):
|
||||
def test_default_index_url(pip_with_index_conf):
|
||||
status, output = invoke([sys.executable, "-m", "piptools", "compile", "--help"])
|
||||
output = output.decode("utf-8")
|
||||
|
||||
|
@ -589,15 +627,15 @@ def test_not_specified_input_file(runner):
|
|||
assert out.exit_code == 2
|
||||
|
||||
|
||||
def test_stdin(runner):
|
||||
def test_stdin(pip_conf, runner):
|
||||
"""
|
||||
Test compile requirements from STDIN.
|
||||
"""
|
||||
out = runner.invoke(
|
||||
cli, ["-", "--output-file", "requirements.txt", "-n"], input="six==1.10.0"
|
||||
cli, ["-", "--output-file", "requirements.txt", "-n"], input="small-fake-a==0.1"
|
||||
)
|
||||
|
||||
assert "six==1.10.0" in out.stderr
|
||||
assert "small-fake-a==0.1 # via -r -" in out.stderr.splitlines()
|
||||
|
||||
|
||||
def test_multiple_input_files_without_output_file(runner):
|
||||
|
@ -621,18 +659,25 @@ def test_multiple_input_files_without_output_file(runner):
|
|||
@pytest.mark.parametrize(
|
||||
"option, expected",
|
||||
[
|
||||
("--annotate", "six==1.10.0 # via small-fake-with-deps\n"),
|
||||
("--no-annotate", "six==1.10.0\n"),
|
||||
(
|
||||
"--annotate",
|
||||
"small-fake-a==0.1 "
|
||||
"# via -c constraints.txt, small-fake-with-deps\n",
|
||||
),
|
||||
("--no-annotate", "small-fake-a==0.1\n"),
|
||||
],
|
||||
)
|
||||
def test_annotate_option(pip_conf, runner, option, expected):
|
||||
"""
|
||||
The output lines has have annotations if option is turned on.
|
||||
"""
|
||||
with open("constraints.txt", "w") as constraints_in:
|
||||
constraints_in.write("small-fake-a==0.1")
|
||||
with open("requirements.in", "w") as req_in:
|
||||
req_in.write("-c constraints.txt\n")
|
||||
req_in.write("small_fake_with_deps")
|
||||
|
||||
out = runner.invoke(cli, [option, "-n", "-f", MINIMAL_WHEELS_PATH])
|
||||
out = runner.invoke(cli, [option, "-n"])
|
||||
|
||||
assert expected in out.stderr
|
||||
assert out.exit_code == 0
|
||||
|
@ -640,19 +685,19 @@ def test_annotate_option(pip_conf, runner, option, expected):
|
|||
|
||||
@pytest.mark.parametrize(
|
||||
"option, expected",
|
||||
[("--allow-unsafe", "\nsetuptools=="), (None, "\n# setuptools==")],
|
||||
[("--allow-unsafe", "small-fake-a==0.1"), (None, "# small-fake-a")],
|
||||
)
|
||||
def test_allow_unsafe_option(runner, option, expected):
|
||||
def test_allow_unsafe_option(pip_conf, monkeypatch, runner, option, expected):
|
||||
"""
|
||||
Unsafe packages are printed as expected with and without --allow-unsafe.
|
||||
"""
|
||||
|
||||
monkeypatch.setattr("piptools.resolver.UNSAFE_PACKAGES", {"small-fake-a"})
|
||||
with open("requirements.in", "w") as req_in:
|
||||
req_in.write(path_to_url(os.path.join(TEST_DATA_PATH, "small_fake_package")))
|
||||
req_in.write(path_to_url(os.path.join(PACKAGES_PATH, "small_fake_with_deps")))
|
||||
|
||||
out = runner.invoke(cli, [option] if option else [])
|
||||
out = runner.invoke(cli, ["--no-annotate", option] if option else [])
|
||||
|
||||
assert expected in out.stderr
|
||||
assert expected in out.stderr.splitlines()
|
||||
assert out.exit_code == 0
|
||||
|
||||
|
||||
|
@ -660,29 +705,28 @@ def test_allow_unsafe_option(runner, option, expected):
|
|||
"option, attr, expected",
|
||||
[("--cert", "cert", "foo.crt"), ("--client-cert", "client_cert", "bar.pem")],
|
||||
)
|
||||
@mock.patch("piptools.scripts.compile.PyPIRepository")
|
||||
def test_cert_option(MockPyPIRepository, runner, option, attr, expected):
|
||||
@mock.patch("piptools.scripts.compile.parse_requirements")
|
||||
def test_cert_option(parse_requirements, runner, option, attr, expected):
|
||||
"""
|
||||
The options --cert and --client-crt have to be passed to the PyPIRepository.
|
||||
"""
|
||||
with open("requirements.in", "w") as req_in:
|
||||
req_in.write("six==1.10.0")
|
||||
with open("requirements.in", "w"):
|
||||
pass
|
||||
|
||||
out = runner.invoke(cli, [option, expected])
|
||||
runner.invoke(cli, [option, expected])
|
||||
|
||||
assert "six==1.10.0" in out.stderr
|
||||
|
||||
# Ensure the pip_options in PyPIRepository has the expected option
|
||||
assert [
|
||||
getattr(call[0][0], attr) for call in MockPyPIRepository.call_args_list
|
||||
] == [expected]
|
||||
# Ensure the options in parse_requirements has the expected option
|
||||
assert getattr(parse_requirements.call_args.kwargs["options"], attr) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"option, expected", [("--build-isolation", True), ("--no-build-isolation", False)]
|
||||
)
|
||||
@mock.patch("piptools.scripts.compile.PyPIRepository")
|
||||
def test_build_isolation_option(MockPyPIRepository, runner, option, expected):
|
||||
@mock.patch("piptools.scripts.compile.parse_requirements") # prevent to parse
|
||||
def test_build_isolation_option(
|
||||
parse_requirements, PyPIRepository, runner, option, expected
|
||||
):
|
||||
"""
|
||||
A value of the --build-isolation/--no-build-isolation flag
|
||||
must be passed to the PyPIRepository.
|
||||
|
@ -693,7 +737,7 @@ def test_build_isolation_option(MockPyPIRepository, runner, option, expected):
|
|||
runner.invoke(cli, [option])
|
||||
|
||||
# Ensure the build_isolation option in PyPIRepository has the expected value.
|
||||
assert [call[0][2] for call in MockPyPIRepository.call_args_list] == [expected]
|
||||
assert PyPIRepository.call_args.kwargs["build_isolation"] is expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
@ -707,7 +751,7 @@ def test_build_isolation_option(MockPyPIRepository, runner, option, expected):
|
|||
(True, True, "small-fake-a==0.3b1"),
|
||||
],
|
||||
)
|
||||
def test_pre_option(runner, cli_option, infile_option, expected_package):
|
||||
def test_pre_option(pip_conf, runner, cli_option, infile_option, expected_package):
|
||||
"""
|
||||
Tests pip-compile respects --pre option.
|
||||
"""
|
||||
|
@ -716,9 +760,7 @@ def test_pre_option(runner, cli_option, infile_option, expected_package):
|
|||
req_in.write("--pre\n")
|
||||
req_in.write("small-fake-a\n")
|
||||
|
||||
out = runner.invoke(
|
||||
cli, ["-n", "-f", MINIMAL_WHEELS_PATH] + (["-p"] if cli_option else [])
|
||||
)
|
||||
out = runner.invoke(cli, ["--no-annotate", "-n"] + (["-p"] if cli_option else []))
|
||||
|
||||
assert out.exit_code == 0, out.stderr
|
||||
assert expected_package in out.stderr.splitlines(), out.stderr
|
||||
|
@ -735,16 +777,14 @@ def test_pre_option(runner, cli_option, infile_option, expected_package):
|
|||
["--upgrade-package", "small-fake-a", "--output-file", "requirements.txt"],
|
||||
],
|
||||
)
|
||||
def test_dry_run_option(runner, add_options):
|
||||
def test_dry_run_option(pip_conf, runner, add_options):
|
||||
"""
|
||||
Tests pip-compile doesn't create requirements.txt file on dry-run.
|
||||
"""
|
||||
with open("requirements.in", "w") as req_in:
|
||||
req_in.write("small-fake-a\n")
|
||||
|
||||
out = runner.invoke(
|
||||
cli, ["--dry-run", "--find-links", MINIMAL_WHEELS_PATH] + add_options
|
||||
)
|
||||
out = runner.invoke(cli, ["--no-annotate", "--dry-run"] + add_options)
|
||||
|
||||
assert out.exit_code == 0, out.stderr
|
||||
assert "small-fake-a==0.2" in out.stderr.splitlines()
|
||||
|
@ -766,7 +806,7 @@ def test_dry_run_option(runner, add_options):
|
|||
],
|
||||
)
|
||||
def test_dry_run_doesnt_touch_output_file(
|
||||
runner, add_options, expected_cli_output_package
|
||||
pip_conf, runner, add_options, expected_cli_output_package
|
||||
):
|
||||
"""
|
||||
Tests pip-compile doesn't touch requirements.txt file on dry-run.
|
||||
|
@ -779,9 +819,7 @@ def test_dry_run_doesnt_touch_output_file(
|
|||
|
||||
before_compile_mtime = os.stat("requirements.txt").st_mtime
|
||||
|
||||
out = runner.invoke(
|
||||
cli, ["--dry-run", "--find-links", MINIMAL_WHEELS_PATH] + add_options
|
||||
)
|
||||
out = runner.invoke(cli, ["--no-annotate", "--dry-run"] + add_options)
|
||||
|
||||
assert out.exit_code == 0, out.stderr
|
||||
assert expected_cli_output_package in out.stderr.splitlines()
|
||||
|
@ -793,3 +831,225 @@ def test_dry_run_doesnt_touch_output_file(
|
|||
# The output file must not be touched
|
||||
after_compile_mtime = os.stat("requirements.txt").st_mtime
|
||||
assert after_compile_mtime == before_compile_mtime
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"empty_input_pkg, prior_output_pkg",
|
||||
[
|
||||
("", ""),
|
||||
("", "small-fake-a==0.1\n"),
|
||||
("# Nothing to see here", ""),
|
||||
("# Nothing to see here", "small-fake-a==0.1\n"),
|
||||
],
|
||||
)
|
||||
def test_empty_input_file_no_header(runner, empty_input_pkg, prior_output_pkg):
|
||||
"""
|
||||
Tests pip-compile creates an empty requirements.txt file,
|
||||
given --no-header and empty requirements.in
|
||||
"""
|
||||
with open("requirements.in", "w") as req_in:
|
||||
req_in.write(empty_input_pkg) # empty input file
|
||||
|
||||
with open("requirements.txt", "w") as req_txt:
|
||||
req_txt.write(prior_output_pkg)
|
||||
|
||||
runner.invoke(cli, ["--no-header", "requirements.in"])
|
||||
|
||||
with open("requirements.txt", "r") as req_txt:
|
||||
assert req_txt.read().strip() == ""
|
||||
|
||||
|
||||
def test_upgrade_package_doesnt_remove_annotation(pip_conf, runner):
|
||||
"""
|
||||
Tests pip-compile --upgrade-package shouldn't remove "via" annotation.
|
||||
See: GH-929
|
||||
"""
|
||||
with open("requirements.in", "w") as req_in:
|
||||
req_in.write("small-fake-with-deps\n")
|
||||
|
||||
runner.invoke(cli)
|
||||
|
||||
# Downgrade small-fake-a to 0.1
|
||||
with open("requirements.txt", "w") as req_txt:
|
||||
req_txt.write(
|
||||
"small-fake-with-deps==0.1\n"
|
||||
"small-fake-a==0.1 # via small-fake-with-deps\n"
|
||||
)
|
||||
|
||||
runner.invoke(cli, ["-P", "small-fake-a"])
|
||||
with open("requirements.txt", "r") as req_txt:
|
||||
assert (
|
||||
"small-fake-a==0.1 # via small-fake-with-deps"
|
||||
in req_txt.read().splitlines()
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"options",
|
||||
[
|
||||
# TODO add --no-index support in OutputWriter
|
||||
# "--no-index",
|
||||
"--index-url https://example.com",
|
||||
"--extra-index-url https://example.com",
|
||||
"--find-links ./libs1",
|
||||
"--trusted-host example.com",
|
||||
"--no-binary :all:",
|
||||
"--only-binary :all:",
|
||||
],
|
||||
)
|
||||
def test_options_in_requirements_file(runner, options):
|
||||
"""
|
||||
Test the options from requirements.in is copied to requirements.txt.
|
||||
"""
|
||||
with open("requirements.in", "w") as reqs_in:
|
||||
reqs_in.write(options)
|
||||
|
||||
out = runner.invoke(cli)
|
||||
assert out.exit_code == 0, out
|
||||
|
||||
with open("requirements.txt") as reqs_txt:
|
||||
assert options in reqs_txt.read().splitlines()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"cli_options, expected_message",
|
||||
(
|
||||
pytest.param(
|
||||
["--index-url", "file:foo"],
|
||||
"Was file:foo reachable?",
|
||||
id="single index url",
|
||||
),
|
||||
pytest.param(
|
||||
["--index-url", "file:foo", "--extra-index-url", "file:bar"],
|
||||
"Were file:foo or file:bar reachable?",
|
||||
id="multiple index urls",
|
||||
),
|
||||
),
|
||||
)
|
||||
def test_unreachable_index_urls(runner, cli_options, expected_message):
|
||||
"""
|
||||
Test pip-compile raises an error if index URLs are not reachable.
|
||||
"""
|
||||
with open("requirements.in", "w") as reqs_in:
|
||||
reqs_in.write("some-package")
|
||||
|
||||
out = runner.invoke(cli, cli_options)
|
||||
|
||||
assert out.exit_code == 2, out
|
||||
|
||||
stderr_lines = out.stderr.splitlines()
|
||||
assert "No versions found" in stderr_lines
|
||||
assert expected_message in stderr_lines
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"current_package, upgraded_package",
|
||||
(
|
||||
pytest.param("small-fake-b==0.1", "small-fake-b==0.2", id="upgrade"),
|
||||
pytest.param("small-fake-b==0.2", "small-fake-b==0.1", id="downgrade"),
|
||||
),
|
||||
)
|
||||
def test_upgrade_packages_option_subdependency(
|
||||
pip_conf, runner, current_package, upgraded_package
|
||||
):
|
||||
"""
|
||||
Test that pip-compile --upgrade-package/-P upgrades/dpwngrades subdependencies.
|
||||
"""
|
||||
|
||||
with open("requirements.in", "w") as reqs:
|
||||
reqs.write("small-fake-with-unpinned-deps\n")
|
||||
|
||||
with open("requirements.txt", "w") as reqs:
|
||||
reqs.write("small-fake-a==0.1\n")
|
||||
reqs.write(current_package + "\n")
|
||||
reqs.write("small-fake-with-unpinned-deps==0.1\n")
|
||||
|
||||
out = runner.invoke(
|
||||
cli, ["--no-annotate", "--dry-run", "--upgrade-package", upgraded_package]
|
||||
)
|
||||
|
||||
stderr_lines = out.stderr.splitlines()
|
||||
assert "small-fake-a==0.1" in stderr_lines, "small-fake-a must keep its version"
|
||||
assert (
|
||||
upgraded_package in stderr_lines
|
||||
), "{} must be upgraded/downgraded to {}".format(current_package, upgraded_package)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"input_opts, output_opts",
|
||||
(
|
||||
# Test that input options overwrite output options
|
||||
pytest.param(
|
||||
"--index-url https://index-url",
|
||||
"--index-url https://another-index-url",
|
||||
id="index url",
|
||||
),
|
||||
pytest.param(
|
||||
"--extra-index-url https://extra-index-url",
|
||||
"--extra-index-url https://another-extra-index-url",
|
||||
id="extra index url",
|
||||
),
|
||||
pytest.param("--find-links dir", "--find-links another-dir", id="find links"),
|
||||
pytest.param(
|
||||
"--trusted-host hostname",
|
||||
"--trusted-host another-hostname",
|
||||
id="trusted host",
|
||||
),
|
||||
pytest.param(
|
||||
"--no-binary :package:", "--no-binary :another-package:", id="no binary"
|
||||
),
|
||||
pytest.param(
|
||||
"--only-binary :package:",
|
||||
"--only-binary :another-package:",
|
||||
id="only binary",
|
||||
),
|
||||
# Test misc corner cases
|
||||
pytest.param("", "--index-url https://index-url", id="empty input options"),
|
||||
pytest.param(
|
||||
"--index-url https://index-url",
|
||||
(
|
||||
"--index-url https://index-url\n"
|
||||
"--extra-index-url https://another-extra-index-url"
|
||||
),
|
||||
id="partially matched options",
|
||||
),
|
||||
),
|
||||
)
|
||||
def test_remove_outdated_options(runner, input_opts, output_opts):
|
||||
"""
|
||||
Test that the options from the current requirements.txt wouldn't stay
|
||||
after compile if they were removed from requirements.in file.
|
||||
"""
|
||||
with open("requirements.in", "w") as req_in:
|
||||
req_in.write(input_opts)
|
||||
with open("requirements.txt", "w") as req_txt:
|
||||
req_txt.write(output_opts)
|
||||
|
||||
out = runner.invoke(cli, ["--no-header"])
|
||||
|
||||
assert out.exit_code == 0, out
|
||||
assert out.stderr.strip() == input_opts
|
||||
|
||||
|
||||
def test_sub_dependencies_with_constraints(pip_conf, runner):
|
||||
# Write constraints file
|
||||
with open("constraints.txt", "w") as constraints_in:
|
||||
constraints_in.write("small-fake-a==0.1\n")
|
||||
constraints_in.write("small-fake-b==0.2\n")
|
||||
constraints_in.write("small-fake-with-unpinned-deps==0.1")
|
||||
|
||||
with open("requirements.in", "w") as req_in:
|
||||
req_in.write("-c constraints.txt\n")
|
||||
req_in.write("small_fake_with_deps_and_sub_deps") # require fake package
|
||||
|
||||
out = runner.invoke(cli, ["--no-annotate"])
|
||||
|
||||
assert out.exit_code == 0
|
||||
|
||||
req_out_lines = set(out.stderr.splitlines())
|
||||
assert {
|
||||
"small-fake-a==0.1",
|
||||
"small-fake-b==0.2",
|
||||
"small-fake-with-deps-and-sub-deps==0.1",
|
||||
"small-fake-with-unpinned-deps==0.1",
|
||||
}.issubset(req_out_lines)
|
||||
|
|
|
@ -147,3 +147,30 @@ def test_pip_install_flags(check_call, cli_flags, expected_install_flags, runner
|
|||
assert [args[6:] for args in call_args if args[3] == "install"] == [
|
||||
expected_install_flags
|
||||
]
|
||||
|
||||
|
||||
@mock.patch("piptools.sync.check_call")
|
||||
def test_sync_ask_declined(check_call, runner):
|
||||
"""
|
||||
Make sure nothing is installed if the confirmation is declined
|
||||
"""
|
||||
with open("requirements.txt", "w") as req_in:
|
||||
req_in.write("small-fake-a==1.10.0")
|
||||
|
||||
runner.invoke(cli, ["--ask"], input="n\n")
|
||||
|
||||
check_call.assert_not_called()
|
||||
|
||||
|
||||
@mock.patch("piptools.sync.check_call")
|
||||
def test_sync_ask_accepted(check_call, runner):
|
||||
"""
|
||||
Make sure pip is called when the confirmation is accepted (even if
|
||||
--dry-run is given)
|
||||
"""
|
||||
with open("requirements.txt", "w") as req_in:
|
||||
req_in.write("small-fake-a==1.10.0")
|
||||
|
||||
runner.invoke(cli, ["--ask", "--dry-run"], input="y\n")
|
||||
|
||||
assert check_call.call_count == 2
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
{
|
||||
"git+git://example.org/django.git#egg=django": []
|
||||
"git+git://example.org/django.git#egg=django": [],
|
||||
"git+https://github.com/celery/billiard#egg=billiard==3.5.9999": []
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
{
|
||||
"aiohttp": {
|
||||
"3.6.2": {"": ["yarl"]}
|
||||
},
|
||||
"anyjson": {
|
||||
"0.3.3": {"": []}
|
||||
},
|
||||
|
@ -79,6 +82,9 @@
|
|||
"setuptools>=18.5"
|
||||
]}
|
||||
},
|
||||
"idna": {
|
||||
"2.8": {"": []}
|
||||
},
|
||||
"ipython": {
|
||||
"2.1.0": {
|
||||
"": ["gnureadline"],
|
||||
|
@ -163,5 +169,8 @@
|
|||
"0.6": {"": []},
|
||||
"0.10": {"": []},
|
||||
"0.10.4": {"": []}
|
||||
},
|
||||
"yarl": {
|
||||
"1.4.2": {"": ["idna"]}
|
||||
}
|
||||
}
|
||||
|
|
Двоичные данные
third_party/python/pip-tools/tests/test_data/minimal_wheels/small_fake_a-0.3b1-py2.py3-none-any.whl
поставляемый
Двоичные данные
third_party/python/pip-tools/tests/test_data/minimal_wheels/small_fake_a-0.3b1-py2.py3-none-any.whl
поставляемый
Двоичный файл не отображается.
Двоичные данные
third_party/python/pip-tools/tests/test_data/minimal_wheels/small_fake_multi_arch-0.1-py2.py3-none-manylinux1_i686.whl
поставляемый
Normal file
Двоичные данные
third_party/python/pip-tools/tests/test_data/minimal_wheels/small_fake_multi_arch-0.1-py2.py3-none-manylinux1_i686.whl
поставляемый
Normal file
Двоичный файл не отображается.
Двоичные данные
third_party/python/pip-tools/tests/test_data/minimal_wheels/small_fake_multi_arch-0.1-py2.py3-none-manylinux1_x86_64.whl
поставляемый
Normal file
Двоичные данные
third_party/python/pip-tools/tests/test_data/minimal_wheels/small_fake_multi_arch-0.1-py2.py3-none-manylinux1_x86_64.whl
поставляемый
Normal file
Двоичный файл не отображается.
Двоичные данные
third_party/python/pip-tools/tests/test_data/minimal_wheels/small_fake_multi_arch-0.1-py2.py3-none-win32.whl
поставляемый
Normal file
Двоичные данные
third_party/python/pip-tools/tests/test_data/minimal_wheels/small_fake_multi_arch-0.1-py2.py3-none-win32.whl
поставляемый
Normal file
Двоичный файл не отображается.
Двоичные данные
third_party/python/pip-tools/tests/test_data/minimal_wheels/small_fake_with_deps-0.1-py2.py3-none-any.whl
поставляемый
Двоичные данные
third_party/python/pip-tools/tests/test_data/minimal_wheels/small_fake_with_deps-0.1-py2.py3-none-any.whl
поставляемый
Двоичный файл не отображается.
Двоичные данные
third_party/python/pip-tools/tests/test_data/minimal_wheels/small_fake_with_deps_and_sub_deps-0.1-py2.py3-none-any.whl
поставляемый
Normal file
Двоичные данные
third_party/python/pip-tools/tests/test_data/minimal_wheels/small_fake_with_deps_and_sub_deps-0.1-py2.py3-none-any.whl
поставляемый
Normal file
Двоичный файл не отображается.
Двоичные данные
third_party/python/pip-tools/tests/test_data/minimal_wheels/small_fake_with_unpinned_deps-0.1-py2.py3-none-any.whl
поставляемый
Normal file
Двоичные данные
third_party/python/pip-tools/tests/test_data/minimal_wheels/small_fake_with_unpinned_deps-0.1-py2.py3-none-any.whl
поставляемый
Normal file
Двоичный файл не отображается.
|
@ -3,5 +3,5 @@ from setuptools import setup
|
|||
setup(
|
||||
name="small_fake_with_deps",
|
||||
version=0.1,
|
||||
install_requires=["six==1.10.0", "setuptools>=35.0.0"],
|
||||
install_requires=["small-fake-a==0.1", "small-fake-b==0.1"],
|
||||
)
|
7
third_party/python/pip-tools/tests/test_data/packages/small_fake_with_deps_and_sub_deps/setup.py
поставляемый
Normal file
7
third_party/python/pip-tools/tests/test_data/packages/small_fake_with_deps_and_sub_deps/setup.py
поставляемый
Normal file
|
@ -0,0 +1,7 @@
|
|||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name="small_fake_with_deps_and_sub_deps",
|
||||
version=0.1,
|
||||
install_requires=["small-fake-with-unpinned-deps"],
|
||||
)
|
7
third_party/python/pip-tools/tests/test_data/packages/small_fake_with_unpinned_deps/setup.py
поставляемый
Normal file
7
third_party/python/pip-tools/tests/test_data/packages/small_fake_with_unpinned_deps/setup.py
поставляемый
Normal file
|
@ -0,0 +1,7 @@
|
|||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name="small_fake_with_unpinned_deps",
|
||||
version=0.1,
|
||||
install_requires=["small-fake-a", "small-fake-b"],
|
||||
)
|
|
@ -78,3 +78,12 @@ def test_get_dependencies_rejects_non_pinned_requirements(from_line, repository)
|
|||
not_a_pinned_req = from_line("django>1.6")
|
||||
with raises(TypeError):
|
||||
repository.get_dependencies(not_a_pinned_req)
|
||||
|
||||
|
||||
def test_get_hashes(from_line, repository):
|
||||
ireq = from_line("django==1.8")
|
||||
expected = {
|
||||
"test:123",
|
||||
"sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
}
|
||||
assert repository.get_hashes(ireq) == expected
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
import pytest
|
||||
from mock import MagicMock, patch
|
||||
from pip import __version__ as pip_version
|
||||
from pip._vendor.packaging.version import parse as parse_version
|
||||
|
||||
from piptools._compat import PackageFinder, install_req_from_line
|
||||
from piptools.pip import get_pip_command
|
||||
from piptools.repositories.pypi import PyPIRepository
|
||||
|
||||
|
||||
def test_pypirepo_build_dir_is_str():
|
||||
assert isinstance(get_pypi_repository().build_dir, str)
|
||||
|
||||
|
||||
def test_pypirepo_source_dir_is_str():
|
||||
assert isinstance(get_pypi_repository().source_dir, str)
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
parse_version(pip_version) >= parse_version("10.0.0"),
|
||||
reason="RequirementSet objects don't take arguments after pip 10.",
|
||||
)
|
||||
def test_pypirepo_calls_reqset_with_str_paths():
|
||||
"""
|
||||
Make sure that paths passed to RequirementSet init are str.
|
||||
|
||||
Passing unicode paths on Python 2 could make pip fail later on
|
||||
unpack, if the package contains non-ASCII file names, because
|
||||
non-ASCII str and unicode paths cannot be combined.
|
||||
"""
|
||||
with patch("piptools.repositories.pypi.RequirementSet") as mocked_init:
|
||||
repo = get_pypi_repository()
|
||||
ireq = install_req_from_line("ansible==2.4.0.0")
|
||||
|
||||
# Setup a mock object to be returned from the RequirementSet call
|
||||
mocked_reqset = MagicMock()
|
||||
mocked_init.return_value = mocked_reqset
|
||||
|
||||
# Do the call
|
||||
repo.get_dependencies(ireq)
|
||||
|
||||
# Check that RequirementSet init is called with correct type arguments
|
||||
assert mocked_init.call_count == 1
|
||||
(init_call_args, init_call_kwargs) = mocked_init.call_args
|
||||
assert isinstance(init_call_args[0], str)
|
||||
assert isinstance(init_call_args[1], str)
|
||||
assert isinstance(init_call_kwargs.get("download_dir"), str)
|
||||
assert isinstance(init_call_kwargs.get("wheel_download_dir"), str)
|
||||
|
||||
# Check that _prepare_file is called correctly
|
||||
assert mocked_reqset._prepare_file.call_count == 1
|
||||
(pf_call_args, pf_call_kwargs) = mocked_reqset._prepare_file.call_args
|
||||
(called_with_finder, called_with_ireq) = pf_call_args
|
||||
assert isinstance(called_with_finder, PackageFinder)
|
||||
assert called_with_ireq == ireq
|
||||
assert not pf_call_kwargs
|
||||
|
||||
|
||||
def get_pypi_repository():
|
||||
"""
|
||||
Get a PyPIRepository object for the tests.
|
||||
|
||||
:rtype: PyPIRepository
|
||||
"""
|
||||
pip_command = get_pip_command()
|
||||
pip_options = pip_command.parse_args([])[0]
|
||||
session = pip_command._build_session(pip_options)
|
||||
return PyPIRepository(pip_options, session)
|
|
@ -1,77 +1,25 @@
|
|||
from piptools.pip import get_pip_command
|
||||
from piptools.repositories.local import LocalRequirementsRepository
|
||||
from piptools.repositories.pypi import PyPIRepository
|
||||
from piptools.utils import name_from_req
|
||||
|
||||
EXPECTED = {
|
||||
"sha256:04b133ef629ae2bc05f83d0b079a964494a9cd17914943e690c57209b44aae20",
|
||||
"sha256:0f1b3193c17b93c75e73eeac92f22eec4c98a021d9969b1c347d1944fae0d26b",
|
||||
"sha256:1fb1cf40c315656f98f4d3acfb1bd031a14a9a69d155e9a180d5f9b52eaf745a",
|
||||
"sha256:20af85d8e154b50f540bc8d517a0dbf6b1c20b5d06e572afda919d5dafd1d06b",
|
||||
"sha256:2570f93b42c61013ab4b26e23aa25b640faf5b093ad7dd3504c3a8eadd69bc24",
|
||||
"sha256:2f4e2872833ee3764dfc168dea566b7dd83b01ac61b377490beba53b5ece57f7",
|
||||
"sha256:31776a37a67424e7821324b9e03a05aa6378bbc2bccc58fa56402547f82803c6",
|
||||
"sha256:353421c76545f1d440cacc137abc865f07eab9df0dd3510c0851a2ca04199e90",
|
||||
"sha256:36d06de7b09b1eba54b1f5f76e2221afef7489cc61294508c5a7308a925a50c6",
|
||||
"sha256:3f1908d0bcd654f8b7b73204f24336af9f020b707fb8af937e3e2279817cbcd6",
|
||||
"sha256:5268de3a18f031e9787c919c1b9137ff681ea696e76740b1c6c336a26baaa58a",
|
||||
"sha256:563e0bd53fda03c151573217b3a49b3abad8813de9dd0632e10090f6190fdaf8",
|
||||
"sha256:5e1368d13f1774852f9e435260be19ad726bbfb501b80472f61c2dc768a0692a",
|
||||
"sha256:60881c79eb72cb75bd0a4be5e31c9e431739146c4184a2618cabea3938418984",
|
||||
"sha256:6120b62a642a40e47eb6c9ff00c02be69158fc7f7c5ff78e42a2c739d1c57cd6",
|
||||
"sha256:65c223e77f87cb463191ace3398e0a6d84ce4ac575d42eb412a220b099f593d6",
|
||||
"sha256:6fbf8db55710959344502b58ab937424173ad8b5eb514610bcf56b119caa350a",
|
||||
"sha256:74aadea668c94eef4ceb09be3d0eae6619e28b4f1ced4e29cd43a05bb2cfd7a4",
|
||||
"sha256:7be1efa623e1ed91b15b1e62e04c536def1d75785eb930a0b8179ca6b65ed16d",
|
||||
"sha256:83266cdede210393889471b0c2631e78da9d4692fcca875af7e958ad39b897ee",
|
||||
"sha256:86c68a3f8246495962446c6f96f6a27f182b91208187b68f1e87ec3dfd29fa32",
|
||||
"sha256:9163f7743cf9991edaddf9cf886708e288fab38e1b9fec9c41c15c85c8f7f147",
|
||||
"sha256:97d9f338f91b7927893ea6500b953e4b4b7e47c6272222992bb76221e17056ff",
|
||||
"sha256:a7930e73a4359b52323d09de6d6860840314aa09346cbcf4def8875e1b07ebc7",
|
||||
"sha256:ada8a42c493e4934a1a8875c2bc9efcb1b88c09883f70375bfa053ab32d6a118",
|
||||
"sha256:b0bc2d83cc0ba0e8f0d9eca2ffe07f72f33bec7d84547071e7e875d4cca8272d",
|
||||
"sha256:b5412a65605c642adf3e1544b59b8537daf5696dedadd2b3cbebc42e24da45ed",
|
||||
"sha256:ba6b5205fced1625b6d9d55f9ef422f9667c5d95f18f07c0611eb964a3355331",
|
||||
"sha256:bcaf3d86385daaab0ae51c9c53ebe70a6c1c5dfcb9e311b13517e04773ddf6b6",
|
||||
"sha256:cfa15570ecec1ea6bee089e86fd4deae6208c96a811344ce246de5e5c9ac824a",
|
||||
"sha256:d3e3063af1fa6b59e255da9a812891cdaf24b90fbaf653c02797871069b7c4c9",
|
||||
"sha256:d9cfe26ecea2fec320cd0cac400c9c2435328994d23596ee6df63945fe7292b0",
|
||||
"sha256:e5ef800ef8ef9ee05ae9a5b7d7d9cf7d6c936b32e312e54823faca3034ee16ab",
|
||||
"sha256:f1366150acf611d09d37ffefb3559ed3ffeb1713643d3cd10716d6c5da3f83fb",
|
||||
"sha256:f4eb9747a37120b35f59c8e96265e87b0c432ff010d32fc0772992aa14659502",
|
||||
"sha256:f8264463cc08cd696ad17e4bf3c80f3344628c04c11ffdc545ddf0798bc17316",
|
||||
"sha256:f8ba54848dfe280b1be0d6e699544cee4ba10d566f92464538063d9e645aed3e",
|
||||
"sha256:f93d1edcaea7b6a7a8fbf936f4492a9a0ee0b4cb281efebd5e1dd73e5e432c71",
|
||||
"sha256:fc8865c7e0ac25ddd71036c2b9a799418b32d9acb40400d345b8791b6e1058cb",
|
||||
"sha256:fce6b0cb9ade1546178c031393633b09c4793834176496c99a94de0bfa471b27",
|
||||
"sha256:fde17c52d7ce7d55a9fb263b57ccb5da6439915b5c7105617eb21f636bb1bd9c",
|
||||
}
|
||||
EXPECTED = {"sha256:5e6071ee6e4c59e0d0408d366fe9b66781d2cf01be9a6e19a2433bb3c5336330"}
|
||||
|
||||
|
||||
def test_get_hashes_local_repository_cache_miss(from_line):
|
||||
pip_command = get_pip_command()
|
||||
pip_options, _ = pip_command.parse_args(
|
||||
["--index-url", PyPIRepository.DEFAULT_INDEX_URL]
|
||||
)
|
||||
session = pip_command._build_session(pip_options)
|
||||
repository = PyPIRepository(pip_options, session)
|
||||
|
||||
def test_get_hashes_local_repository_cache_miss(pip_conf, from_line, pypi_repository):
|
||||
existing_pins = {}
|
||||
local_repository = LocalRequirementsRepository(existing_pins, repository)
|
||||
with repository.allow_all_wheels():
|
||||
hashes = local_repository.get_hashes(from_line("cffi==1.9.1"))
|
||||
local_repository = LocalRequirementsRepository(existing_pins, pypi_repository)
|
||||
with local_repository.allow_all_wheels():
|
||||
hashes = local_repository.get_hashes(from_line("small-fake-a==0.1"))
|
||||
assert hashes == EXPECTED
|
||||
|
||||
|
||||
def test_get_hashes_local_repository_cache_hit(from_line, repository):
|
||||
# Create an install requirement with the hashes included in its options
|
||||
options = {}
|
||||
options["hashes"] = {"sha256": [entry.split(":")[1] for entry in EXPECTED]}
|
||||
req = from_line("cffi==1.9.1", options=options)
|
||||
options = {"hashes": {"sha256": [entry.split(":")[1] for entry in EXPECTED]}}
|
||||
req = from_line("small-fake-a==0.1", options=options)
|
||||
existing_pins = {name_from_req(req): req}
|
||||
|
||||
# Use fake repository so that we know the hashes are coming from cache
|
||||
local_repository = LocalRequirementsRepository(existing_pins, repository)
|
||||
with repository.allow_all_wheels():
|
||||
hashes = local_repository.get_hashes(from_line("cffi==1.9.1"))
|
||||
with local_repository.allow_all_wheels():
|
||||
hashes = local_repository.get_hashes(from_line("small-fake-a==0.1"))
|
||||
assert hashes == EXPECTED
|
||||
|
|
|
@ -1,87 +1,62 @@
|
|||
import os
|
||||
|
||||
import mock
|
||||
import pytest
|
||||
|
||||
from piptools._compat.pip_compat import Link, Session, path_to_url
|
||||
from piptools.pip import get_pip_command
|
||||
from piptools.repositories.pypi import PyPIRepository, open_local_or_remote_file
|
||||
from piptools._compat import PackageFinder
|
||||
from piptools._compat.pip_compat import PIP_VERSION, Link, Session, path_to_url
|
||||
from piptools.repositories import PyPIRepository
|
||||
from piptools.repositories.pypi import open_local_or_remote_file
|
||||
|
||||
|
||||
def test_generate_hashes_all_platforms(from_line):
|
||||
def test_generate_hashes_all_platforms(pip_conf, from_line, pypi_repository):
|
||||
expected = {
|
||||
"sha256:04b133ef629ae2bc05f83d0b079a964494a9cd17914943e690c57209b44aae20",
|
||||
"sha256:0f1b3193c17b93c75e73eeac92f22eec4c98a021d9969b1c347d1944fae0d26b",
|
||||
"sha256:1fb1cf40c315656f98f4d3acfb1bd031a14a9a69d155e9a180d5f9b52eaf745a",
|
||||
"sha256:20af85d8e154b50f540bc8d517a0dbf6b1c20b5d06e572afda919d5dafd1d06b",
|
||||
"sha256:2570f93b42c61013ab4b26e23aa25b640faf5b093ad7dd3504c3a8eadd69bc24",
|
||||
"sha256:2f4e2872833ee3764dfc168dea566b7dd83b01ac61b377490beba53b5ece57f7",
|
||||
"sha256:31776a37a67424e7821324b9e03a05aa6378bbc2bccc58fa56402547f82803c6",
|
||||
"sha256:353421c76545f1d440cacc137abc865f07eab9df0dd3510c0851a2ca04199e90",
|
||||
"sha256:36d06de7b09b1eba54b1f5f76e2221afef7489cc61294508c5a7308a925a50c6",
|
||||
"sha256:3f1908d0bcd654f8b7b73204f24336af9f020b707fb8af937e3e2279817cbcd6",
|
||||
"sha256:5268de3a18f031e9787c919c1b9137ff681ea696e76740b1c6c336a26baaa58a",
|
||||
"sha256:563e0bd53fda03c151573217b3a49b3abad8813de9dd0632e10090f6190fdaf8",
|
||||
"sha256:5e1368d13f1774852f9e435260be19ad726bbfb501b80472f61c2dc768a0692a",
|
||||
"sha256:60881c79eb72cb75bd0a4be5e31c9e431739146c4184a2618cabea3938418984",
|
||||
"sha256:6120b62a642a40e47eb6c9ff00c02be69158fc7f7c5ff78e42a2c739d1c57cd6",
|
||||
"sha256:65c223e77f87cb463191ace3398e0a6d84ce4ac575d42eb412a220b099f593d6",
|
||||
"sha256:6fbf8db55710959344502b58ab937424173ad8b5eb514610bcf56b119caa350a",
|
||||
"sha256:74aadea668c94eef4ceb09be3d0eae6619e28b4f1ced4e29cd43a05bb2cfd7a4",
|
||||
"sha256:7be1efa623e1ed91b15b1e62e04c536def1d75785eb930a0b8179ca6b65ed16d",
|
||||
"sha256:83266cdede210393889471b0c2631e78da9d4692fcca875af7e958ad39b897ee",
|
||||
"sha256:86c68a3f8246495962446c6f96f6a27f182b91208187b68f1e87ec3dfd29fa32",
|
||||
"sha256:9163f7743cf9991edaddf9cf886708e288fab38e1b9fec9c41c15c85c8f7f147",
|
||||
"sha256:97d9f338f91b7927893ea6500b953e4b4b7e47c6272222992bb76221e17056ff",
|
||||
"sha256:a7930e73a4359b52323d09de6d6860840314aa09346cbcf4def8875e1b07ebc7",
|
||||
"sha256:ada8a42c493e4934a1a8875c2bc9efcb1b88c09883f70375bfa053ab32d6a118",
|
||||
"sha256:b0bc2d83cc0ba0e8f0d9eca2ffe07f72f33bec7d84547071e7e875d4cca8272d",
|
||||
"sha256:b5412a65605c642adf3e1544b59b8537daf5696dedadd2b3cbebc42e24da45ed",
|
||||
"sha256:ba6b5205fced1625b6d9d55f9ef422f9667c5d95f18f07c0611eb964a3355331",
|
||||
"sha256:bcaf3d86385daaab0ae51c9c53ebe70a6c1c5dfcb9e311b13517e04773ddf6b6",
|
||||
"sha256:cfa15570ecec1ea6bee089e86fd4deae6208c96a811344ce246de5e5c9ac824a",
|
||||
"sha256:d3e3063af1fa6b59e255da9a812891cdaf24b90fbaf653c02797871069b7c4c9",
|
||||
"sha256:d9cfe26ecea2fec320cd0cac400c9c2435328994d23596ee6df63945fe7292b0",
|
||||
"sha256:e5ef800ef8ef9ee05ae9a5b7d7d9cf7d6c936b32e312e54823faca3034ee16ab",
|
||||
"sha256:f1366150acf611d09d37ffefb3559ed3ffeb1713643d3cd10716d6c5da3f83fb",
|
||||
"sha256:f4eb9747a37120b35f59c8e96265e87b0c432ff010d32fc0772992aa14659502",
|
||||
"sha256:f8264463cc08cd696ad17e4bf3c80f3344628c04c11ffdc545ddf0798bc17316",
|
||||
"sha256:f8ba54848dfe280b1be0d6e699544cee4ba10d566f92464538063d9e645aed3e",
|
||||
"sha256:f93d1edcaea7b6a7a8fbf936f4492a9a0ee0b4cb281efebd5e1dd73e5e432c71",
|
||||
"sha256:fc8865c7e0ac25ddd71036c2b9a799418b32d9acb40400d345b8791b6e1058cb",
|
||||
"sha256:fce6b0cb9ade1546178c031393633b09c4793834176496c99a94de0bfa471b27",
|
||||
"sha256:fde17c52d7ce7d55a9fb263b57ccb5da6439915b5c7105617eb21f636bb1bd9c",
|
||||
"sha256:8d4d131cd05338e09f461ad784297efea3652e542c5fabe04a62358429a6175e",
|
||||
"sha256:ad05e1371eb99f257ca00f791b755deb22e752393eb8e75bc01d651715b02ea9",
|
||||
"sha256:24afa5b317b302f356fd3fc3b1cfb0aad114d509cf635ea9566052424191b944",
|
||||
}
|
||||
|
||||
pip_command = get_pip_command()
|
||||
pip_options, _ = pip_command.parse_args(
|
||||
["--index-url", PyPIRepository.DEFAULT_INDEX_URL]
|
||||
ireq = from_line("small-fake-multi-arch==0.1")
|
||||
with pypi_repository.allow_all_wheels():
|
||||
assert pypi_repository.get_hashes(ireq) == expected
|
||||
|
||||
|
||||
@pytest.mark.network
|
||||
def test_get_file_hash_without_interfering_with_each_other(from_line, pypi_repository):
|
||||
"""
|
||||
The PyPIRepository._get_file_hash() used to call unpack_url(),
|
||||
when generating the hash. Unpacking both packages to the same directory
|
||||
will then fail. E.g. matplotlib-2.0.2.tar.gz has a directory named LICENSE,
|
||||
but many other packages have a file named LICENSE.
|
||||
|
||||
See GH-512 and GH-544.
|
||||
"""
|
||||
assert (
|
||||
pypi_repository._get_file_hash(
|
||||
Link(
|
||||
"https://files.pythonhosted.org/packages/"
|
||||
"f5/f0/9da3ef24ea7eb0ccd12430a261b66eca36b924aeef06e17147f9f9d7d310/"
|
||||
"matplotlib-2.0.2.tar.gz"
|
||||
)
|
||||
)
|
||||
== "sha256:0ffbc44faa34a8b1704bc108c451ecf87988f900ef7ce757b8e2e84383121ff1"
|
||||
)
|
||||
session = pip_command._build_session(pip_options)
|
||||
repository = PyPIRepository(pip_options, session)
|
||||
ireq = from_line("cffi==1.9.1")
|
||||
with repository.allow_all_wheels():
|
||||
assert repository.get_hashes(ireq) == expected
|
||||
|
||||
|
||||
def test_generate_hashes_without_interfering_with_each_other(from_line):
|
||||
pip_command = get_pip_command()
|
||||
pip_options, _ = pip_command.parse_args(
|
||||
["--index-url", PyPIRepository.DEFAULT_INDEX_URL]
|
||||
assert (
|
||||
pypi_repository._get_file_hash(
|
||||
Link(
|
||||
"https://files.pythonhosted.org/packages/"
|
||||
"a1/32/e3d6c3a8b5461b903651dd6ce958ed03c093d2e00128e3f33ea69f1d7965/"
|
||||
"cffi-1.9.1.tar.gz"
|
||||
)
|
||||
)
|
||||
== "sha256:563e0bd53fda03c151573217b3a49b3abad8813de9dd0632e10090f6190fdaf8"
|
||||
)
|
||||
session = pip_command._build_session(pip_options)
|
||||
repository = PyPIRepository(pip_options, session)
|
||||
repository.get_hashes(from_line("cffi==1.9.1"))
|
||||
repository.get_hashes(from_line("matplotlib==2.0.2"))
|
||||
|
||||
|
||||
def test_get_hashes_editable_empty_set(from_editable):
|
||||
pip_command = get_pip_command()
|
||||
pip_options, _ = pip_command.parse_args(
|
||||
["--index-url", PyPIRepository.DEFAULT_INDEX_URL]
|
||||
)
|
||||
session = pip_command._build_session(pip_options)
|
||||
repository = PyPIRepository(pip_options, session)
|
||||
def test_get_hashes_editable_empty_set(from_editable, pypi_repository):
|
||||
ireq = from_editable("git+https://github.com/django/django.git#egg=django")
|
||||
assert repository.get_hashes(ireq) == set()
|
||||
assert pypi_repository.get_hashes(ireq) == set()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("content, content_length", [(b"foo", 3), (b"foobar", 6)])
|
||||
|
@ -141,3 +116,92 @@ def test_open_local_or_remote_file__remote_file(
|
|||
assert file_stream.size == expected_content_length
|
||||
|
||||
mock_response.close.assert_called_once()
|
||||
|
||||
|
||||
def test_pypirepo_build_dir_is_str(pypi_repository):
|
||||
assert isinstance(pypi_repository.build_dir, str)
|
||||
|
||||
|
||||
def test_pypirepo_source_dir_is_str(pypi_repository):
|
||||
assert isinstance(pypi_repository.source_dir, str)
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
PIP_VERSION >= (10,),
|
||||
reason="RequirementSet objects don't take arguments after pip 10.",
|
||||
)
|
||||
def test_pypirepo_calls_reqset_with_str_paths(pypi_repository, from_line):
|
||||
"""
|
||||
Make sure that paths passed to RequirementSet init are str.
|
||||
|
||||
Passing unicode paths on Python 2 could make pip fail later on
|
||||
unpack, if the package contains non-ASCII file names, because
|
||||
non-ASCII str and unicode paths cannot be combined.
|
||||
"""
|
||||
with mock.patch("piptools.repositories.pypi.RequirementSet") as mocked_init:
|
||||
ireq = from_line("ansible==2.4.0.0")
|
||||
|
||||
# Setup a mock object to be returned from the RequirementSet call
|
||||
mocked_reqset = mock.MagicMock()
|
||||
mocked_init.return_value = mocked_reqset
|
||||
|
||||
# Do the call
|
||||
pypi_repository.get_dependencies(ireq)
|
||||
|
||||
# Check that RequirementSet init is called with correct type arguments
|
||||
assert mocked_init.call_count == 1
|
||||
(init_call_args, init_call_kwargs) = mocked_init.call_args
|
||||
assert isinstance(init_call_args[0], str)
|
||||
assert isinstance(init_call_args[1], str)
|
||||
assert isinstance(init_call_kwargs.get("download_dir"), str)
|
||||
assert isinstance(init_call_kwargs.get("wheel_download_dir"), str)
|
||||
|
||||
# Check that _prepare_file is called correctly
|
||||
assert mocked_reqset._prepare_file.call_count == 1
|
||||
(pf_call_args, pf_call_kwargs) = mocked_reqset._prepare_file.call_args
|
||||
(called_with_finder, called_with_ireq) = pf_call_args
|
||||
assert isinstance(called_with_finder, PackageFinder)
|
||||
assert called_with_ireq == ireq
|
||||
assert not pf_call_kwargs
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
PIP_VERSION < (10,), reason="WheelCache.cleanup() introduced in pip==10.0.0"
|
||||
)
|
||||
@mock.patch("piptools.repositories.pypi.PyPIRepository.resolve_reqs") # to run offline
|
||||
@mock.patch("piptools.repositories.pypi.WheelCache")
|
||||
def test_wheel_cache_cleanup_called(
|
||||
WheelCache, resolve_reqs, pypi_repository, from_line
|
||||
):
|
||||
"""
|
||||
Test WheelCache.cleanup() called once after dependency resolution.
|
||||
"""
|
||||
ireq = from_line("six==1.10.0")
|
||||
pypi_repository.get_dependencies(ireq)
|
||||
WheelCache.return_value.cleanup.assert_called_once_with()
|
||||
|
||||
|
||||
def test_relative_path_cache_dir_is_normalized(from_line):
|
||||
relative_cache_dir = "pypi-repo-cache"
|
||||
pypi_repository = PyPIRepository([], cache_dir=relative_cache_dir)
|
||||
|
||||
assert os.path.isabs(pypi_repository._cache_dir)
|
||||
assert pypi_repository._cache_dir.endswith(relative_cache_dir)
|
||||
|
||||
|
||||
def test_relative_path_pip_cache_dir_is_normalized(from_line, tmpdir):
|
||||
relative_cache_dir = "pip-cache"
|
||||
pypi_repository = PyPIRepository(
|
||||
["--cache-dir", relative_cache_dir], cache_dir=str(tmpdir / "pypi-repo-cache")
|
||||
)
|
||||
|
||||
assert os.path.isabs(pypi_repository.options.cache_dir)
|
||||
assert pypi_repository.options.cache_dir.endswith(relative_cache_dir)
|
||||
|
||||
|
||||
def test_pip_cache_dir_is_empty(from_line, tmpdir):
|
||||
pypi_repository = PyPIRepository(
|
||||
["--no-cache-dir"], cache_dir=str(tmpdir / "pypi-repo-cache")
|
||||
)
|
||||
|
||||
assert not pypi_repository.options.cache_dir
|
||||
|
|
|
@ -81,7 +81,7 @@ from piptools.resolver import combine_install_requirements
|
|||
# We must remove child dependencies from result if parent
|
||||
# is removed (e.g. vine from amqp>=2.0)
|
||||
# See: GH-370
|
||||
# because of upated dependencies in the test index, we need to pin celery
|
||||
# because of updated dependencies in the test index, we need to pin celery
|
||||
# in order to reproduce vine removal (because it was readded in later
|
||||
# releases)
|
||||
(
|
||||
|
@ -155,6 +155,28 @@ from piptools.resolver import combine_install_requirements
|
|||
),
|
||||
},
|
||||
),
|
||||
# Git URL requirement
|
||||
# See: GH-851
|
||||
(
|
||||
[
|
||||
"git+https://github.com/celery/billiard#egg=billiard==3.5.9999",
|
||||
"celery==4.0.2",
|
||||
],
|
||||
[
|
||||
"amqp==2.1.4 (from kombu==4.0.2->celery==4.0.2)",
|
||||
"kombu==4.0.2 (from celery==4.0.2)",
|
||||
"billiard<3.6.0,==3.5.9999,>=3.5.0.2 from "
|
||||
"git+https://github.com/celery/billiard#egg=billiard==3.5.9999",
|
||||
"vine==1.1.3 (from amqp==2.1.4->kombu==4.0.2->celery==4.0.2)",
|
||||
"celery==4.0.2",
|
||||
"pytz==2016.4 (from celery==4.0.2)",
|
||||
],
|
||||
),
|
||||
# Check that dependencies of relevant constraints are resolved
|
||||
(
|
||||
["aiohttp", ("yarl==1.4.2", True)],
|
||||
["aiohttp==3.6.2", "idna==2.8 (from yarl==1.4.2)", "yarl==1.4.2"],
|
||||
),
|
||||
]
|
||||
),
|
||||
)
|
||||
|
@ -236,6 +258,19 @@ def test_iter_dependencies(resolver, from_line):
|
|||
next(res._iter_dependencies(ireq))
|
||||
|
||||
|
||||
def test_iter_dependencies_results(resolver, from_line):
|
||||
res = resolver([])
|
||||
ireq = from_line("aiohttp==3.6.2")
|
||||
assert next(res._iter_dependencies(ireq)).comes_from == ireq
|
||||
|
||||
|
||||
def test_iter_dependencies_ignores_constraints(resolver, from_line):
|
||||
res = resolver([])
|
||||
ireq = from_line("aiohttp==3.6.2", constraint=True)
|
||||
with pytest.raises(StopIteration):
|
||||
next(res._iter_dependencies(ireq))
|
||||
|
||||
|
||||
def test_combine_install_requirements(from_line):
|
||||
celery30 = from_line("celery>3.0", comes_from="-r requirements.in")
|
||||
celery31 = from_line("celery==3.1.1", comes_from=from_line("fake-package"))
|
||||
|
@ -264,8 +299,8 @@ def test_compile_failure_shows_provenance(resolver, from_line):
|
|||
with pytest.raises(NoCandidateFound) as err:
|
||||
resolver(requirements).resolve()
|
||||
lines = str(err.value).splitlines()
|
||||
assert lines[-2].strip() == "celery>3.2"
|
||||
assert (
|
||||
lines[-2].strip()
|
||||
lines[-1].strip()
|
||||
== "celery==3.1.18 (from fake-piptools-test-with-pinned-deps==0.1)"
|
||||
)
|
||||
assert lines[-1].strip() == "celery>3.2"
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import os
|
||||
import platform
|
||||
import sys
|
||||
import tempfile
|
||||
from collections import Counter
|
||||
|
@ -7,6 +6,9 @@ from collections import Counter
|
|||
import mock
|
||||
import pytest
|
||||
|
||||
from .constants import PACKAGES_PATH
|
||||
|
||||
from piptools._compat import path_to_url
|
||||
from piptools.exceptions import IncompatibleRequirements
|
||||
from piptools.sync import dependency_tree, diff, merge, sync
|
||||
|
||||
|
@ -226,17 +228,9 @@ def test_diff_leave_piptools_alone(fake_dist, from_line):
|
|||
assert to_uninstall == {"foobar"}
|
||||
|
||||
|
||||
def _get_file_url(local_path):
|
||||
if platform.system() == "Windows":
|
||||
local_path = "/%s" % local_path.replace("\\", "/")
|
||||
return "file://%s" % local_path
|
||||
|
||||
|
||||
def test_diff_with_editable(fake_dist, from_editable):
|
||||
installed = [fake_dist("small-fake-with-deps==0.0.1"), fake_dist("six==1.10.0")]
|
||||
path_to_package = os.path.join(
|
||||
os.path.dirname(__file__), "test_data", "small_fake_package"
|
||||
)
|
||||
path_to_package = os.path.join(PACKAGES_PATH, "small_fake_with_deps")
|
||||
reqs = [from_editable(path_to_package)]
|
||||
to_install, to_uninstall = diff(reqs, installed)
|
||||
|
||||
|
@ -247,7 +241,7 @@ def test_diff_with_editable(fake_dist, from_editable):
|
|||
assert len(to_install) == 1
|
||||
package = list(to_install)[0]
|
||||
assert package.editable
|
||||
assert str(package.link) == _get_file_url(path_to_package)
|
||||
assert package.link.url == path_to_url(path_to_package)
|
||||
|
||||
|
||||
def test_diff_with_matching_url_versions(fake_dist, from_line):
|
||||
|
@ -403,39 +397,81 @@ def test_sync_verbose(check_call, from_line):
|
|||
assert "-q" not in check_call_args
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("to_install", "to_uninstall", "expected_message"),
|
||||
[
|
||||
({"django==1.8", "click==4.0"}, set(), "Would install:"),
|
||||
(set(), {"django==1.8", "click==4.0"}, "Would uninstall:"),
|
||||
],
|
||||
)
|
||||
@mock.patch("piptools.sync.click.echo")
|
||||
def test_sync_dry_run_would_install(echo, from_line):
|
||||
def test_sync_dry_run(echo, from_line, to_install, to_uninstall, expected_message):
|
||||
"""
|
||||
Sync with --dry-run option prints what's is going to be installed.
|
||||
Sync with --dry-run option prints what's is going to be installed/uninstalled.
|
||||
"""
|
||||
to_install = {from_line("django==1.8"), from_line("click==4.0")}
|
||||
to_install = set(from_line(pkg) for pkg in to_install)
|
||||
to_uninstall = set(from_line(pkg) for pkg in to_uninstall)
|
||||
|
||||
sync(to_install, set(), dry_run=True)
|
||||
sync(to_install, to_uninstall, dry_run=True)
|
||||
|
||||
expected_calls = [
|
||||
mock.call("Would install:"),
|
||||
mock.call(expected_message),
|
||||
mock.call(" django==1.8"),
|
||||
mock.call(" click==4.0"),
|
||||
]
|
||||
echo.assert_has_calls(expected_calls, any_order=True)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("to_install", "to_uninstall", "expected_message"),
|
||||
[
|
||||
({"django==1.8", "click==4.0"}, set(), "Would install:"),
|
||||
(set(), {"django==1.8", "click==4.0"}, "Would uninstall:"),
|
||||
],
|
||||
)
|
||||
@mock.patch("piptools.sync.check_call")
|
||||
@mock.patch("piptools.sync.click.confirm")
|
||||
@mock.patch("piptools.sync.click.echo")
|
||||
def test_sync_dry_run_would_uninstall(echo, from_line):
|
||||
def test_sync_ask_declined(
|
||||
echo, confirm, check_call, from_line, to_install, to_uninstall, expected_message
|
||||
):
|
||||
"""
|
||||
Sync with --dry-run option prints what is going to be uninstalled.
|
||||
Sync with --ask option does a dry run if the user declines
|
||||
"""
|
||||
to_uninstall = {from_line("django==1.8"), from_line("click==4.0")}
|
||||
confirm.return_value = False
|
||||
|
||||
sync(set(), to_uninstall, dry_run=True)
|
||||
to_install = set(from_line(pkg) for pkg in to_install)
|
||||
to_uninstall = set(from_line(pkg) for pkg in to_uninstall)
|
||||
|
||||
sync(to_install, to_uninstall, ask=True)
|
||||
|
||||
expected_calls = [
|
||||
mock.call("Would uninstall:"),
|
||||
mock.call(expected_message),
|
||||
mock.call(" django==1.8"),
|
||||
mock.call(" click==4.0"),
|
||||
]
|
||||
echo.assert_has_calls(expected_calls, any_order=True)
|
||||
|
||||
confirm.assert_called_once_with("Would you like to proceed with these changes?")
|
||||
check_call.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("dry_run", [True, False])
|
||||
@mock.patch("piptools.sync.click.confirm")
|
||||
@mock.patch("piptools.sync.check_call")
|
||||
def test_sync_ask_accepted(check_call, confirm, from_line, dry_run):
|
||||
"""
|
||||
pip should be called as normal when the user confirms, even with dry_run
|
||||
"""
|
||||
confirm.return_value = True
|
||||
|
||||
sync(
|
||||
{from_line("django==1.8")}, {from_line("click==4.0")}, ask=True, dry_run=dry_run
|
||||
)
|
||||
assert check_call.call_count == 2
|
||||
|
||||
confirm.assert_called_once_with("Would you like to proceed with these changes?")
|
||||
|
||||
|
||||
@mock.patch("piptools.sync.check_call")
|
||||
def test_sync_uninstall_pip_command(check_call):
|
||||
|
|
|
@ -2,7 +2,8 @@ import os
|
|||
|
||||
import pytest
|
||||
|
||||
from piptools.pip import get_pip_command
|
||||
from .constants import PACKAGES_PATH
|
||||
|
||||
from piptools.repositories import PyPIRepository
|
||||
|
||||
|
||||
|
@ -16,42 +17,22 @@ class MockedPyPIRepository(PyPIRepository):
|
|||
return super(MockedPyPIRepository, self).get_dependencies(ireq)
|
||||
|
||||
|
||||
def _get_repository():
|
||||
pip_command = get_pip_command()
|
||||
pip_args = []
|
||||
pip_options, _ = pip_command.parse_args(pip_args)
|
||||
session = pip_command._build_session(pip_options)
|
||||
repository = MockedPyPIRepository(pip_options, session)
|
||||
return repository
|
||||
@pytest.fixture
|
||||
def mocked_repository(tmpdir):
|
||||
return MockedPyPIRepository(["--no-index"], cache_dir=str(tmpdir / "pypi-repo"))
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("input", "expected"),
|
||||
(
|
||||
(tup)
|
||||
for tup in [
|
||||
(
|
||||
[
|
||||
os.path.join(
|
||||
os.path.dirname(__file__), "test_data", "small_fake_package"
|
||||
)
|
||||
],
|
||||
["six"],
|
||||
)
|
||||
]
|
||||
),
|
||||
)
|
||||
def test_editable_top_level_deps_preserved(
|
||||
base_resolver, repository, from_editable, input, expected
|
||||
base_resolver, mocked_repository, from_editable
|
||||
):
|
||||
input = [from_editable(line) for line in input]
|
||||
repository = _get_repository()
|
||||
output = base_resolver(input, prereleases=False, repository=repository).resolve()
|
||||
package_path = os.path.join(PACKAGES_PATH, "small_fake_with_deps")
|
||||
ireqs = [from_editable(package_path)]
|
||||
output = base_resolver(
|
||||
ireqs, prereleases=False, repository=mocked_repository
|
||||
).resolve()
|
||||
|
||||
output = {p.name for p in output}
|
||||
|
||||
# sanity check that we're expecting something
|
||||
assert output != set()
|
||||
|
||||
for package_name in expected:
|
||||
assert package_name in output
|
||||
assert "small-fake-a" in output
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
# coding: utf-8
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import itertools
|
||||
import os
|
||||
|
||||
import pytest
|
||||
import six
|
||||
from pytest import mark, raises
|
||||
from six.moves import shlex_quote
|
||||
|
||||
from piptools.repositories import PyPIRepository
|
||||
from piptools.scripts.compile import cli as compile_cli
|
||||
from piptools.utils import (
|
||||
as_tuple,
|
||||
create_install_command,
|
||||
dedup,
|
||||
flat_map,
|
||||
force_text,
|
||||
|
@ -18,6 +22,7 @@ from piptools.utils import (
|
|||
fs_str,
|
||||
get_compile_command,
|
||||
get_hashes_from_ireq,
|
||||
get_trusted_hosts,
|
||||
is_pinned_requirement,
|
||||
is_url_requirement,
|
||||
name_from_req,
|
||||
|
@ -324,3 +329,29 @@ def test_get_compile_command_sort_args(tmpdir_cwd):
|
|||
"--no-annotate --no-emit-trusted-host --no-index "
|
||||
"requirements.in setup.py"
|
||||
)
|
||||
|
||||
|
||||
def test_create_install_command():
|
||||
"""
|
||||
Test create_install_command returns an instance of InstallCommand.
|
||||
"""
|
||||
install_command = create_install_command()
|
||||
assert install_command.name == "install"
|
||||
|
||||
|
||||
@mark.parametrize(
|
||||
"hosts",
|
||||
[
|
||||
pytest.param((), id="no hosts"),
|
||||
pytest.param(("example.com",), id="single host"),
|
||||
pytest.param(("example.com:8080",), id="host with port"),
|
||||
pytest.param(("example1.com", "example2.com:8080"), id="multiple hosts"),
|
||||
],
|
||||
)
|
||||
def test_get_trusted_hosts(hosts, tmpdir):
|
||||
"""
|
||||
Test get_trusted_hosts(finder) returns a list of hosts.
|
||||
"""
|
||||
pip_args = list(itertools.chain(*zip(["--trusted-host"] * len(hosts), hosts)))
|
||||
repository = PyPIRepository(pip_args, cache_dir=str(tmpdir / "pypi-repo"))
|
||||
assert tuple(get_trusted_hosts(repository.finder)) == hosts
|
||||
|
|
|
@ -41,6 +41,7 @@ def writer(tmpdir_cwd):
|
|||
format_control=FormatControl(set(), set()),
|
||||
allow_unsafe=False,
|
||||
find_links=[],
|
||||
emit_find_links=True,
|
||||
)
|
||||
yield writer
|
||||
|
||||
|
@ -48,54 +49,48 @@ def writer(tmpdir_cwd):
|
|||
def test_format_requirement_annotation_editable(from_editable, writer):
|
||||
# Annotations are printed as comments at a fixed column
|
||||
ireq = from_editable("git+git://fake.org/x/y.git#egg=y")
|
||||
reverse_dependencies = {"y": ["xyz"]}
|
||||
ireq.comes_from = "xyz"
|
||||
|
||||
assert writer._format_requirement(
|
||||
ireq, reverse_dependencies, primary_packages=[]
|
||||
ireq
|
||||
) == "-e git+git://fake.org/x/y.git#egg=y " + comment("# via xyz")
|
||||
|
||||
|
||||
def test_format_requirement_annotation(from_line, writer):
|
||||
ireq = from_line("test==1.2")
|
||||
reverse_dependencies = {"test": ["xyz"]}
|
||||
ireq.comes_from = "xyz"
|
||||
|
||||
assert writer._format_requirement(
|
||||
ireq, reverse_dependencies, primary_packages=[]
|
||||
) == "test==1.2 " + comment("# via xyz")
|
||||
assert writer._format_requirement(ireq) == "test==1.2 " + comment(
|
||||
"# via xyz"
|
||||
)
|
||||
|
||||
|
||||
def test_format_requirement_annotation_lower_case(from_line, writer):
|
||||
ireq = from_line("Test==1.2")
|
||||
reverse_dependencies = {"test": ["xyz"]}
|
||||
ireq.comes_from = "xyz"
|
||||
|
||||
assert writer._format_requirement(
|
||||
ireq, reverse_dependencies, primary_packages=[]
|
||||
) == "test==1.2 " + comment("# via xyz")
|
||||
|
||||
|
||||
def test_format_requirement_not_for_primary(from_line, writer):
|
||||
"Primary packages should not get annotated."
|
||||
ireq = from_line("test==1.2")
|
||||
reverse_dependencies = {"test": ["xyz"]}
|
||||
|
||||
assert (
|
||||
writer._format_requirement(
|
||||
ireq, reverse_dependencies, primary_packages=["test"]
|
||||
)
|
||||
== "test==1.2"
|
||||
assert writer._format_requirement(ireq) == "test==1.2 " + comment(
|
||||
"# via xyz"
|
||||
)
|
||||
|
||||
|
||||
def test_format_requirement_not_for_primary_lower_case(from_line, writer):
|
||||
"Primary packages should not get annotated."
|
||||
ireq = from_line("Test==1.2")
|
||||
reverse_dependencies = {"test": ["xyz"]}
|
||||
def test_format_requirement_for_primary(from_line, writer):
|
||||
"Primary packages should get annotated."
|
||||
ireq = from_line("test==1.2")
|
||||
ireq.comes_from = "xyz"
|
||||
|
||||
assert (
|
||||
writer._format_requirement(
|
||||
ireq, reverse_dependencies, primary_packages=["test"]
|
||||
)
|
||||
== "test==1.2"
|
||||
assert writer._format_requirement(ireq) == "test==1.2 " + comment(
|
||||
"# via xyz"
|
||||
)
|
||||
|
||||
|
||||
def test_format_requirement_for_primary_lower_case(from_line, writer):
|
||||
"Primary packages should get annotated."
|
||||
ireq = from_line("Test==1.2")
|
||||
ireq.comes_from = "xyz"
|
||||
|
||||
assert writer._format_requirement(ireq) == "test==1.2 " + comment(
|
||||
"# via xyz"
|
||||
)
|
||||
|
||||
|
||||
|
@ -104,11 +99,8 @@ def test_format_requirement_environment_marker(from_line, writer):
|
|||
ireq = from_line(
|
||||
'test ; python_version == "2.7" and platform_python_implementation == "CPython"'
|
||||
)
|
||||
reverse_dependencies = set()
|
||||
|
||||
result = writer._format_requirement(
|
||||
ireq, reverse_dependencies, primary_packages=["test"], marker=ireq.markers
|
||||
)
|
||||
result = writer._format_requirement(ireq, marker=ireq.markers)
|
||||
assert (
|
||||
result == 'test ; python_version == "2.7" and '
|
||||
'platform_python_implementation == "CPython"'
|
||||
|
@ -118,52 +110,53 @@ def test_format_requirement_environment_marker(from_line, writer):
|
|||
@mark.parametrize(("allow_unsafe",), [(True,), (False,)])
|
||||
def test_iter_lines__unsafe_dependencies(writer, from_line, allow_unsafe):
|
||||
writer.allow_unsafe = allow_unsafe
|
||||
output = "\n".join(
|
||||
writer._iter_lines([from_line("test==1.2")], [from_line("setuptools")])
|
||||
writer.emit_header = False
|
||||
|
||||
lines = writer._iter_lines(
|
||||
[from_line("test==1.2")], [from_line("setuptools==1.10.0")]
|
||||
)
|
||||
assert (
|
||||
"\n".join(
|
||||
[
|
||||
"test==1.2",
|
||||
"",
|
||||
MESSAGE_UNSAFE_PACKAGES,
|
||||
"setuptools" if allow_unsafe else comment("# setuptools"),
|
||||
]
|
||||
)
|
||||
in output
|
||||
|
||||
expected_lines = (
|
||||
"test==1.2",
|
||||
"",
|
||||
MESSAGE_UNSAFE_PACKAGES,
|
||||
"setuptools==1.10.0" if allow_unsafe else comment("# setuptools"),
|
||||
)
|
||||
assert tuple(lines) == expected_lines
|
||||
|
||||
|
||||
def test_iter_lines__unsafe_with_hashes(writer, from_line):
|
||||
writer.allow_unsafe = False
|
||||
writer.emit_header = False
|
||||
ireqs = [from_line("test==1.2")]
|
||||
unsafe_ireqs = [from_line("setuptools")]
|
||||
unsafe_ireqs = [from_line("setuptools==1.10.0")]
|
||||
hashes = {ireqs[0]: {"FAKEHASH"}, unsafe_ireqs[0]: set()}
|
||||
output = "\n".join(writer._iter_lines(ireqs, unsafe_ireqs, hashes=hashes))
|
||||
assert (
|
||||
"\n".join(
|
||||
[
|
||||
"test==1.2 \\",
|
||||
" --hash=FAKEHASH",
|
||||
"",
|
||||
MESSAGE_UNSAFE_PACKAGES_UNPINNED,
|
||||
comment("# setuptools"),
|
||||
]
|
||||
)
|
||||
in output
|
||||
|
||||
lines = writer._iter_lines(ireqs, unsafe_ireqs, hashes=hashes)
|
||||
|
||||
expected_lines = (
|
||||
"test==1.2 \\\n --hash=FAKEHASH",
|
||||
"",
|
||||
MESSAGE_UNSAFE_PACKAGES_UNPINNED,
|
||||
comment("# setuptools"),
|
||||
)
|
||||
assert tuple(lines) == expected_lines
|
||||
|
||||
|
||||
def test_iter_lines__hash_missing(writer, from_line):
|
||||
writer.allow_unsafe = False
|
||||
writer.emit_header = False
|
||||
ireqs = [from_line("test==1.2"), from_line("file:///example/#egg=example")]
|
||||
hashes = {ireqs[0]: {"FAKEHASH"}, ireqs[1]: set()}
|
||||
output = "\n".join(writer._iter_lines(ireqs, hashes=hashes))
|
||||
assert (
|
||||
"\n".join(
|
||||
[MESSAGE_UNHASHED_PACKAGE, "file:///example/#egg=example", "test==1.2"]
|
||||
)
|
||||
in output
|
||||
|
||||
lines = writer._iter_lines(ireqs, hashes=hashes)
|
||||
|
||||
expected_lines = (
|
||||
MESSAGE_UNHASHED_PACKAGE,
|
||||
"file:///example/#egg=example",
|
||||
"test==1.2 \\\n --hash=FAKEHASH",
|
||||
)
|
||||
assert tuple(lines) == expected_lines
|
||||
|
||||
|
||||
def test_write_header(writer):
|
||||
|
|
|
@ -1,23 +1,27 @@
|
|||
[tox]
|
||||
envlist =
|
||||
# NOTE: keep this in sync with the env list in .travis.yml for tox-travis.
|
||||
py{27,35,36,37,38,py,py3}-pip{8.1.1,9.0.1,9.0.3,10.0.1,18.0,19.0,latest,master}-coverage
|
||||
py{27,35,36,37,38,py,py3}-pip{8.1.1,9.0.1,9.0.3,10.0.1,18.0,19.0.3,19.1,19.2.3,19.3,20.0,latest,master}-coverage
|
||||
checkqa
|
||||
readme
|
||||
skip_missing_interpreters = True
|
||||
|
||||
[testenv]
|
||||
deps =
|
||||
piplatest: pip
|
||||
pipmaster: -e git+https://github.com/pypa/pip.git@master#egg=pip
|
||||
pip8.1.1: pip==8.1.1
|
||||
pip9.0.1: pip==9.0.1
|
||||
pip9.0.3: pip==9.0.3
|
||||
pip10.0.1: pip==10.0.1
|
||||
pip18.0: pip==18.0
|
||||
pip19.0: pip==19.0
|
||||
pip19.0.3: pip==19.0.3
|
||||
pip19.1: pip==19.1
|
||||
pip19.2.3: pip==19.2.3
|
||||
pip19.3: pip==19.3
|
||||
pip20.0: pip==20.0.*
|
||||
mock
|
||||
pytest
|
||||
pytest!=5.1.2
|
||||
pytest-rerunfailures
|
||||
coverage: pytest-cov
|
||||
setenv =
|
||||
piplatest: PIP=latest
|
||||
|
@ -27,20 +31,29 @@ setenv =
|
|||
pip9.0.3: PIP=9.0.3
|
||||
pip10.0.1: PIP=10.0.1
|
||||
pip18.0: PIP=18.0
|
||||
pip19.0: PIP==19.0
|
||||
pip19.0.3: PIP==19.0.3
|
||||
pip19.1: PIP==19.1
|
||||
pip19.2.3: PIP==19.2.3
|
||||
pip19.3: PIP==19.3
|
||||
pip20.0: PIP==20.0
|
||||
|
||||
coverage: PYTEST_ADDOPTS=--strict --doctest-modules --cov --cov-report=term-missing {env:PYTEST_ADDOPTS:}
|
||||
install_command= python -m pip install {opts} {packages}
|
||||
commands_pre =
|
||||
piplatest: python -m pip install -U pip
|
||||
pip --version
|
||||
commands = pytest {posargs}
|
||||
passenv = CI
|
||||
|
||||
[testenv:checkqa]
|
||||
basepython = python3
|
||||
skip_install = True
|
||||
deps = pre-commit
|
||||
commands_pre =
|
||||
commands = pre-commit run --all-files --show-diff-on-failure
|
||||
|
||||
[testenv:readme]
|
||||
deps = twine
|
||||
commands_pre =
|
||||
commands = twine check {distdir}/*
|
||||
|
||||
[travis:env]
|
||||
|
@ -50,6 +63,10 @@ PIP =
|
|||
9.0.3: pip9.0.3
|
||||
10.0.1: pip10.0.1
|
||||
18.0: pip18.0
|
||||
19.0: pip19.0
|
||||
19.0.3: pip19.0.3
|
||||
19.1: pip19.1
|
||||
19.2.3: pip19.2.3
|
||||
19.3: pip19.3
|
||||
20.0: pip20.0
|
||||
latest: piplatest
|
||||
master: pipmaster
|
||||
|
|
|
@ -7,7 +7,7 @@ jsmin==2.1.0
|
|||
json-e==2.7.0
|
||||
mozilla-version==0.3.0
|
||||
pathlib2==2.3.2
|
||||
pip-tools==3.9.0
|
||||
pip-tools==4.5.1
|
||||
pipenv==2018.5.18
|
||||
psutil==5.7.0
|
||||
pytest==3.6.2
|
||||
|
|
|
@ -53,9 +53,9 @@ mozilla-version==0.3.0 \
|
|||
pathlib2==2.3.2 \
|
||||
--hash=sha256:8eb170f8d0d61825e09a95b38be068299ddeda82f35e96c3301a8a5e7604cb83 \
|
||||
--hash=sha256:d1aa2a11ba7b8f7b21ab852b1fb5afb277e1bb99d5dfc663380b5015c0d80c5a
|
||||
pip-tools==3.9.0 \
|
||||
--hash=sha256:0b866b6da097fa1f060a1ce3a22bccf515bf6af7654370bcb6d0fcc65daba2b5 \
|
||||
--hash=sha256:2457106d7df6b627cc54819e986db75ba5fbe8243ae3bdfbb79159c1f610e90b
|
||||
pip-tools==4.5.1 \
|
||||
--hash=sha256:693f30e451875796b1b25203247f0b4cf48a4c4a5ab7341f4f33ffd498cdcc98 \
|
||||
--hash=sha256:be9c796aa88b2eec5cabf1323ba1cb60a08212b84bfb75b8b4037a8ef8cb8cb6 \
|
||||
pipenv==2018.5.18 \
|
||||
--hash=sha256:04b9a8b02a3ff12a5502b335850cfdb192adcfd1d6bbdb7a7c47cae9ab9ddece \
|
||||
--hash=sha256:e96d5bfa6822a17b2200d455aa5f9002c14361c50df1b1e51921479d7c09e741
|
||||
|
|
Загрузка…
Ссылка в новой задаче