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:
Andrew Halberstadt 2020-03-09 13:23:08 +00:00
Родитель f01ae26843
Коммит 161d14b474
61 изменённых файлов: 1963 добавлений и 984 удалений

99
third_party/python/pip-tools/.appveyor.yml поставляемый
Просмотреть файл

@ -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
)

1
third_party/python/pip-tools/.coveragerc поставляемый
Просмотреть файл

@ -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/)).

28
third_party/python/pip-tools/.github/workflows/cron.yml поставляемый Normal file
Просмотреть файл

@ -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 }}

3
third_party/python/pip-tools/.gitignore поставляемый
Просмотреть файл

@ -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/

24
third_party/python/pip-tools/.travis.yml поставляемый
Просмотреть файл

@ -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:

116
third_party/python/pip-tools/CHANGELOG.md поставляемый
Просмотреть файл

@ -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*

204
third_party/python/pip-tools/PKG-INFO поставляемый
Просмотреть файл

@ -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

198
third_party/python/pip-tools/README.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):

31
third_party/python/pip-tools/piptools/pip.py поставляемый
Просмотреть файл

@ -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,
)
)

39
third_party/python/pip-tools/piptools/sync.py поставляемый
Просмотреть файл

@ -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(

8
third_party/python/pip-tools/setup.cfg поставляемый
Просмотреть файл

@ -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

6
third_party/python/pip-tools/setup.py поставляемый
Просмотреть файл

@ -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
)
)
)

5
third_party/python/pip-tools/tests/constants.py поставляемый Normal file
Просмотреть файл

@ -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"]}
}
}

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -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"],
)

Просмотреть файл

@ -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"],
)

Просмотреть файл

@ -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):

31
third_party/python/pip-tools/tox.ini поставляемый
Просмотреть файл

@ -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

2
third_party/python/requirements.in поставляемый
Просмотреть файл

@ -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

6
third_party/python/requirements.txt поставляемый
Просмотреть файл

@ -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