Commit 0cf4bd79 by Sébastien Eustace

Merge branch 'master' into develop

# Conflicts:
#	poetry/masonry/builders/builder.py
#	poetry/masonry/builders/wheel.py
#	tests/masonry/builders/fixtures/default_with_excluded_data_toml/pyproject.toml
#	tests/masonry/builders/fixtures/exclude_nested_data_toml/pyproject.toml
#	tests/masonry/builders/test_wheel.py
parents 39c57cc3 01ec4a16
......@@ -16,8 +16,8 @@ assignees: ''
-->
<!-- Checked checkbox should look like this: [x] -->
- [ ] I am on the [latest](https://github.com/sdispater/poetry/releases/latest) Poetry version.
- [ ] I have searched the [issues](https://github.com/sdispater/poetry/issues) of this repo and believe that this is not a duplicate.
- [ ] I am on the [latest](https://github.com/python-poetry/poetry/releases/latest) Poetry version.
- [ ] I have searched the [issues](https://github.com/python-poetry/poetry/issues) of this repo and believe that this is not a duplicate.
- [ ] If an exception occurs when executing a command, I executed it again in debug mode (`-vvv` option).
<!--
......
---
name: "\U0001F4DA Documentation"
about: Did you find errors, problems, or anything unintelligible in the docs (https://poetry.eustace.io/docs)?
about: Did you find errors, problems, or anything unintelligible in the docs (https://python-poetry.org/docs)?
title: ''
labels: 'Documentation'
assignees: ''
......@@ -16,7 +16,7 @@ assignees: ''
-->
<!-- Checked checkbox should look like this: [x] -->
- [ ] I have searched the [issues](https://github.com/sdispater/poetry/issues) of this repo and believe that this is not a duplicate.
- [ ] I have searched the [issues](https://github.com/python-poetry/poetry/issues) of this repo and believe that this is not a duplicate.
## Issue
<!-- Now feel free to write your issue, but please be descriptive! Thanks again 🙌 ❤️ -->
......@@ -13,8 +13,8 @@ assignees: ''
<!-- Checked checkbox should look like this: [x] -->
- [ ] I have searched the [issues](https://github.com/sdispater/poetry/issues) of this repo and believe that this is not a duplicate.
- [ ] I have searched the [documentation](https://poetry.eustace.io/docs/) and believe that my question is not covered.
- [ ] I have searched the [issues](https://github.com/python-poetry/poetry/issues) of this repo and believe that this is not a duplicate.
- [ ] I have searched the [documentation](https://python-poetry.org/docs/) and believe that my question is not covered.
## Issue
<!-- Now feel free to write your issue, but please be descriptive! Thanks again 🙌 ❤️ -->
......@@ -16,8 +16,8 @@ assignees: ''
-->
<!-- Checked checkbox should look like this: [x] -->
- [ ] I have searched the [issues](https://github.com/sdispater/poetry/issues) of this repo and believe that this is not a duplicate.
- [ ] I have searched the [documentation](https://poetry.eustace.io/docs/) and believe that my question is not covered.
- [ ] I have searched the [issues](https://github.com/python-poetry/poetry/issues) of this repo and believe that this is not a duplicate.
- [ ] I have searched the [documentation](https://python-poetry.org/docs/) and believe that my question is not covered.
## Feature Request
<!-- Now feel free to write your idea for improvement. Thanks again 🙌 ❤️ -->
# Pull Request Check List
This is just a reminder about the most common mistakes. Please make sure that you tick all *appropriate* boxes. But please read our [contribution guide](https://poetry.eustace.io/docs/contributing/) at least once, it will save you unnecessary review cycles!
This is just a reminder about the most common mistakes. Please make sure that you tick all *appropriate* boxes. But please read our [contribution guide](https://python-poetry.org/docs/contributing/) at least once, it will save you unnecessary review cycles!
- [ ] Added **tests** for changed code.
- [ ] Updated **documentation** for changed code.
......
exemptProjects: true
exemptMilestones: true
staleLabel: stale
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
closeComment: >
Closing this issue automatically because it has not had any activity since
it has been marked as stale. If you think it is still relevant and should
be addressed, feel free to open a new one.
pulls:
markComment: >
This pull request has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
closeComment: >
Closing this pull request automatically because it has not had any activity since
it has been marked as stale. If you think it is still relevant and should
be addressed, feel free to open a new one.
......@@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [2.7, 3.5, 3.6, 3.7]
python-version: [2.7, 3.5, 3.6, 3.7, 3.8]
steps:
- uses: actions/checkout@v1
......@@ -30,10 +30,16 @@ jobs:
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Install Poetry
- name: Install and set up Poetry
run: |
python get-poetry.py --preview -y
source $HOME/.poetry/env
poetry config virtualenvs.in-project true
- name: Set up cache
uses: actions/cache@v1
with:
path: .venv
key: ${{ runner.os }}-venv-${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }}
- name: Install dependencies
run: |
source $HOME/.poetry/env
......@@ -48,7 +54,7 @@ jobs:
runs-on: macos-latest
strategy:
matrix:
python-version: [2.7, 3.5, 3.6, 3.7]
python-version: [2.7, 3.5, 3.6, 3.7, 3.8]
steps:
- uses: actions/checkout@v1
......@@ -56,10 +62,16 @@ jobs:
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Install Poetry
- name: Install and set up Poetry
run: |
python get-poetry.py --preview -y
source $HOME/.poetry/env
poetry config virtualenvs.in-project true
- name: Set up cache
uses: actions/cache@v1
with:
path: .venv
key: ${{ runner.os }}-venv-${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }}
- name: Install dependencies
run: |
source $HOME/.poetry/env
......@@ -74,7 +86,7 @@ jobs:
runs-on: windows-latest
strategy:
matrix:
python-version: [2.7, 3.5, 3.6, 3.7]
python-version: [2.7, 3.5, 3.6, 3.7, 3.8]
steps:
- uses: actions/checkout@v1
......@@ -82,10 +94,16 @@ jobs:
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Install Poetry
- name: Install and setup Poetry
run: |
python get-poetry.py --preview -y
$env:Path += ";$env:Userprofile\.poetry\bin"
poetry config virtualenvs.in-project true
- name: Set up cache
uses: actions/cache@v1
with:
path: .venv
key: ${{ runner.os }}-venv-${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }}
- name: Install dependencies
run: |
$env:Path += ";$env:Userprofile\.poetry\bin"
......
name: Release
on:
push:
tags:
- '*.*.*'
jobs:
Linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Get tag
id: tag
run: |
echo ::set-output name=tag::${GITHUB_REF#refs/tags/}
- name: Building release
run: |
make linux_release
- name: Upload release file
uses: actions/upload-artifact@v1
with:
name: poetry-${{ steps.tag.outputs.tag }}-linux.tar.gz
path: releases/poetry-${{ steps.tag.outputs.tag }}-linux.tar.gz
- name: Upload checksum file
uses: actions/upload-artifact@v1
with:
name: poetry-${{ steps.tag.outputs.tag }}-linux.sha256sum
path: releases/poetry-${{ steps.tag.outputs.tag }}-linux.sha256sum
MacOS:
runs-on: macos-latest
steps:
- uses: actions/checkout@v1
- name: Get tag
id: tag
run: |
echo ::set-output name=tag::${GITHUB_REF#refs/tags/}
- name: Set up Python 3.8
uses: actions/setup-python@v1
with:
python-version: "3.8"
- name: Install Poetry
run: |
python get-poetry.py --preview -y
source $HOME/.poetry/env
- name: Install dependencies
run: |
source $HOME/.poetry/env
poetry install --no-dev
- name: Preparing Python executables
run: |
curl -L https://github.com/sdispater/python-binaries/releases/download/2.7.17/python-2.7.17.macos.tar.xz -o python-2.7.17.tar.xz
curl -L https://github.com/sdispater/python-binaries/releases/download/3.4.10/python-3.4.10.macos.tar.xz -o python-3.4.10.tar.xz
curl -L https://github.com/sdispater/python-binaries/releases/download/3.5.9/python-3.5.9.macos.tar.xz -o python-3.5.9.tar.xz
curl -L https://github.com/sdispater/python-binaries/releases/download/3.6.8/python-3.6.8.macos.tar.xz -o python-3.6.8.tar.xz
curl -L https://github.com/sdispater/python-binaries/releases/download/3.7.5/python-3.7.5.macos.tar.xz -o python-3.7.5.tar.xz
curl -L https://github.com/sdispater/python-binaries/releases/download/3.8.0/python-3.8.0.macos.tar.xz -o python-3.8.0.tar.xz
tar -zxf python-2.7.17.tar.xz
tar -zxf python-3.4.10.tar.xz
tar -zxf python-3.5.9.tar.xz
tar -zxf python-3.6.8.tar.xz
tar -zxf python-3.7.5.tar.xz
tar -zxf python-3.8.0.tar.xz
- name: Build specific release
run: |
source $HOME/.poetry/env
poetry run python sonnet make release --ansi -P "2.7:python-2.7.17/bin/python" -P "3.4:python-3.4.10/bin/python" -P "3.5:python-3.5.9/bin/python" -P "3.6:python-3.6.8/bin/python" -P "3.7:python-3.7.5/bin/python" -P "3.8:python-3.8.0/bin/python"
- name: Upload release file
uses: actions/upload-artifact@v1
with:
name: poetry-${{ steps.tag.outputs.tag }}-darwin.tar.gz
path: releases/poetry-${{ steps.tag.outputs.tag }}-darwin.tar.gz
- name: Upload checksum file
uses: actions/upload-artifact@v1
with:
name: poetry-${{ steps.tag.outputs.tag }}-darwin.sha256sum
path: releases/poetry-${{ steps.tag.outputs.tag }}-darwin.sha256sum
Windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v1
- name: Get tag
id: tag
shell: bash
run: |
echo ::set-output name=tag::${GITHUB_REF#refs/tags/}
- name: Set up Python 3.8
uses: actions/setup-python@v1
with:
python-version: "3.8"
- name: Install Poetry
run: |
python get-poetry.py --preview -y
$env:Path += ";$env:Userprofile\.poetry\bin"
- name: Install dependencies
run: |
$env:Path += ";$env:Userprofile\.poetry\bin"
poetry install --no-dev
- name: Preparing Python executables
run: |
Invoke-WebRequest https://github.com/sdispater/python-binaries/releases/download/2.7.17/python-2.7.17.windows.tar.xz -O python-2.7.17.tar.xz
Invoke-WebRequest https://github.com/sdispater/python-binaries/releases/download/3.4.4/python-3.4.4.windows.tar.xz -O python-3.4.4.tar.xz
Invoke-WebRequest https://github.com/sdispater/python-binaries/releases/download/3.5.4/python-3.5.4.windows.tar.xz -O python-3.5.4.tar.xz
Invoke-WebRequest https://github.com/sdispater/python-binaries/releases/download/3.6.8/python-3.6.8.windows.tar.xz -O python-3.6.8.tar.xz
Invoke-WebRequest https://github.com/sdispater/python-binaries/releases/download/3.7.5/python-3.7.5.windows.tar.xz -O python-3.7.5.tar.xz
Invoke-WebRequest https://github.com/sdispater/python-binaries/releases/download/3.8.0/python-3.8.0.windows.tar.xz -O python-3.8.0.tar.xz
7z x python-2.7.17.tar.xz
7z x python-3.4.4.tar.xz
7z x python-3.5.4.tar.xz
7z x python-3.6.8.tar.xz
7z x python-3.7.5.tar.xz
7z x python-3.8.0.tar.xz
7z x python-2.7.17.tar
7z x python-3.4.4.tar
7z x python-3.5.4.tar
7z x python-3.6.8.tar
7z x python-3.7.5.tar
7z x python-3.8.0.tar
- name: Build specific release
run: |
$env:Path += ";$env:Userprofile\.poetry\bin"
poetry run python sonnet make release --ansi -P "2.7:python-2.7.17\python.exe" -P "3.4:python-3.4.4\python.exe" -P "3.5:python-3.5.4\python.exe" -P "3.6:python-3.6.8\python.exe" -P "3.7:python-3.7.5\python.exe" -P "3.8:python-3.8.0\python.exe"
- name: Upload release file
uses: actions/upload-artifact@v1
with:
name: poetry-${{ steps.tag.outputs.tag }}-win32.tar.gz
path: releases/poetry-${{ steps.tag.outputs.tag }}-win32.tar.gz
- name: Upload checksum file
uses: actions/upload-artifact@v1
with:
name: poetry-${{ steps.tag.outputs.tag }}-win32.sha256sum
path: releases/poetry-${{ steps.tag.outputs.tag }}-win32.sha256sum
Release:
needs: [Linux, MacOS, Windows]
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@master
- name: Get tag
id: tag
run: |
echo ::set-output name=tag::${GITHUB_REF#refs/tags/}
- name: Download Linux release file
uses: actions/download-artifact@master
with:
name: poetry-${{ steps.tag.outputs.tag }}-linux.tar.gz
path: releases/poetry-${{ steps.tag.outputs.tag }}-linux.tar.gz
- name: Download Linux checksum file
uses: actions/download-artifact@master
with:
name: poetry-${{ steps.tag.outputs.tag }}-linux.sha256sum
path: releases/poetry-${{ steps.tag.outputs.tag }}-linux.sha256sum
- name: Download MacOS release file
uses: actions/download-artifact@master
with:
name: poetry-${{ steps.tag.outputs.tag }}-darwin.tar.gz
path: releases/poetry-${{ steps.tag.outputs.tag }}-darwin.tar.gz
- name: Download MacOS checksum file
uses: actions/download-artifact@master
with:
name: poetry-${{ steps.tag.outputs.tag }}-darwin.sha256sum
path: releases/poetry-${{ steps.tag.outputs.tag }}-darwin.sha256sum
- name: Download Windows release file
uses: actions/download-artifact@master
with:
name: poetry-${{ steps.tag.outputs.tag }}-win32.tar.gz
path: releases/poetry-${{ steps.tag.outputs.tag }}-win32.tar.gz
- name: Download Windows checksum file
uses: actions/download-artifact@master
with:
name: poetry-${{ steps.tag.outputs.tag }}-win32.sha256sum
path: releases/poetry-${{ steps.tag.outputs.tag }}-win32.sha256sum
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.tag.outputs.tag }}
release_name: ${{ steps.tag.outputs.tag }}
draft: false
prerelease: false
- name: Upload Linux release file asset
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: releases/poetry-${{ steps.tag.outputs.tag }}-linux.tar.gz/poetry-${{ steps.tag.outputs.tag }}-linux.tar.gz
asset_name: poetry-${{ steps.tag.outputs.tag }}-linux.tar.gz
asset_content_type: application/gzip
- name: Upload Linux checksum file asset
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: releases/poetry-${{ steps.tag.outputs.tag }}-linux.sha256sum/poetry-${{ steps.tag.outputs.tag }}-linux.sha256sum
asset_name: poetry-${{ steps.tag.outputs.tag }}-linux.sha256sum
asset_content_type: text/pain
- name: Upload MacOS release file asset
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: releases/poetry-${{ steps.tag.outputs.tag }}-darwin.tar.gz/poetry-${{ steps.tag.outputs.tag }}-darwin.tar.gz
asset_name: poetry-${{ steps.tag.outputs.tag }}-darwin.tar.gz
asset_content_type: application/gzip
- name: Upload MacOS checksum file asset
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: releases/poetry-${{ steps.tag.outputs.tag }}-darwin.sha256sum/poetry-${{ steps.tag.outputs.tag }}-darwin.sha256sum
asset_name: poetry-${{ steps.tag.outputs.tag }}-darwin.sha256sum
asset_content_type: text/pain
- name: Upload Windows release file asset
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: releases/poetry-${{ steps.tag.outputs.tag }}-win32.tar.gz/poetry-${{ steps.tag.outputs.tag }}-win32.tar.gz
asset_name: poetry-${{ steps.tag.outputs.tag }}-win32.tar.gz
asset_content_type: application/gzip
- name: Upload Windows checksum file asset
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: releases/poetry-${{ steps.tag.outputs.tag }}-win32.sha256sum/poetry-${{ steps.tag.outputs.tag }}-win32.sha256sum
asset_name: poetry-${{ steps.tag.outputs.tag }}-win32.sha256sum
asset_content_type: text/pain
- name: Install Poetry
run: |
python get-poetry.py --preview -y
- name: Install dependencies
run: |
source $HOME/.poetry/env
poetry install --no-dev
- name: Build project for distribution
run: |
source $HOME/.poetry/env
poetry run poetry build
- name: Publish to PyPI
env:
POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }}
run: |
source $HOME/.poetry/env
poetry run poetry publish
......@@ -2,7 +2,9 @@
# Packages
*.egg
*.egg-info
!/tests/**/*.egg
/*.egg-info
/tests/fixtures/**/*.egg-info
/dist/*
build
_build
......
......@@ -20,5 +20,7 @@ repos:
rev: v2.3.0
hooks:
- id: trailing-whitespace
exclude: ^tests/.*/fixtures/.*
- id: end-of-file-fixer
exclude: ^tests/.*/fixtures/.*
- id: debug-statements
# Change Log
## [Unreleased]
## [1.0.1] - 2020-01-10
### Fixed
- Fixed an error in `env use` where the wrong Python executable was being used to check compatibility ([#1736](https://github.com/python-poetry/poetry/pull/1736)).
- Fixed an error where VCS dependencies were not properly categorized as development dependencies ([#1725](https://github.com/python-poetry/poetry/pull/1725)).
- Fixed an error where some shells would no longer be usable after using the `shell` command ([#1673](https://github.com/python-poetry/poetry/pull/1673)).
- Fixed an error where explicitly included files where not included in wheel distributions ([#1750](https://github.com/python-poetry/poetry/pull/1750)).
- Fixed an error where some Git dependencies url were not properly parsed ([#1756](https://github.com/python-poetry/poetry/pull/1756)).
- Fixed an error in the `env` commands on Windows if the path to the executable contained a space ([#1774](https://github.com/python-poetry/poetry/pull/1774)).
- Fixed several errors and UX issues caused by `keyring` on some systems ([#1788](https://github.com/python-poetry/poetry/pull/1788)).
- Fixed errors when trying to detect installed packages ([#1786](https://github.com/python-poetry/poetry/pull/1786)).
- Fixed an error when packaging projects where Python packages were not properly detected ([#1592](https://github.com/python-poetry/poetry/pull/1592)).
- Fixed an error where local file dependencies were exported as editable when using the `export` command ([#1840](https://github.com/python-poetry/poetry/pull/1840)).
- Fixed the way environment markers are propagated and evaluated when resolving dependencies ([#1829](https://github.com/python-poetry/poetry/pull/1829), [#1789](https://github.com/python-poetry/poetry/pull/1789)).
- Fixed an error in the PEP-508 compliant representation of directory and file dependencies ([#1796](https://github.com/python-poetry/poetry/pull/1796)).
- Fixed an error where invalid virtual environments would be silently used. They will not be recreated and a warning will be displayed ([#1797](https://github.com/python-poetry/poetry/pull/1797)).
- Fixed an error where dependencies were not properly detected when reading the `setup.py` file in some cases ([#1764](https://github.com/python-poetry/poetry/pull/1764)).
## [1.0.0] - 2019-12-12
### Added
......@@ -18,10 +38,10 @@
- Full environment markers are now supported for dependencies via the `markers` property.
- Added the ability to specify git dependencies directly in `add`, it no longer requires the `--git` option.
- Added the ability to specify path dependencies directly in `add`, it no longer requires the `--path` option.
- Added support for url dependencies ([#1260](https://github.com/sdispater/poetry/pull/1260)).
- Publishing to PyPI using [API tokens](https://pypi.org/help/#apitoken) is now supported ([#1275](https://github.com/sdispater/poetry/pull/1275)).
- Added support for url dependencies ([#1260](https://github.com/python-poetry/poetry/pull/1260)).
- Publishing to PyPI using [API tokens](https://pypi.org/help/#apitoken) is now supported ([#1275](https://github.com/python-poetry/poetry/pull/1275)).
- Licenses can now be identified by their full name.
- Added support for support for custom certificate authority and client certificates for private repositories.
- Added support for custom certificate authority and client certificates for private repositories.
- Poetry can now detect and use Conda environments.
### Changed
......@@ -35,11 +55,16 @@
- The `--git` option of the `add` command has been removed.
- The `--path` option of the `add` command has been removed.
- The `add` command will now automatically select the latest prerelease if only prereleases are available.
- The `add` command can now update a dependencies if an explicit constraint is given ([#1221](https://github.com/sdispater/poetry/pull/1221)).
- The `add` command can now update a dependencies if an explicit constraint is given ([#1221](https://github.com/python-poetry/poetry/pull/1221)).
- Removed the `--develop` option from the `install` command.
- Improved UX when searching for packages in the `init` command.
- The `shell` has been improved.
- The `shell` command has been improved.
- The `poetry run` command now uses `os.execvp()` rather than spawning a new subprocess.
- Specifying dependencies with `allows-prereleases` in the `pyproject.toml` file is deprecated for consistency with the `add` command. Use `allow-prereleases` instead.
- Improved the error message when the lock file is invalid.
- Whenever Poetry needs to use the "system" Python, it will now call `sys.executable` instead of the `python` command.
- Improved the error message displayed on conflicting Python requirements ([#1681](https://github.com/python-poetry/poetry/pull/1681)).
- Improved the `site-packages` directory detection ([#1683](https://github.com/python-poetry/poetry/pull/1683)).
### Fixed
......@@ -56,6 +81,18 @@
- Fixed dependency resolution for conditional development dependencies.
- Fixed generated dependency constraints when they contain inequality operators.
- The `run` command now properly handles the `--` separator.
- Fixed some issues with `path` dependencies being seen as `git` dependencies.
- Fixed various issues with the way `extra` markers in dependencies were handled.
- Fixed the option conflicts in the `run` command.
- Fixed wrong latest version being displayed when executing `show -l`.
- Fixed `TooManyRedirects` errors being raised when resolving dependencies.
- Fixed custom indices dependencies being constantly updated.
- Fixed the behavior of the `--install` option of the debug resolve command.
- Fixed an error in `show` when using the `-o/--outdated` option.
- Fixed PEP 508 url dependency handling.
- Fixed excluded files via the `exclude` being included in distributions.
- Fixed an error in `env use` if the `virtualenvs.in-project` setting is activated ([#1682](https://github.com/python-poetry/poetry/pull/1682))
- Fixed handling of `empty` and `any` markers in unions of markers ([#1650](https://github.com/python-poetry/poetry/pull/1650)).
## [0.12.17] - 2019-07-03
......@@ -739,56 +776,58 @@ Initial release
[Unreleased]: https://github.com/sdispater/poetry/compare/0.12.17...master
[0.12.17]: https://github.com/sdispater/poetry/releases/tag/0.12.17
[0.12.16]: https://github.com/sdispater/poetry/releases/tag/0.12.16
[0.12.15]: https://github.com/sdispater/poetry/releases/tag/0.12.15
[0.12.14]: https://github.com/sdispater/poetry/releases/tag/0.12.14
[0.12.13]: https://github.com/sdispater/poetry/releases/tag/0.12.13
[0.12.12]: https://github.com/sdispater/poetry/releases/tag/0.12.12
[0.12.11]: https://github.com/sdispater/poetry/releases/tag/0.12.11
[0.12.10]: https://github.com/sdispater/poetry/releases/tag/0.12.10
[0.12.9]: https://github.com/sdispater/poetry/releases/tag/0.12.9
[0.12.8]: https://github.com/sdispater/poetry/releases/tag/0.12.8
[0.12.7]: https://github.com/sdispater/poetry/releases/tag/0.12.7
[0.12.6]: https://github.com/sdispater/poetry/releases/tag/0.12.6
[0.12.5]: https://github.com/sdispater/poetry/releases/tag/0.12.5
[0.12.4]: https://github.com/sdispater/poetry/releases/tag/0.12.4
[0.12.3]: https://github.com/sdispater/poetry/releases/tag/0.12.3
[0.12.2]: https://github.com/sdispater/poetry/releases/tag/0.12.2
[0.12.1]: https://github.com/sdispater/poetry/releases/tag/0.12.1
[0.12.0]: https://github.com/sdispater/poetry/releases/tag/0.12.0
[0.11.5]: https://github.com/sdispater/poetry/releases/tag/0.11.5
[0.11.4]: https://github.com/sdispater/poetry/releases/tag/0.11.4
[0.11.3]: https://github.com/sdispater/poetry/releases/tag/0.11.3
[0.11.2]: https://github.com/sdispater/poetry/releases/tag/0.11.2
[0.11.1]: https://github.com/sdispater/poetry/releases/tag/0.11.1
[0.11.0]: https://github.com/sdispater/poetry/releases/tag/0.11.0
[0.10.3]: https://github.com/sdispater/poetry/releases/tag/0.10.3
[0.10.2]: https://github.com/sdispater/poetry/releases/tag/0.10.2
[0.10.1]: https://github.com/sdispater/poetry/releases/tag/0.10.1
[0.10.0]: https://github.com/sdispater/poetry/releases/tag/0.10.0
[0.9.1]: https://github.com/sdispater/poetry/releases/tag/0.9.1
[0.9.0]: https://github.com/sdispater/poetry/releases/tag/0.9.0
[0.8.6]: https://github.com/sdispater/poetry/releases/tag/0.8.6
[0.8.5]: https://github.com/sdispater/poetry/releases/tag/0.8.5
[0.8.4]: https://github.com/sdispater/poetry/releases/tag/0.8.4
[0.8.3]: https://github.com/sdispater/poetry/releases/tag/0.8.3
[0.8.2]: https://github.com/sdispater/poetry/releases/tag/0.8.2
[0.8.1]: https://github.com/sdispater/poetry/releases/tag/0.8.1
[0.8.0]: https://github.com/sdispater/poetry/releases/tag/0.8.0
[0.7.1]: https://github.com/sdispater/poetry/releases/tag/0.7.1
[0.7.0]: https://github.com/sdispater/poetry/releases/tag/0.7.0
[0.6.5]: https://github.com/sdispater/poetry/releases/tag/0.6.5
[0.6.4]: https://github.com/sdispater/poetry/releases/tag/0.6.4
[0.6.3]: https://github.com/sdispater/poetry/releases/tag/0.6.3
[0.6.2]: https://github.com/sdispater/poetry/releases/tag/0.6.2
[0.6.1]: https://github.com/sdispater/poetry/releases/tag/0.6.1
[0.6.0]: https://github.com/sdispater/poetry/releases/tag/0.6.0
[0.5.0]: https://github.com/sdispater/poetry/releases/tag/0.5.0
[0.4.2]: https://github.com/sdispater/poetry/releases/tag/0.4.2
[0.4.1]: https://github.com/sdispater/poetry/releases/tag/0.4.1
[0.4.0]: https://github.com/sdispater/poetry/releases/tag/0.4.0
[0.3.0]: https://github.com/sdispater/poetry/releases/tag/0.3.0
[0.2.0]: https://github.com/sdispater/poetry/releases/tag/0.2.0
[0.1.0]: https://github.com/sdispater/poetry/releases/tag/0.1.0
[Unreleased]: https://github.com/python-poetry/poetry/compare/1.0.1...master
[1.0.1]: https://github.com/python-poetry/poetry/releases/tag/1.0.1
[1.0.0]: https://github.com/python-poetry/poetry/releases/tag/1.0.0
[0.12.17]: https://github.com/python-poetry/poetry/releases/tag/0.12.17
[0.12.16]: https://github.com/python-poetry/poetry/releases/tag/0.12.16
[0.12.15]: https://github.com/python-poetry/poetry/releases/tag/0.12.15
[0.12.14]: https://github.com/python-poetry/poetry/releases/tag/0.12.14
[0.12.13]: https://github.com/python-poetry/poetry/releases/tag/0.12.13
[0.12.12]: https://github.com/python-poetry/poetry/releases/tag/0.12.12
[0.12.11]: https://github.com/python-poetry/poetry/releases/tag/0.12.11
[0.12.10]: https://github.com/python-poetry/poetry/releases/tag/0.12.10
[0.12.9]: https://github.com/python-poetry/poetry/releases/tag/0.12.9
[0.12.8]: https://github.com/python-poetry/poetry/releases/tag/0.12.8
[0.12.7]: https://github.com/python-poetry/poetry/releases/tag/0.12.7
[0.12.6]: https://github.com/python-poetry/poetry/releases/tag/0.12.6
[0.12.5]: https://github.com/python-poetry/poetry/releases/tag/0.12.5
[0.12.4]: https://github.com/python-poetry/poetry/releases/tag/0.12.4
[0.12.3]: https://github.com/python-poetry/poetry/releases/tag/0.12.3
[0.12.2]: https://github.com/python-poetry/poetry/releases/tag/0.12.2
[0.12.1]: https://github.com/python-poetry/poetry/releases/tag/0.12.1
[0.12.0]: https://github.com/python-poetry/poetry/releases/tag/0.12.0
[0.11.5]: https://github.com/python-poetry/poetry/releases/tag/0.11.5
[0.11.4]: https://github.com/python-poetry/poetry/releases/tag/0.11.4
[0.11.3]: https://github.com/python-poetry/poetry/releases/tag/0.11.3
[0.11.2]: https://github.com/python-poetry/poetry/releases/tag/0.11.2
[0.11.1]: https://github.com/python-poetry/poetry/releases/tag/0.11.1
[0.11.0]: https://github.com/python-poetry/poetry/releases/tag/0.11.0
[0.10.3]: https://github.com/python-poetry/poetry/releases/tag/0.10.3
[0.10.2]: https://github.com/python-poetry/poetry/releases/tag/0.10.2
[0.10.1]: https://github.com/python-poetry/poetry/releases/tag/0.10.1
[0.10.0]: https://github.com/python-poetry/poetry/releases/tag/0.10.0
[0.9.1]: https://github.com/python-poetry/poetry/releases/tag/0.9.1
[0.9.0]: https://github.com/python-poetry/poetry/releases/tag/0.9.0
[0.8.6]: https://github.com/python-poetry/poetry/releases/tag/0.8.6
[0.8.5]: https://github.com/python-poetry/poetry/releases/tag/0.8.5
[0.8.4]: https://github.com/python-poetry/poetry/releases/tag/0.8.4
[0.8.3]: https://github.com/python-poetry/poetry/releases/tag/0.8.3
[0.8.2]: https://github.com/python-poetry/poetry/releases/tag/0.8.2
[0.8.1]: https://github.com/python-poetry/poetry/releases/tag/0.8.1
[0.8.0]: https://github.com/python-poetry/poetry/releases/tag/0.8.0
[0.7.1]: https://github.com/python-poetry/poetry/releases/tag/0.7.1
[0.7.0]: https://github.com/python-poetry/poetry/releases/tag/0.7.0
[0.6.5]: https://github.com/python-poetry/poetry/releases/tag/0.6.5
[0.6.4]: https://github.com/python-poetry/poetry/releases/tag/0.6.4
[0.6.3]: https://github.com/python-poetry/poetry/releases/tag/0.6.3
[0.6.2]: https://github.com/python-poetry/poetry/releases/tag/0.6.2
[0.6.1]: https://github.com/python-poetry/poetry/releases/tag/0.6.1
[0.6.0]: https://github.com/python-poetry/poetry/releases/tag/0.6.0
[0.5.0]: https://github.com/python-poetry/poetry/releases/tag/0.5.0
[0.4.2]: https://github.com/python-poetry/poetry/releases/tag/0.4.2
[0.4.1]: https://github.com/python-poetry/poetry/releases/tag/0.4.1
[0.4.0]: https://github.com/python-poetry/poetry/releases/tag/0.4.0
[0.3.0]: https://github.com/python-poetry/poetry/releases/tag/0.3.0
[0.2.0]: https://github.com/python-poetry/poetry/releases/tag/0.2.0
[0.1.0]: https://github.com/python-poetry/poetry/releases/tag/0.1.0
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at sebastien@eustace.io. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
......@@ -20,18 +20,18 @@ The following is a set of guidelines for contributing to Poetry on GitHub. These
This section guides you through submitting a bug report for Poetry.
Following these guidelines helps maintainers and the community understand your report, reproduce the behavior, and find related reports.
Before creating bug reports, please check [this list](#before-submitting-a-bug-report) to be sure that you need to create one. When you are creating a bug report, please include as many details as possible. Fill out the [required template](https://github.com/sdispater/poetry/blob/master/.github/ISSUE_TEMPLATE/1_Bug_report.md), the information it asks helps the maintainers resolve the issue faster.
Before creating bug reports, please check [this list](#before-submitting-a-bug-report) to be sure that you need to create one. When you are creating a bug report, please include as many details as possible. Fill out the [required template](https://github.com/python-poetry/poetry/blob/master/.github/ISSUE_TEMPLATE/1_Bug_report.md), the information it asks helps the maintainers resolve the issue faster.
> **Note:** If you find a **Closed** issue that seems like it is the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one.
#### Before submitting a bug report
* **Check the [FAQs on the official website](https://poetry.eustace.io)** for a list of common questions and problems.
* **Check that your issue does not already exist in the [issue tracker](https://github.com/sdispater/poetry/issues)**.
* **Check the [FAQs on the official website](https://python-poetry.org)** for a list of common questions and problems.
* **Check that your issue does not already exist in the [issue tracker](https://github.com/python-poetry/poetry/issues)**.
#### How do I submit a bug report?
Bugs are tracked on the [official issue tracker](https://github.com/sdispater/poetry/issues) where you can create a new one and provide the following information by filling in [the template](https://github.com/sdispater/poetry/blob/master/.github/ISSUE_TEMPLATE/1_Bug_report.md).
Bugs are tracked on the [official issue tracker](https://github.com/python-poetry/poetry/issues) where you can create a new one and provide the following information by filling in [the template](https://github.com/python-poetry/poetry/blob/master/.github/ISSUE_TEMPLATE/1_Bug_report.md).
Explain the problem and include additional details to help maintainers reproduce the problem:
......@@ -60,17 +60,17 @@ Include details about your configuration and environment:
This section guides you through submitting an enhancement suggestion for Poetry, including completely new features and minor improvements to existing functionality. Following these guidelines helps maintainers and the community understand your suggestion and find related suggestions.
Before creating enhancement suggestions, please check [this list](#before-submitting-an-enhancement-suggestion) as you might find out that you don't need to create one. When you are creating an enhancement suggestion, please [include as many details as possible](#how-do-i-submit-an-enhancement-suggestion). Fill in [the template](https://github.com/sdispater/poetry/blob/master/.github/ISSUE_TEMPLATE/2_Feature_request.md), including the steps that you imagine you would take if the feature you're requesting existed.
Before creating enhancement suggestions, please check [this list](#before-submitting-an-enhancement-suggestion) as you might find out that you don't need to create one. When you are creating an enhancement suggestion, please [include as many details as possible](#how-do-i-submit-an-enhancement-suggestion). Fill in [the template](https://github.com/python-poetry/poetry/blob/master/.github/ISSUE_TEMPLATE/2_Feature_request.md), including the steps that you imagine you would take if the feature you're requesting existed.
#### Before submitting an enhancement suggestion
* **Check the [FAQs on the official website](https://poetry.eustace.io) for a list of common questions and problems.
* **Check that your issue does not already exist in the [issue tracker](https://github.com/sdispater/poetry/issues).
* **Check the [FAQs on the official website](https://python-poetry.org) for a list of common questions and problems.
* **Check that your issue does not already exist in the [issue tracker](https://github.com/python-poetry/poetry/issues).
#### How do I submit an Enhancement suggestion?
Enhancement suggestions are tracked on the [official issue tracker](https://github.com/sdispater/poetry/issues) where you can create a new one and provide the following information:
Enhancement suggestions are tracked on the [official issue tracker](https://github.com/python-poetry/poetry/issues) where you can create a new one and provide the following information:
* **Use a clear and descriptive title** for the issue to identify the suggestion.
* **Provide a step-by-step description of the suggested enhancement** in as many details as possible.
......@@ -82,12 +82,12 @@ Enhancement suggestions are tracked on the [official issue tracker](https://gith
#### Local development
You will need Poetry to start contributing on the Poetry codebase. Refer to the [documentation](https://poetry.eustace.io/docs/#introduction) to start using Poetry.
You will need Poetry to start contributing on the Poetry codebase. Refer to the [documentation](https://python-poetry.org/docs/#introduction) to start using Poetry.
You will first need to clone the repository using `git` and place yourself in its directory:
```bash
$ git clone git@github.com:sdispater/poetry.git
$ git clone git@github.com:python-poetry/poetry.git
$ cd poetry
```
......@@ -123,6 +123,6 @@ will not be merged.
#### Pull requests
* Fill in [the required template](https://github.com/sdispater/poetry/blob/master/.github/PULL_REQUEST_TEMPLATE.md)
* Fill in [the required template](https://github.com/python-poetry/poetry/blob/master/.github/PULL_REQUEST_TEMPLATE.md)
* Be sure that your pull request contains tests that cover the changed or added code.
* If your changes warrant a documentation change, the pull request must also update the documentation.
# This file is part of Poetry
# https://github.com/sdispater/poetry
# https://github.com/python-poetry/poetry
# Licensed under the MIT license:
# http://www.opensource.org/licenses/MIT-license
......@@ -48,7 +48,7 @@ wheel:
linux_release:
docker pull quay.io/pypa/manylinux2010_x86_64
docker run --rm -t -i -v `pwd`:/io quay.io/pypa/manylinux2010_x86_64 /io/make-linux-release.sh
docker run --rm -i -v `pwd`:/io quay.io/pypa/manylinux2010_x86_64 /io/make-linux-release.sh
# run tests against all supported python versions
tox:
......
......@@ -3,11 +3,13 @@
Poetry helps you declare, manage and install dependencies of Python projects,
ensuring you have the right stack everywhere.
![Poetry Install](https://raw.githubusercontent.com/sdispater/poetry/master/assets/install.gif)
![Poetry Install](https://raw.githubusercontent.com/python-poetry/poetry/master/assets/install.gif)
It supports Python 2.7 and 3.4+.
![Tests Status](https://github.com/sdispater/poetry/workflows/Tests/badge.svg)
![Tests Status](https://github.com/python-poetry/poetry/workflows/Tests/badge.svg)
The [complete documentation](https://python-poetry.org/docs/) is available on the [official website](https://python-poetry.org).
## Installation
......@@ -16,7 +18,7 @@ from the rest of your system by vendorizing its dependencies. This is the
recommended way of installing `poetry`.
```bash
curl -sSL https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py | python
curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python
```
Alternatively, you can download the `get-poetry.py` file and execute it separately.
......@@ -44,25 +46,29 @@ which might cause conflicts.
## Updating `poetry`
Updating poetry to the latest stable version is as simple as calling the `self:update` command.
Updating poetry to the latest stable version is as simple as calling the `self update` command.
```bash
poetry self:update
poetry self update
```
If you want to install prerelease versions, you can use the `--preview` option.
```bash
poetry self:update --preview
poetry self update --preview
```
And finally, if you want to install a specific version you can pass it as an argument
to `self:update`.
to `self update`.
```bash
poetry self:update 0.8.0
poetry self update 1.0.0
```
!!!note
If you are still on poetry version < 1.0 use `poetry self:update` instead.
## Enable tab completion for Bash, Fish, or Zsh
......@@ -118,8 +124,8 @@ authors = [
readme = 'README.md' # Markdown files are supported
repository = "https://github.com/sdispater/poetry"
homepage = "https://github.com/sdispater/poetry"
repository = "https://github.com/python-poetry/poetry"
homepage = "https://github.com/python-poetry/poetry"
keywords = ['packaging', 'poetry']
......@@ -129,7 +135,7 @@ toml = "^0.9"
# Dependencies with extras
requests = { version = "^2.13", extras = [ "security" ] }
# Python specific dependencies with prereleases allowed
pathlib2 = { version = "^2.2", python = "~2.7", allows-prereleases = true }
pathlib2 = { version = "^2.2", python = "~2.7", allow-prereleases = true }
# Git dependencies
cleo = { git = "https://github.com/sdispater/cleo.git", branch = "master" }
......@@ -238,599 +244,8 @@ so `poetry` will try to find a version of `oslo.i18n` that satisfies `pbr (>=0.6
By analyzing the releases of `oslo.i18n`, it will find `oslo.i18n==2.1.0` which requires `pbr (>=0.11,<2.0)`.
At this point the rest of the resolution is straightforward since there is no more conflict.
#### Install command
When you specify a package to the `install` command it will add it as a wildcard
dependency. This means that **any** version of this package can be installed which
can lead to compatibility issues.
Also, you have to explicitly tell it to not update the locked packages when you
install new ones. This should be the default.
#### Uninstall command
The `uninstall` command will only remove the package specified but not its dependencies
if they are no longer needed.
You either have to use `sync` or `clean` to fix that.
#### Too limited in scope
Finally, the `Pipfile` is just a replacement from `requirements.txt` and, in the end, you will still need to
populate your `setup.py` file (or `setup.cfg`) with the exact same dependencies you declared in your `Pipfile`.
So, in the end, you will still need to manage a few configuration files to properly setup your project.
## Commands
### new
This command will help you kickstart your new Python project by creating
a directory structure suitable for most projects.
```bash
poetry new my-package
```
will create a folder as follows:
```text
my-package
├── pyproject.toml
├── README.rst
├── my_package
│ └── __init__.py
└── tests
├── __init__.py
└── test_my_package
```
If you want to name your project differently than the folder, you can pass
the `--name` option:
```bash
poetry new my-folder --name my-package
```
### init
This command will help you create a `pyproject.toml` file interactively
by prompting you to provide basic information about your package.
It will interactively ask you to fill in the fields, while using some smart defaults.
```bash
poetry init
```
#### Options
* `--name`: Name of the package.
* `--description`: Description of the package.
* `--author`: Author of the package.
* `--dependency`: Package to require with a version constraint. Should be in format `foo:1.0.0`.
* `--dev-dependency`: Development requirements, see `--require`.
### install
The `install` command reads the `pyproject.toml` file from the current directory, resolves the dependencies,
and installs them.
```bash
poetry install
```
If there is a `poetry.lock` file in the current directory,
it will use the exact versions from there instead of resolving them.
This ensures that everyone using the library will get the same versions of the dependencies.
If there is no `poetry.lock` file, Poetry will create one after dependency resolution.
You can specify to the command that you do not want the development dependencies installed by passing
the `--no-dev` option.
```bash
poetry install --no-dev
```
You can also specify the extras you want installed
by passing the `-E|--extras` option (See [Extras](#extras) for more info)
```bash
poetry install --extras "mysql pgsql"
poetry install -E mysql -E pgsql
```
By default `poetry` will install your project's package everytime you run `install`:
```bash
$ poetry install
Installing dependencies from lock file
Nothing to install or update
- Installing <your-package-name> (x.x.x)
```
If you want to skip this installation, use the `--no-root` option.
```bash
poetry install --no-root
```
#### Options
* `--no-dev`: Do not install dev dependencies.
* `--no-root`: Do not install the root package (your project).
* `-E|--extras`: Features to install (multiple values allowed).
### update
In order to get the latest versions of the dependencies and to update the `poetry.lock` file,
you should use the `update` command.
```bash
poetry update
```
This will resolve all dependencies of the project and write the exact versions into `poetry.lock`.
If you just want to update a few packages and not all, you can list them as such:
```bash
poetry update requests toml
```
#### Options
* `--dry-run` : Outputs the operations but will not execute anything (implicitly enables --verbose).
* `--no-dev` : Do not install dev dependencies.
* `--lock` : Do not perform install (only update the lockfile).
### add
The `add` command adds required packages to your `pyproject.toml` and installs them.
If you do not specify a version constraint,
poetry will choose a suitable one based on the available package versions.
```bash
poetry add requests pendulum
```
#### Options
* `--D|dev`: Add package as development dependency.
* `--optional` : Add as an optional dependency.
* `--dry-run` : Outputs the operations but will not execute anything (implicitly enables --verbose).
### remove
The `remove` command removes a package from the current
list of installed packages
```bash
poetry remove pendulum
```
#### Options
* `--D|dev`: Removes a package from the development dependencies.
* `--dry-run` : Outputs the operations but will not execute anything (implicitly enables --verbose).
### show
To list all of the available packages, you can use the `show` command.
```bash
poetry show
```
If you want to see the details of a certain package, you can pass the package name.
```bash
poetry show pendulum
name : pendulum
version : 1.4.2
description : Python datetimes made easy
dependencies:
- python-dateutil >=2.6.1
- tzlocal >=1.4
- pytzdata >=2017.2.2
```
#### Options
* `--no-dev`: Do not list the dev dependencies.
* `--tree`: List the dependencies as a tree.
* `-l|--latest`: Show the latest version.
* `-o|--outdated`: Show the latest version but only for packages that are outdated.
### build
The `build` command builds the source and wheels archives.
```bash
poetry build
```
Note that, at the moment, only pure python wheels are supported.
#### Options
* `-f|--format`: Limit the format to either wheel or sdist.
### publish
This command builds (if not already built) and publishes the package to the remote repository.
It will automatically register the package before uploading if this is the first time it is submitted.
```bash
poetry publish
```
#### Options
* `-r|--repository`: The repository to register the package to (default: `pypi`).
Should match a repository name set by the [`config`](#config) command.
* `--username (-u)`: The username to access the repository.
* `--password (-p)`: The password to access the repository.
### `config`
The `config` command allows you to edit poetry config settings and repositories.
```bash
poetry config --list
```
#### Usage
````bash
poetry config [options] [setting-key] [setting-value1] ... [setting-valueN]
````
`setting-key` is a configuration option name and `setting-value1` is a configuration value.
#### Modifying repositories
In addition to modifying the config section,
the config command also supports making changes to the repositories section by using it the following way:
```bash
poetry config repositories.foo https://foo.bar/simple/
```
This will set the url for repository `foo` to `https://foo.bar/simple/`.
If you want to store your credentials for a specific repository, you can do so easily:
```bash
poetry config http-basic.foo username password
```
If you do not specify the password you will be prompted to write it.
#### Options
* `--unset`: Remove the configuration element named by `setting-key`.
* `--list`: Show the list of current config variables.
### search
This command searches for packages on a remote index.
```bash
poetry search requests pendulum
```
#### Options
* `-N|--only-name`: Search only in name.
### lock
This command locks (without installing) the dependencies specified in `pyproject.toml`.
```bash
poetry lock
```
### export
This command exports the lock file to other formats.
```bash
poetry export -f requirements.txt > requirements.txt
```
#### Options
* `--format (-f)`: the format to export to. Currently, only
`requirements.txt` is supported.
* `--output (-o)`: the name of the output file. If omitted, print to standard
output.
## The `pyproject.toml` file
The `tool.poetry` section of the `pyproject.toml` file is composed of multiple sections.
#### name
The name of the package. **Required**
#### version
The version of the package. **Required**
This should follow [semantic versioning](http://semver.org/). However it will not be enforced and you remain
free to follow another specification.
#### description
A short description of the package. **Required**
#### license
The license of the package.
The recommended notation for the most common licenses is (alphabetical):
* Apache-2.0
* BSD-2-Clause
* BSD-3-Clause
* BSD-4-Clause
* GPL-2.0
* GPL-2.0+
* GPL-3.0
* GPL-3.0+
* LGPL-2.1
* LGPL-2.1+
* LGPL-3.0
* LGPL-3.0+
* MIT
Optional, but it is highly recommended to supply this.
More identifiers are listed at the [SPDX Open Source License Registry](https://www.spdx.org/licenses/).
#### authors
The authors of the package. **Required**
This is a list of authors and should contain at least one author. Authors must be in the form `name <email>`.
#### readme
The readme file of the package. **Required**
The file can be either `README.rst` or `README.md`.
#### homepage
An URL to the website of the project. **Optional**
#### repository
An URL to the repository of the project. **Optional**
#### documentation
An URL to the documentation of the project. **Optional**
#### keywords
A list of keywords (max: 5) that the package is related to. **Optional**
#### include and exclude
A list of patterns that will be included in the final package.
You can explicitly specify to Poetry that a set of globs should be ignored or included for the purposes of packaging.
The globs specified in the exclude field identify a set of files that are not included when a package is built.
If a VCS is being used for a package, the exclude field will be seeded with the VCS’ ignore settings (`.gitignore` for git for example).
```toml
[tool.poetry]
# ...
include = ["package/**/*.py", "package/**/.c"]
```
```toml
exclude = ["package/excluded.py"]
```
### `dependencies` and `dev-dependencies`
Poetry is configured to look for dependencies on [PyPi](https://pypi.org) by default.
Only the name and a version string are required in this case.
```toml
[tool.poetry.dependencies]
requests = "^2.13.0"
```
If you want to use a private repository, you can add it to your `pyproject.toml` file, like so:
```toml
[[tool.poetry.source]]
name = 'private'
url = 'http://example.com/simple'
```
Be aware that declaring the python version for which your package
is compatible is mandatory:
```toml
[tool.poetry.dependencies]
python = "^3.6"
```
#### Caret requirement
**Caret requirements** allow SemVer compatible updates to a specified version.
An update is allowed if the new version number does not modify the left-most non-zero digit in the major, minor, patch grouping.
In this case, if we ran `poetry update requests`, poetry would update us to version `2.14.0` if it was available,
but would not update us to `3.0.0`.
If instead we had specified the version string as `^0.1.13`, poetry would update to `0.1.14` but not `0.2.0`.
`0.0.x` is not considered compatible with any other version.
Here are some more examples of caret requirements and the versions that would be allowed with them:
```text
^1.2.3 := >=1.2.3 <2.0.0
^1.2 := >=1.2.0 <2.0.0
^1 := >=1.0.0 <2.0.0
^0.2.3 := >=0.2.3 <0.3.0
^0.0.3 := >=0.0.3 <0.0.4
^0.0 := >=0.0.0 <0.1.0
^0 := >=0.0.0 <1.0.0
```
#### Tilde requirements
**Tilde requirements** specify a minimal version with some ability to update.
If you specify a major, minor, and patch version or only a major and minor version, only patch-level changes are allowed.
If you only specify a major version, then minor- and patch-level changes are allowed.
`~1.2.3` is an example of a tilde requirement.
```text
~1.2.3 := >=1.2.3 <1.3.0
~1.2 := >=1.2.0 <1.3.0
~1 := >=1.0.0 <2.0.0
```
#### Wildcard requirements
**Wildcard requirements** allow for any version where the wildcard is positioned.
`*`, `1.*` and `1.2.*` are examples of wildcard requirements.
```text
* := >=0.0.0
1.* := >=1.0.0 <2.0.0
1.2.* := >=1.2.0 <1.3.0
```
#### Inequality requirements
**Inequality requirements** allow manually specifying a version range or an exact version to depend on.
Here are some examples of inequality requirements:
```text
>= 1.2.0
> 1
< 2
!= 1.2.3
```
#### Multiple requirements
Multiple version requirements can also be separated with a comma, e.g. `>= 1.2, < 1.5`.
#### `git` dependencies
To depend on a library located in a `git` repository,
the minimum information you need to specify is the location of the repository with the git key:
```toml
[tool.poetry.dependencies]
requests = { git = "https://github.com/requests/requests.git" }
```
Since we haven’t specified any other information,
Poetry assumes that we intend to use the latest commit on the `master` branch to build our project.
You can combine the `git` key with the `rev`, `tag`, or `branch` keys to specify something else.
Here's an example of specifying that you want to use the latest commit on a branch named `next`:
```toml
[tool.poetry.dependencies]
requests = { git = "https://github.com/kennethreitz/requests.git", branch = "next" }
```
#### Python restricted dependencies
You can also specify that a dependency should be installed only for specific Python versions:
```toml
[tool.poetry.dependencies]
pathlib2 = { version = "^2.2", python = "~2.7" }
```
```toml
[tool.poetry.dependencies]
pathlib2 = { version = "^2.2", python = ["~2.7", "^3.2"] }
```
### `scripts`
This section describe the scripts or executable that will be installed when installing the package
```toml
[tool.poetry.scripts]
poetry = 'poetry.console:run'
```
After installing a package with the above toml, `poetry` will be a global command available from the command line that will execute `console.run` in the `poetry` package.
### `extras`
Poetry supports extras to allow expression of:
* optional dependencies, which enhance a package, but are not required; and
* clusters of optional dependencies.
```toml
[tool.poetry]
name = "awesome"
[tool.poetry.dependencies]
# These packages are mandatory and form the core of this package’s distribution.
mandatory = "^1.0"
# A list of all of the optional dependencies, some of which are included in the
# below `extras`. They can be opted into by apps.
psycopg2 = { version = "^2.7", optional = true }
mysqlclient = { version = "^1.3", optional = true }
[tool.poetry.extras]
mysql = ["mysqlclient"]
pgsql = ["psycopg2"]
```
When installing packages, you can specify extras by using the `-E|--extras` option:
```bash
poetry install --extras "mysql pgsql"
poetry install -E mysql -E pgsql
```
### `plugins`
Poetry supports arbitrary plugins which work similarly to
[setuptools entry points](http://setuptools.readthedocs.io/en/latest/setuptools.html).
To match the example in the setuptools documentation, you would use the following:
```toml
[tool.poetry.plugins] # Optional super table
[tool.poetry.plugins."blogtool.parsers"]
".rst" = "some_module::SomeClass"
```
## Resources
* [Official Website](https://poetry.eustace.io)
* [Issue Tracker](https://github.com/sdispater/poetry/issues)
* [Official Website](https://python-poetry.org)
* [Issue Tracker](https://github.com/python-poetry/poetry/issues)
* [Discord](https://discordapp.com/invite/awxPgve)
......@@ -69,7 +69,8 @@ It will automatically find a suitable version constraint **and install** the pac
In our example, we are requesting the `pendulum` package with the version constraint `^1.4`.
This means any version greater or equal to 1.4.0 and less than 2.0.0 (`>=1.4.0 <2.0.0`).
Please read [versions](/docs/versions/) for more in-depth information on versions, how versions relate to each other, and on version constraints.
Please read [Dependency specification](/docs/dependency-specification) for more in-depth information on versions,
how versions relate to each other, and on the different ways you can specify dependencies.
!!!note
......@@ -147,107 +148,3 @@ and update the lock file with the new versions.
Poetry will display a **Warning** when executing an install command if `poetry.lock` and `pyproject.toml`
are not synchronized.
## Poetry and virtualenvs
When you execute the `install` command (or any other "install" commands like `add` or `remove`),
Poetry will check if it's currently inside a virtualenv and, if not, will use an existing one
or create a brand new one for you to always work isolated from your global Python installation.
By default, Poetry will use the currently activated Python version
to create the virtualenv for the current project.
To easily switch between Python versions, it is recommended to
use [pyenv](https://github.com/pyenv/pyenv) or similar tools.
For instance, if your project is Python 2.7 only, a standard workflow
would be:
```bash
pyenv install 2.7.15
pyenv local 2.7.15 # Activate Python 2.7 for the current project
poetry install
```
However, this might not be feasible for your system, especially Windows where `pyenv`,
is not available. To circumvent that you can use the `env use` command to tell
Poetry which Python version to use for the current project.
```bash
poetry env use /full/path/to/python
```
If you have the python executable in your `PATH` you can use it:
```bash
poetry env use python3.7
```
You can even just use the minor Python version in this case:
```bash
poetry env use 3.7
```
If you want to disable the explicitly activated virtualenv, you can use the
special `system` Python version to retrieve the default behavior:
```bash
poetry env use system
```
If you want to get basic information about the currently activated virtualenv,
you can use the `env info` command:
```bash
poetry env info
```
will output something similar to this:
```text
Virtualenv
Python: 3.7.1
Implementation: CPython
Path: /path/to/poetry/cache/virtualenvs/test-O3eWbxRl-py3.7
Valid: True
System
Platform: darwin
OS: posix
Python: /path/to/main/python
```
If you only want to know the path to the virtualenv, you can pass the `--path` option
to `env info`:
```bash
poetry env info --path
```
You can also list all the virtualenvs associated with the current virtualenv
with the `env list` command:
```bash
poetry env list
```
will output something like the following:
```text
test-O3eWbxRl-py2.7
test-O3eWbxRl-py3.6
test-O3eWbxRl-py3.7 (Activated)
```
Finally, you can delete existing virtualenvs by using `env remove`:
```bash
poetry env remove /full/path/to/python
poetry env remove python3.7
poetry env remove 3.7
poetry env remove test-O3eWbxRl-py3.7
```
If your remove the currently activated virtualenv, it will be automatically deactivated.
......@@ -3,7 +3,7 @@
You've already learned how to use the command-line interface to do some things.
This chapter documents all the available commands.
To get help from the command-line, simply call `poetry` or `poetry list` to see the complete list of commands,
To get help from the command-line, simply call `poetry` to see the complete list of commands,
then `--help` combined with any of those can give you more information.
As `Poetry` uses [cleo](https://github.com/sdispater/cleo) you can call commands by short name if it's not ambiguous.
......@@ -130,7 +130,7 @@ By default `poetry` will install your project's package everytime you run `insta
$ poetry install
Installing dependencies from lock file
Nothing to install or update
No dependencies to install or update
- Installing <your-package-name> (x.x.x)
......@@ -208,8 +208,8 @@ If you need to checkout a specific branch, tag or revision,
you can specify it when using `add`:
```bash
poetry add git+https://github.com/sdispater/pendulum.git@develop
poetry add git+https://github.com/sdispater/pendulum.git@2.0.5
poetry add git+https://github.com/sdispater/pendulum.git#develop
poetry add git+https://github.com/sdispater/pendulum.git#2.0.5
```
or make them point to a local directory or file:
......@@ -451,106 +451,4 @@ poetry export -f requirements.txt > requirements.txt
The `env` command regroups sub commands to interact with the virtualenvs
associated with a specific project.
### env use
The `env use` command tells Poetry which Python version
to use for the current project.
```bash
poetry env use /full/path/to/python
```
If you have the python executable in your `PATH` you can use it:
```bash
poetry env use python3.7
```
You can even just use the minor Python version in this case:
```bash
poetry env use 3.7
```
If you want to disable the explicitly activated virtualenv, you can use the
special `system` Python version to retrieve the default behavior:
```bash
poetry env use system
```
### env info
The `env info` command displays basic information about the currently activated virtualenv:
```bash
poetry env info
```
will output something similar to this:
```text
Virtualenv
Python: 3.7.1
Implementation: CPython
Path: /path/to/poetry/cache/virtualenvs/test-O3eWbxRl-py3.7
Valid: True
System
Platform: darwin
OS: posix
Python: /path/to/main/python
```
If you only want to know the path to the virtualenv, you can pass the `--path` option
to `env info`:
```bash
poetry env info --path
```
#### Options
* `--path`: Only display the path of the virtualenv.
### env list
The `env list` command lists all the virtualenvs associated with the current virtualenv.
```bash
poetry env list
```
will output something like the following:
```text
test-O3eWbxRl-py2.7
test-O3eWbxRl-py3.6
test-O3eWbxRl-py3.7 (Activated)
```
#### Options
* `--full-path`: Display the full path of the virtualenvs.
### env remove
The `env remove` command deletes virtualenvs associated with the current project:
```bash
poetry env remove /full/path/to/python
```
Similarly to `env use`, you can either pass `python3.7`, `3.7` or the name of
the virtualenv (as returned by `env list`):
```bash
poetry env remove python3.7
poetry env remove 3.7
poetry env remove test-O3eWbxRl-py3.7
```
!!!note
If your remove the currently activated virtualenv, it will be automatically deactivated.
See [Managing environments](./managing-environments.md) for more information about these commands.
......@@ -100,11 +100,11 @@ Defaults to one of the following directories:
- macOS: `~/Library/Caches/pypoetry`
- Windows: `C:\Users\<username>\AppData\Local\pypoetry\Cache`
- Unix: `~/.cache/pypoetry/virtualenvs`
- Unix: `~/.cache/pypoetry`
### `virtualenvs.create`: boolean
Create a new virtualenv if one doesn't already exist.
Create a new virtual environment if one doesn't already exist.
Defaults to `true`.
### `virtualenvs.in-project`: boolean
......@@ -114,7 +114,7 @@ Defaults to `false`.
### `virtualenvs.path`: string
Directory where virtualenvs will be created.
Directory where virtual environments will be created.
Defaults to `{cache-dir}/virtualenvs` (`{cache-dir}\virtualenvs` on Windows).
### `repositories.<name>`: string
......
# Contributing to Poetry
First off, thank for taking the time to contribute!
The following is a set of guidelines for contributing to Poetry on GitHub. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.
#### Table of Contents
[How to contribute](#how-to-contribute)
* [Reporting bugs](#reporting-bugs)
* [Suggesting enhancements](#suggesting-enhancements)
* [Contributing to code](#contributing-to-code)
## How to contribute
### Reporting bugs
This section guides you through submitting a bug report for Poetry.
Following these guidelines helps maintainers and the community understand your report, reproduce the behavior, and find related reports.
Before creating bug reports, please check [this list](#before-submitting-a-bug-report) to be sure that you need to create one. When you are creating a bug report, please include as many details as possible. Fill out the [required template](https://github.com/sdispater/poetry/blob/master/.github/ISSUE_TEMPLATE/1_Bug_report.md), the information it asks helps the maintainers resolve the issue faster.
!!!note
If you find a **Closed** issue that seems like it is the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one.
#### Before submitting a bug report
* **Check the [FAQs on the official website](https://poetry.eustace.io)** for a list of common questions and problems.
* **Check that your issue does not already exist in the [issue tracker](https://github.com/sdispater/poetry/issues)**.
#### How do I submit a bug report
Bugs are tracked on the [official issue tracker](https://github.com/sdispater/poetry/issues) where you can create a new one and provide the following information by filling in [the template](https://github.com/sdispater/poetry/blob/master/.github/ISSUE_TEMPLATE/1_Bug_report.md).
Explain the problem and include additional details to help maintainers reproduce the problem:
* **Use a clear and descriptive title** for the issue to identify the problem.
* **Describe the exact steps which reproduce the problem** in as many details as possible.
* **Provide your pyproject.toml file** in a [Gist](https://gist.github.com) after removing potential private information (like private package repositories).
* **Provide specific examples to demonstrate the steps to reproduce the issue**. Include links to files or GitHub projects, or copy/pasteable snippets, which you use in those examples.
* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior.
* **Explain which behavior you expected to see instead and why.**
* **If the problem is an unexpected error being raised**, execute the corresponding command in **debug** mode (the `-vvv` option).
Provide more context by answering these questions:
* **Did the problem start happening recently** (e.g. after updating to a new version of Poetry) or was this always a problem?
* If the problem started happening recently, **can you reproduce the problem in an older version of Poetry?** What's the most recent version in which the problem doesn't happen?
* **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens.
Include details about your configuration and environment:
* **Which version of Poetry are you using?** You can get the exact version by running `poetry -V` in your terminal.
* **Which Python version Poetry has been installed for?** Execute the `debug:info` to get the information.
* **What's the name and version of the OS you're using**?
### Suggesting enhancements
This section guides you through submitting an enhancement suggestion for Poetry, including completely new features and minor improvements to existing functionality. Following these guidelines helps maintainers and the community understand your suggestion and find related suggestions.
Before creating enhancement suggestions, please check [this list](#before-submitting-an-enhancement-suggestion) as you might find out that you don't need to create one. When you are creating an enhancement suggestion, please [include as many details as possible](#how-do-i-submit-an-enhancement-suggestion). Fill in [the template](https://github.com/sdispater/poetry/blob/master/.github/ISSUE_TEMPLATE/2_Feature_request.md), including the steps that you imagine you would take if the feature you're requesting existed.
#### Before submitting an enhancement suggestion
* **Check the [FAQs on the official website](https://poetry.eustace.io)** for a list of common questions and problems.
* **Check that your issue does not already exist in the [issue tracker](https://github.com/sdispater/poetry/issues)**.
#### How do I submit an enhancement suggestion?
Enhancement suggestions are tracked on the [official issue tracker](https://github.com/sdispater/poetry/issues) where you can create a new one and provide the following information:
* **Use a clear and descriptive title** for the issue to identify the suggestion.
* **Provide a step-by-step description of the suggested enhancement** in as many details as possible.
* **Provide specific examples to demonstrate the steps**..
* **Describe the current behavior** and **explain which behavior you expected to see instead** and why.
### Contributing to code
#### Local development
You will need Poetry to start contributing on the Poetry codebase. Refer to the [documentation](https://poetry.eustace.io/docs/#introduction) to start using Poetry.
You will first need to clone the repository using `git` and place yourself in its directory:
```bash
$ git clone git@github.com:sdispater/poetry.git
$ cd poetry
```
Now, you will need to install the required dependency for Poetry and be sure that the current
tests are passing on your machine:
```bash
$ poetry install
$ poetry run pytest tests/
```
Poetry uses the [black](https://github.com/ambv/black) coding style and you must ensure that your
code follows it. If not, the CI will fail and your Pull Request will not be merged.
Similarly, the import statements are sorted with [isort](https://github.com/timothycrosley/isort)
and special care must be taken to respect it. If you don't, the CI will fail as well.
To make sure that you don't accidentally commit code that does not follow the coding style, you can
install a pre-commit hook that will check that everything is in order:
```bash
$ poetry run pre-commit install
```
You can also run it anytime using:
```bash
$ poetry run pre-commit run --all-files
```
Your code must always be accompanied by corresponding tests, if tests are not present your code
will not be merged.
#### Pull requests
* Fill in [the required template](https://github.com/sdispater/poetry/blob/master/.github/PULL_REQUEST_TEMPLATE.md)
* Be sure that you pull request contains tests that cover the changed or added code.
* If you changes warrant a documentation change, the pull request must also update the documentation.
{!../CONTRIBUTING.md!}
# Versions and constraints
# Dependency specification
Poetry recommends following [semantic versioning](https://semver.org) but will not enforce it.
Dependencies for a project can be specified in various forms, which depend on the type
of the dependency and on the optional constraints that might be needed for it to be installed.
## Version constraints
......@@ -74,7 +75,7 @@ If other dependencies require a different version, the solver will ultimately fa
Multiple version requirements can also be separated with a comma, e.g. `>= 1.2, < 1.5`.
### `git` dependencies
## `git` dependencies
To depend on a library located in a `git` repository,
the minimum information you need to specify is the location of the repository with the git key:
......@@ -85,16 +86,24 @@ requests = { git = "https://github.com/requests/requests.git" }
```
Since we haven’t specified any other information,
Poetry assumes that we intend to use the latest commit on the `master` branch to build our project.
You can combine the `git` key with the `rev`, `tag`, or `branch` keys to specify something else.
Here's an example of specifying that you want to use the latest commit on a branch named `next`:
Poetry assumes that we intend to use the latest commit on the `master` branch
to build our project.
You can combine the `git` key with the `branch` key to use another branch.
Alternatively, use `rev` or `tag` to pin a dependency to a specific commit hash
or tagged ref, respectively. For example:
```toml
[tool.poetry.dependencies]
# Get the latest revision on the branch named "next"
requests = { git = "https://github.com/kennethreitz/requests.git", branch = "next" }
# Get a revision by its commit hash
flask = { git = "https://github.com/pallets/flask.git", rev = "38eb5d3b" }
# Get a revision by its tag
numpy = { git = "https://github.com/numpy/numpy.git", tag = "v0.13.2" }
```
### `path` dependencies
## `path` dependencies
To depend on a library located in a local directory or file,
you can use the `path` property:
......@@ -109,7 +118,7 @@ my-package = { path = "../my-package/dist/my-package-0.1.0.tar.gz" }
```
### `url` dependencies
## `url` dependencies
To depend on a library located on a remote archive,
you can use the `url` property:
......@@ -127,7 +136,7 @@ poetry add https://example.com/my-package-0.1.0.tar.gz
```
### Python restricted dependencies
## Python restricted dependencies
You can also specify that a dependency should be installed only for specific Python versions:
......@@ -141,7 +150,7 @@ pathlib2 = { version = "^2.2", python = "~2.7" }
pathlib2 = { version = "^2.2", python = "~2.7 || ^3.2" }
```
### Using environment markers
## Using environment markers
If you need more complex install conditions for your dependencies,
Poetry supports [environment markers](https://www.python.org/dev/peps/pep-0508/#environment-markers)
......@@ -153,7 +162,7 @@ pathlib2 = { version = "^2.2", markers = "python_version ~= '2.7' or sys_platfor
```
### Multiple constraints dependencies
## Multiple constraints dependencies
Sometimes, one of your dependency may have different version ranges depending
on the target Python versions.
......
......@@ -60,14 +60,14 @@ commands =
poetry run pytest tests/
```
## I don't want Poetry to manage my virtualenvs. Can I disable it?
## I don't want Poetry to manage my virtual environments. Can I disable it?
While Poetry automatically creates virtualenvs to always work isolated
While Poetry automatically creates virtual environments to always work isolated
from the global Python installation, there are valid reasons why it's not necessary
and is an overhead, like when working with containers.
In this case, you can disable this feature by setting the `virtualenvs.create` setting to `false`:
```bash
poetry config settings.virtualenvs.create false
poetry config virtualenvs.create false
```
......@@ -16,8 +16,13 @@ Poetry provides a custom installer that will install `poetry` isolated
from the rest of your system by vendorizing its dependencies. This is the
recommended way of installing `poetry`.
### osx / linux / bashonwindows install instructions
```bash
curl -sSL https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py | python
curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python
```
### windows powershell install instructions
```powershell
(Invoke-WebRequest -Uri https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py -UseBasicParsing).Content | python
```
!!! note
......@@ -141,6 +146,10 @@ poetry self update 0.8.0
The `self update` command will only work if you used the recommended
installer to install Poetry.
!!!note
If you are still on poetry version < 1.0 use `poetry self:update` instead.
## Enable tab completion for Bash, Fish, or Zsh
......
......@@ -2,11 +2,6 @@
This chapter will tell you how to make your library installable through Poetry.
## Every project is a package
As soon as you have a `pyproject.toml` in a directory, that directory is a package.
However, to make it accessible to others you will need to package and publish it.
## Versioning
......@@ -14,7 +9,7 @@ While Poetry does not enforce any convention regarding package versioning,
it **strongly** recommends to follow [semantic versioning](https://semver.org).
This has many advantages for the end users and allows them to set appropriate
[version constraints](/docs/versions/).
[version constraints](/docs/dependency-specification/#version-constraints).
## Lock file
......
# Managing environments
Poetry makes project environment isolation one of its core feature.
What this means is that it will always work isolated from your global Python installation.
To achieve this, it will first check if it's currently running inside a virtual environment.
If it is, it will use it directly without creating a new one. But if it's not, it will use
one that it has already created or create a brand new one for you.
By default, Poetry will try to use the currently activated Python version
to create the virtual environment for the current project.
However, for various reasons, this Python version might not be compatible
with the `python` requirement of the project. In this case, Poetry will try
to find one that is and use it. If it's unable to do so then you will be prompted
to activate one explicitly, see [Switching environments](#switching-between-environments).
!!!note
To easily switch between Python versions, it is recommended to
use [pyenv](https://github.com/pyenv/pyenv) or similar tools.
For instance, if your project is Python 2.7 only, a standard workflow
would be:
```bash
pyenv install 2.7.15
pyenv local 2.7.15 # Activate Python 2.7 for the current project
poetry install
```
## Switching between environments
Sometimes this might not be feasible for your system, especially Windows where `pyenv`
is not available, or you simply prefer to have a more explicit control over your environment.
For this specific purpose, you can use the `env use` command to tell Poetry
which Python version to use for the current project.
```bash
poetry env use /full/path/to/python
```
If you have the python executable in your `PATH` you can use it:
```bash
poetry env use python3.7
```
You can even just use the minor Python version in this case:
```bash
poetry env use 3.7
```
If you want to disable the explicitly activated virtual environment, you can use the
special `system` Python version to retrieve the default behavior:
```bash
poetry env use system
```
## Displaying the environment information
If you want to get basic information about the currently activated virtual environment,
you can use the `env info` command:
```bash
poetry env info
```
will output something similar to this:
```text
Virtual environment
Python: 3.7.1
Implementation: CPython
Path: /path/to/poetry/cache/virtualenvs/test-O3eWbxRl-py3.7
Valid: True
System
Platform: darwin
OS: posix
Python: /path/to/main/python
```
If you only want to know the path to the virtual environment, you can pass the `--path` option
to `env info`:
```bash
poetry env info --path
```
## Listing the environments associated with the project
You can also list all the virtual environments associated with the current virtual environment
with the `env list` command:
```bash
poetry env list
```
will output something like the following:
```text
test-O3eWbxRl-py2.7
test-O3eWbxRl-py3.6
test-O3eWbxRl-py3.7 (Activated)
```
## Deleting the environments
Finally, you can delete existing virtual environments by using `env remove`:
```bash
poetry env remove /full/path/to/python
poetry env remove python3.7
poetry env remove 3.7
poetry env remove test-O3eWbxRl-py3.7
```
If your remove the currently activated virtual environment, it will be automatically deactivated.
......@@ -27,14 +27,14 @@ The recommended notation for the most common licenses is (alphabetical):
* BSD-2-Clause
* BSD-3-Clause
* BSD-4-Clause
* GPL-2.0
* GPL-2.0+
* GPL-3.0
* GPL-3.0+
* LGPL-2.1
* LGPL-2.1+
* LGPL-3.0
* LGPL-3.0+
* GPL-2.0-only
* GPL-2.0-or-later
* GPL-3.0-only
* GPL-3.0-or-later
* LGPL-2.1-only
* LGPL-2.1-or-later
* LGPL-3.0-only
* LGPL-3.0-or-later
* MIT
Optional, but it is highly recommended to supply this.
......@@ -264,7 +264,7 @@ any custom url in the `urls` section.
```toml
[tool.poetry.urls]
"Bug Tracker" = "https://github.com/sdispater/poetry/issues"
"Bug Tracker" = "https://github.com/python-poetry/poetry/issues"
```
If you publish you package on PyPI, they will appear in the `Project Links` section.
......
......@@ -71,7 +71,7 @@ export POETRY_HTTP_BASIC_PYPI_USERNAME=username
export POETRY_HTTP_BASIC_PYPI_PASSWORD=password
```
See [Using environment variables](/configuration#using-environment-variables) for more information
See [Using environment variables](/docs/configuration/#using-environment-variables) for more information
on how to configure Poetry with environment variables.
#### Custom certificate authority and mutual TLS authentication
......
......@@ -14,7 +14,8 @@ nav:
- Commands: cli.md
- Configuration: configuration.md
- Repositories: repositories.md
- Versions: versions.md
- Managing environments: managing-environments.md
- Dependency specification: dependency-specification.md
- The pyproject.toml file: pyproject.md
- Contributing: contributing.md
- FAQ: faq.md
......@@ -25,3 +26,5 @@ markdown_extensions:
- pymdownx.superfences
- toc:
permalink:
- markdown_include.include:
base_path: docs
"""
This script will install poetry and its dependencies
This script will install Poetry and its dependencies
in isolation from the rest of the system.
It does, in order:
......@@ -65,6 +65,7 @@ try:
except NameError:
u = str
SHELL = os.getenv("SHELL", "")
WINDOWS = sys.platform.startswith("win") or (sys.platform == "cli" and os.name == "nt")
......@@ -226,8 +227,7 @@ It will add the `poetry` command to {poetry}'s bin directory, located at:
{platform_msg}
You can uninstall at any time with `poetry self:uninstall`,
or by executing this script with the --uninstall option,
You can uninstall at any time by executing this script with the --uninstall option,
and these changes will be reverted.
"""
......@@ -249,6 +249,9 @@ modifying the profile file{plural} located at:
{rcfiles}"""
PRE_MESSAGE_FISH = """This path will then be added to your `PATH` environment variable by
modifying the `fish_user_paths` universal variable."""
PRE_MESSAGE_WINDOWS = """This path will then be added to your `PATH` environment variable by
modifying the `HKEY_CURRENT_USER/Environment/PATH` registry key."""
......@@ -264,6 +267,12 @@ automatically.
To configure your current shell run `source {poetry_home_env}`
"""
POST_MESSAGE_FISH = """{poetry} ({version}) is installed now. Great!
{poetry}'s bin directory ({poetry_home_bin}) has been added to your `PATH`
environment variable by modifying the `fish_user_paths` universal variable.
"""
POST_MESSAGE_WINDOWS = """{poetry} ({version}) is installed now. Great!
To get started you need Poetry's bin directory ({poetry_home_bin}) in your `PATH`
......@@ -279,6 +288,15 @@ environment variable.
To configure your current shell run `source {poetry_home_env}`
"""
POST_MESSAGE_FISH_NO_MODIFY_PATH = """{poetry} ({version}) is installed now. Great!
To get started you need {poetry}'s bin directory ({poetry_home_bin})
in your `PATH` environment variable, which you can add by running
the following command:
set -U fish_user_paths {poetry_home_bin} $fish_user_paths
"""
POST_MESSAGE_WINDOWS_NO_MODIFY_PATH = """{poetry} ({version}) is installed now. Great!
To get started you need Poetry's bin directory ({poetry_home_bin}) in your `PATH`
......@@ -605,6 +623,9 @@ class Installer:
if not self._modify_path:
return
if "fish" in SHELL:
return self.add_to_fish_path()
if WINDOWS:
return self.add_to_windows_path()
......@@ -625,6 +646,40 @@ class Installer:
with open(profile, "a") as f:
f.write(u(addition))
def add_to_fish_path(self):
"""
Ensure POETRY_BIN directory is on Fish shell $PATH
"""
current_path = os.environ.get("PATH", None)
if current_path is None:
print(
colorize(
"warning",
"\nUnable to get the PATH value. It will not be updated automatically.",
)
)
self._modify_path = False
return
if POETRY_BIN not in current_path:
fish_user_paths = subprocess.check_output(
["fish", "-c", "echo $fish_user_paths"]
).decode("utf-8")
if POETRY_BIN not in fish_user_paths:
cmd = "set -U fish_user_paths {} $fish_user_paths".format(POETRY_BIN)
set_fish_user_path = ["fish", "-c", "{}".format(cmd)]
subprocess.check_output(set_fish_user_path)
else:
print(
colorize(
"warning",
"\nPATH already contains {} and thus was not modified.".format(
POETRY_BIN
),
)
)
def add_to_windows_path(self):
try:
old_path = self.get_windows_path_var()
......@@ -685,11 +740,25 @@ class Installer:
)
def remove_from_path(self):
if WINDOWS:
if "fish" in SHELL:
return self.remove_from_fish_path()
elif WINDOWS:
return self.remove_from_windows_path()
return self.remove_from_unix_path()
def remove_from_fish_path(self):
fish_user_paths = subprocess.check_output(
["fish", "-c", "echo $fish_user_paths"]
).decode("utf-8")
if POETRY_BIN in fish_user_paths:
cmd = "set -U fish_user_paths (string match -v {} $fish_user_paths)".format(
POETRY_BIN
)
set_fish_user_path = ["fish", "-c", "{}".format(cmd)]
subprocess.check_output(set_fish_user_path)
def remove_from_windows_path(self):
path = self.get_windows_path_var()
......@@ -741,8 +810,7 @@ class Installer:
def get_unix_profiles(self):
profiles = [os.path.join(HOME, ".profile")]
shell = os.getenv("SHELL", "")
if "zsh" in shell:
if "zsh" in SHELL:
zdotdir = os.getenv("ZDOTDIR", HOME)
profiles.append(os.path.join(zdotdir, ".zprofile"))
......@@ -766,7 +834,9 @@ class Installer:
if not self._modify_path:
kwargs["platform_msg"] = PRE_MESSAGE_NO_MODIFY_PATH
else:
if WINDOWS:
if "fish" in SHELL:
kwargs["platform_msg"] = PRE_MESSAGE_FISH
elif WINDOWS:
kwargs["platform_msg"] = PRE_MESSAGE_WINDOWS
else:
profiles = [
......@@ -809,6 +879,12 @@ class Installer:
poetry_home_bin = POETRY_BIN.replace(
os.getenv("USERPROFILE", ""), "%USERPROFILE%"
)
elif "fish" in SHELL:
message = POST_MESSAGE_FISH
if not self._modify_path:
message = POST_MESSAGE_FISH_NO_MODIFY_PATH
poetry_home_bin = POETRY_BIN.replace(os.getenv("HOME", ""), "$HOME")
else:
message = POST_MESSAGE_UNIX
if not self._modify_path:
......
......@@ -3,8 +3,8 @@ PYTHON_VERSIONS="cp27-cp27m cp34-cp34m cp35-cp35m cp36-cp36m cp37-cp37m cp38-cp3
cd /io
/opt/python/cp37-cp37m/bin/pip install pip -U
/opt/python/cp37-cp37m/bin/pip install poetry -U
/opt/python/cp37-cp37m/bin/poetry config settings.virtualenvs.create false
/opt/python/cp37-cp37m/bin/pip install poetry -U --pre
/opt/python/cp37-cp37m/bin/poetry config virtualenvs.create false
/opt/python/cp37-cp37m/bin/poetry install --no-dev
/opt/python/cp37-cp37m/bin/python sonnet make release --ansi \
-P "2.7:/opt/python/cp27-cp27m/bin/python" \
......
......@@ -67,10 +67,10 @@ description = "httplib2 caching for requests"
name = "cachecontrol"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "0.12.5"
version = "0.12.6"
[package.dependencies]
msgpack = "*"
msgpack = ">=0.5.2"
requests = "*"
[package.dependencies.lockfile]
......@@ -100,7 +100,7 @@ description = "Python package for providing Mozilla's CA Bundle."
name = "certifi"
optional = false
python-versions = "*"
version = "2019.9.11"
version = "2019.11.28"
[[package]]
category = "main"
......@@ -109,7 +109,7 @@ marker = "python_version >= \"2.7\" and python_version < \"2.8\" and (sys_platfo
name = "cffi"
optional = false
python-versions = "*"
version = "1.13.1"
version = "1.13.2"
[package.dependencies]
pycparser = "*"
......@@ -159,7 +159,7 @@ description = "CliKit is a group of utilities to build beautiful and testable co
name = "clikit"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "0.4.0"
version = "0.4.1"
[package.dependencies]
pastel = ">=0.1.0,<0.2.0"
......@@ -176,16 +176,25 @@ version = ">=3.6,<4.0"
[[package]]
category = "dev"
description = "Cross-platform colored terminal text."
marker = "sys_platform == \"win32\""
marker = "sys_platform == \"win32\" and python_version == \"3.4\""
name = "colorama"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "0.4.1"
[[package]]
category = "dev"
description = "Cross-platform colored terminal text."
marker = "sys_platform == \"win32\" and python_version != \"3.4\" or platform_system == \"Windows\""
name = "colorama"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "0.4.3"
[[package]]
category = "main"
description = "Updated configparser from Python 3.7 for Python 2.6+."
marker = "python_version == \"2.7\" and python_version < \"2.8\" or python_version < \"3\""
marker = "python_version == \"2.7\" and python_version == \"2.7\" or python_version < \"3\""
name = "configparser"
optional = false
python-versions = ">=2.6"
......@@ -213,6 +222,17 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, <4"
version = "4.5.4"
[[package]]
category = "dev"
description = "Code coverage measurement for Python"
name = "coverage"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
version = "5.0.2"
[package.extras]
toml = ["toml"]
[[package]]
category = "main"
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
marker = "python_version >= \"2.7\" and python_version < \"2.8\" and (sys_platform == \"linux2\" or sys_platform == \"linux\") or python_version >= \"3.4\" and python_version < \"3.5\" and (sys_platform == \"linux2\" or sys_platform == \"linux\") or python_version >= \"3.5\" and python_version < \"4.0\" and sys_platform == \"linux\""
......@@ -243,7 +263,7 @@ test = ["pytest (>=3.6.0,<3.9.0 || >3.9.0,<3.9.1 || >3.9.1,<3.9.2 || >3.9.2)", "
[[package]]
category = "main"
description = "Discover and load entry points from installed packages."
marker = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version >= \"3.4\" and python_version < \"3.5\" or python_version >= \"3.5\" and python_version < \"4.0\""
marker = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version >= \"3.4\" and python_version < \"3.5\""
name = "entrypoints"
optional = false
python-versions = ">=2.7"
......@@ -342,8 +362,8 @@ category = "dev"
description = "File identification library for Python"
name = "identify"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "1.4.7"
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
version = "1.4.9"
[package.extras]
license = ["editdistance"]
......@@ -361,8 +381,8 @@ category = "main"
description = "Read metadata from Python packages"
name = "importlib-metadata"
optional = false
python-versions = ">=2.7,!=3.0,!=3.1,!=3.2,!=3.3"
version = "0.23"
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
version = "1.1.3"
[package.dependencies]
zipp = ">=0.5"
......@@ -413,7 +433,7 @@ marker = "python_version >= \"3.5\" and python_version < \"4.0\" and sys_platfor
name = "jeepney"
optional = false
python-versions = ">=3.5"
version = "0.4.1"
version = "0.4.2"
[package.extras]
dev = ["testpath"]
......@@ -439,11 +459,10 @@ description = "An implementation of JSON Schema validation for Python"
name = "jsonschema"
optional = false
python-versions = "*"
version = "3.1.1"
version = "3.2.0"
[package.dependencies]
attrs = ">=17.4.0"
importlib-metadata = "*"
pyrsistent = ">=0.14.0"
setuptools = "*"
six = ">=1.11.0"
......@@ -452,8 +471,13 @@ six = ">=1.11.0"
python = "<3"
version = "*"
[package.dependencies.importlib-metadata]
python = "<3.8"
version = "*"
[package.extras]
format = ["idna", "jsonpointer (>1.13)", "rfc3987", "strict-rfc3339", "webcolors"]
format_nongpl = ["idna", "jsonpointer (>1.13)", "webcolors", "rfc3986-validator (>0.1.0)", "rfc3339-validator"]
[[package]]
category = "main"
......@@ -483,16 +507,19 @@ marker = "python_version >= \"3.5\" and python_version < \"4.0\""
name = "keyring"
optional = false
python-versions = ">=3.5"
version = "19.2.0"
version = "20.0.1"
[package.dependencies]
entrypoints = "*"
pywin32-ctypes = "<0.1.0 || >0.1.0,<0.1.1 || >0.1.1"
secretstorage = "*"
[package.dependencies.importlib-metadata]
python = "<3.8"
version = "*"
[package.extras]
docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs", "pytest-flake8", "pytest-black-multipy"]
testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-black-multipy", "pytest-cov"]
[[package]]
category = "dev"
......@@ -510,7 +537,6 @@ tornado = "*"
[[package]]
category = "main"
description = "Platform-independent file locking module"
marker = "extra == \"filecache\""
name = "lockfile"
optional = false
python-versions = "*"
......@@ -540,6 +566,17 @@ testing = ["coverage", "pyyaml"]
[[package]]
category = "dev"
description = "This is an extension to Python-Markdown which provides an \"include\" function, similar to that found in LaTeX (and also the C pre-processor and Fortran). I originally wrote it for my FORD Fortran auto-documentation generator."
name = "markdown-include"
optional = false
python-versions = "*"
version = "0.5.1"
[package.dependencies]
markdown = "*"
[[package]]
category = "dev"
description = "Safely add untrusted strings to HTML/XML markup."
marker = "python_version >= \"2.7.9\" and python_version < \"2.8.0\" or python_version >= \"3.4\" and python_version < \"4.0\""
name = "markupsafe"
......@@ -588,6 +625,7 @@ test = ["pytest", "pytest-cov"]
[[package]]
category = "main"
description = "More routines for operating on iterables, beyond itertools"
marker = "python_version < \"3.8\""
name = "more-itertools"
optional = false
python-versions = "*"
......@@ -599,6 +637,7 @@ six = ">=1.0.0,<2.0.0"
[[package]]
category = "main"
description = "More routines for operating on iterables, beyond itertools"
marker = "python_version < \"3.8\" or python_version > \"2.7\""
name = "more-itertools"
optional = false
python-versions = ">=3.4"
......@@ -606,6 +645,15 @@ version = "7.2.0"
[[package]]
category = "main"
description = "More routines for operating on iterables, beyond itertools"
marker = "python_version >= \"3.5\" and python_version < \"3.8\" or python_version < \"3.8\" or python_version > \"2.7\""
name = "more-itertools"
optional = false
python-versions = ">=3.5"
version = "8.0.2"
[[package]]
category = "main"
description = "MessagePack (de)serializer."
name = "msgpack"
optional = false
......@@ -618,7 +666,7 @@ description = "Node.js virtual environment builder"
name = "nodeenv"
optional = false
python-versions = "*"
version = "1.3.3"
version = "1.3.4"
[[package]]
category = "dev"
......@@ -626,7 +674,7 @@ description = "Core utilities for Python packages"
name = "packaging"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "19.2"
version = "20.0"
[package.dependencies]
pyparsing = ">=2.0.2"
......@@ -662,8 +710,8 @@ description = "Utility library for gitignore style pattern matching of file path
marker = "python_version >= \"3.6\" and python_version < \"4.0\""
name = "pathspec"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "0.6.0"
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "0.7.0"
[[package]]
category = "dev"
......@@ -701,7 +749,7 @@ description = "plugin and hook calling mechanisms for python"
name = "pluggy"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "0.13.0"
version = "0.13.1"
[package.dependencies]
[package.dependencies.importlib-metadata]
......@@ -739,8 +787,8 @@ category = "dev"
description = "A framework for managing and maintaining multi-language pre-commit hooks."
name = "pre-commit"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "1.20.0"
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
version = "1.21.0"
[package.dependencies]
"aspy.yaml" = "*"
......@@ -778,7 +826,7 @@ description = "library with cross-python path, ini-parsing, io, code, log facili
name = "py"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "1.8.0"
version = "1.8.1"
[[package]]
category = "main"
......@@ -803,7 +851,7 @@ description = "Pygments is a syntax highlighting package written in Python."
name = "pygments"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "2.4.2"
version = "2.5.2"
[[package]]
category = "dev"
......@@ -841,7 +889,7 @@ description = "Extension pack for Python Markdown."
name = "pymdown-extensions"
optional = false
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*"
version = "6.1"
version = "6.2.1"
[package.dependencies]
Markdown = ">=3.0.1"
......@@ -853,7 +901,7 @@ description = "Python parsing module"
name = "pyparsing"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
version = "2.4.2"
version = "2.4.6"
[[package]]
category = "main"
......@@ -872,18 +920,25 @@ description = "pytest: simple powerful testing with Python"
name = "pytest"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
version = "4.6.6"
version = "4.6.9"
[package.dependencies]
atomicwrites = ">=1.0"
attrs = ">=17.4.0"
colorama = "*"
packaging = "*"
pluggy = ">=0.12,<1.0"
py = ">=1.5.0"
six = ">=1.10.0"
wcwidth = "*"
[[package.dependencies.colorama]]
python = "<3.4.0 || >=3.5.0"
version = "*"
[[package.dependencies.colorama]]
python = ">=3.4,<3.5"
version = "<=0.4.1"
[[package.dependencies.more-itertools]]
python = "<2.8"
version = ">=4.0.0,<6.0.0"
......@@ -928,7 +983,7 @@ description = "Thin-wrapper around the mock package for easier use with py.test"
name = "pytest-mock"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "1.11.2"
version = "1.13.0"
[package.dependencies]
pytest = ">=2.7"
......@@ -968,7 +1023,15 @@ description = "YAML parser and emitter for Python"
name = "pyyaml"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "5.1.2"
version = "5.2"
[[package]]
category = "dev"
description = "YAML parser and emitter for Python"
name = "pyyaml"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "5.3"
[[package]]
category = "dev"
......@@ -977,7 +1040,7 @@ marker = "python_version >= \"3.6\" and python_version < \"4.0\""
name = "regex"
optional = false
python-versions = "*"
version = "2019.08.19"
version = "2020.1.8"
[[package]]
category = "main"
......@@ -1029,7 +1092,7 @@ requests = ">=2.0.1,<3.0.0"
[[package]]
category = "main"
description = "scandir, a better directory iterator and faster os.walk()"
marker = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version >= \"3.4\" and python_version < \"3.5\" or python_version < \"3.5\""
marker = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version >= \"3.4\" and python_version < \"3.5\""
name = "scandir"
optional = false
python-versions = "*"
......@@ -1057,11 +1120,11 @@ marker = "python_version >= \"3.5\" and python_version < \"4.0\" and sys_platfor
name = "secretstorage"
optional = false
python-versions = ">=3.5"
version = "3.1.1"
version = "3.1.2"
[package.dependencies]
cryptography = "*"
jeepney = "*"
jeepney = ">=0.4.2"
[[package]]
category = "main"
......@@ -1077,7 +1140,7 @@ description = "Python 2 and 3 compatibility utilities"
name = "six"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*"
version = "1.12.0"
version = "1.13.0"
[[package]]
category = "main"
......@@ -1149,24 +1212,46 @@ description = "tox is a generic virtualenv management and test command line tool
name = "tox"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "3.14.0"
version = "3.12.1"
[package.dependencies]
filelock = ">=3.0.0,<4"
pluggy = ">=0.3.0,<1"
py = ">=1.4.17,<2"
setuptools = ">=30.0.0"
six = ">=1.0.0,<2"
toml = ">=0.9.4"
virtualenv = ">=14.0.0"
[package.extras]
docs = ["sphinx (>=2.0.0,<3)", "towncrier (>=18.5.0)", "pygments-github-lexers (>=0.0.5)", "sphinxcontrib-autoprogram (>=0.1.5)"]
testing = ["freezegun (>=0.3.11,<1)", "pathlib2 (>=2.3.3,<3)", "pytest (>=3.0.0,<5)", "pytest-cov (>=2.5.1,<3)", "pytest-mock (>=1.10.0,<2)", "pytest-xdist (>=1.22.2,<2)", "pytest-randomly (>=1.2.3,<2)", "flaky (>=3.4.0,<4)", "psutil (>=5.6.1,<6)"]
[[package]]
category = "dev"
description = "tox is a generic virtualenv management and test command line tool"
name = "tox"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
version = "3.14.3"
[package.dependencies]
colorama = ">=0.4.1"
filelock = ">=3.0.0,<4"
packaging = ">=14"
pluggy = ">=0.12.0,<1"
py = ">=1.4.17,<2"
six = ">=1.0.0,<2"
toml = ">=0.9.4"
virtualenv = ">=14.0.0"
virtualenv = ">=16.0.0"
[package.dependencies.importlib-metadata]
python = "<3.8"
version = ">=0.12,<1"
version = ">=0.12,<2"
[package.extras]
docs = ["sphinx (>=2.0.0,<3)", "towncrier (>=18.5.0)", "pygments-github-lexers (>=0.0.5)", "sphinxcontrib-autoprogram (>=0.1.5)"]
testing = ["freezegun (>=0.3.11,<1)", "pathlib2 (>=2.3.3,<3)", "pytest (>=4.0.0,<6)", "pytest-cov (>=2.5.1,<3)", "pytest-mock (>=1.10.0,<2)", "pytest-xdist (>=1.22.2,<2)", "pytest-randomly (>=1.2.3,<2)", "flaky (>=3.4.0,<4)", "psutil (>=5.6.1,<6)"]
testing = ["freezegun (>=0.3.11,<1)", "pathlib2 (>=2.3.3,<3)", "pytest (>=4.0.0,<6)", "pytest-cov (>=2.5.1,<3)", "pytest-mock (>=1.10.0,<2)", "pytest-xdist (>=1.22.2,<2)", "pytest-randomly (>=1.0.0,<4)", "flaky (>=3.4.0,<4)", "psutil (>=5.6.1,<6)"]
[[package]]
category = "dev"
......@@ -1204,7 +1289,7 @@ description = "HTTP library with thread-safe connection pooling, file post, and
name = "urllib3"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4"
version = "1.25.6"
version = "1.25.7"
[package.extras]
brotli = ["brotlipy (>=0.6.0)"]
......@@ -1217,7 +1302,7 @@ description = "Virtual Python Environment builder"
name = "virtualenv"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
version = "16.7.7"
version = "16.7.9"
[package.extras]
docs = ["sphinx (>=1.8.0,<2)", "towncrier (>=18.5.0)", "sphinx-rtd-theme (>=0.4.2,<1)"]
......@@ -1229,7 +1314,7 @@ description = "Measures number of Terminal column cells of wide-character codes"
name = "wcwidth"
optional = false
python-versions = "*"
version = "0.1.7"
version = "0.1.8"
[[package]]
category = "main"
......@@ -1242,6 +1327,7 @@ version = "0.5.1"
[[package]]
category = "main"
description = "Backport of pathlib-compatible object wrapper for zip files"
marker = "python_version >= \"3.5\" and python_version < \"3.8\" or python_version < \"3.8\""
name = "zipp"
optional = false
python-versions = ">=2.7"
......@@ -1255,7 +1341,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
testing = ["pathlib2", "contextlib2", "unittest2"]
[metadata]
content-hash = "0a019fe9f27e3e3fc226c506b4c9de219d7b9d021e998ce88563f7b9c945aa35"
content-hash = "e0b632d8363fdf9f70d93901ff537714611bfc31705a897f6d2fb3bc010bca0a"
python-versions = "~2.7 || ^3.4"
[metadata.files]
......@@ -1280,49 +1366,51 @@ black = [
{file = "black-19.10b0.tar.gz", hash = "sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"},
]
cachecontrol = [
{file = "CacheControl-0.12.5.tar.gz", hash = "sha256:cef77effdf51b43178f6a2d3b787e3734f98ade253fa3187f3bb7315aaa42ff7"},
{file = "CacheControl-0.12.6-py2.py3-none-any.whl", hash = "sha256:10d056fa27f8563a271b345207402a6dcce8efab7e5b377e270329c62471b10d"},
{file = "CacheControl-0.12.6.tar.gz", hash = "sha256:be9aa45477a134aee56c8fac518627e1154df063e85f67d4f83ce0ccc23688e8"},
]
cachy = [
{file = "cachy-0.3.0-py2.py3-none-any.whl", hash = "sha256:338ca09c8860e76b275aff52374330efedc4d5a5e45dc1c5b539c1ead0786fe7"},
{file = "cachy-0.3.0.tar.gz", hash = "sha256:186581f4ceb42a0bbe040c407da73c14092379b1e4c0e327fdb72ae4a9b269b1"},
]
certifi = [
{file = "certifi-2019.9.11-py2.py3-none-any.whl", hash = "sha256:fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef"},
{file = "certifi-2019.9.11.tar.gz", hash = "sha256:e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50"},
{file = "certifi-2019.11.28-py2.py3-none-any.whl", hash = "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3"},
{file = "certifi-2019.11.28.tar.gz", hash = "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f"},
]
cffi = [
{file = "cffi-1.13.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:9009e917d8f5ef780c2626e29b6bc126f4cb2a4d43ca67aa2b40f2a5d6385e78"},
{file = "cffi-1.13.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:825ecffd9574557590e3225560a8a9d751f6ffe4a49e3c40918c9969b93395fa"},
{file = "cffi-1.13.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:193697c2918ecdb3865acf6557cddf5076bb39f1f654975e087b67efdff83365"},
{file = "cffi-1.13.1-cp27-cp27m-win32.whl", hash = "sha256:1ae14b542bf3b35e5229439c35653d2ef7d8316c1fffb980f9b7647e544baa98"},
{file = "cffi-1.13.1-cp27-cp27m-win_amd64.whl", hash = "sha256:0ea23c9c0cdd6778146a50d867d6405693ac3b80a68829966c98dd5e1bbae400"},
{file = "cffi-1.13.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ec2fa3ee81707a5232bf2dfbd6623fdb278e070d596effc7e2d788f2ada71a05"},
{file = "cffi-1.13.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:7cfcfda59ef1f95b9f729c56fe8a4041899f96b72685d36ef16a3440a0f85da8"},
{file = "cffi-1.13.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:6fd58366747debfa5e6163ada468a90788411f10c92597d3b0a912d07e580c36"},
{file = "cffi-1.13.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:9c77564a51d4d914ed5af096cd9843d90c45b784b511723bd46a8a9d09cf16fc"},
{file = "cffi-1.13.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:728ec653964655d65408949b07f9b2219df78badd601d6c49e28d604efe40599"},
{file = "cffi-1.13.1-cp34-cp34m-win32.whl", hash = "sha256:b8f09f21544b9899defb09afbdaeb200e6a87a2b8e604892940044cf94444644"},
{file = "cffi-1.13.1-cp34-cp34m-win_amd64.whl", hash = "sha256:8a2bcae2258d00fcfc96a9bde4a6177bc4274fe033f79311c5dd3d3148c26518"},
{file = "cffi-1.13.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:a19089fa74ed19c4fe96502a291cfdb89223a9705b1d73b3005df4256976142e"},
{file = "cffi-1.13.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:e22a00c0c81ffcecaf07c2bfb3672fa372c50e2bd1024ffee0da191c1b27fc71"},
{file = "cffi-1.13.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:bb75ba21d5716abc41af16eac1145ab2e471deedde1f22c6f99bd9f995504df0"},
{file = "cffi-1.13.1-cp35-cp35m-win32.whl", hash = "sha256:364f8404034ae1b232335d8c7f7b57deac566f148f7222cef78cf8ae28ef764e"},
{file = "cffi-1.13.1-cp35-cp35m-win_amd64.whl", hash = "sha256:fd82eb4694be712fcae03c717ca2e0fc720657ac226b80bbb597e971fc6928c2"},
{file = "cffi-1.13.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:5ba86e1d80d458b338bda676fd9f9d68cb4e7a03819632969cf6d46b01a26730"},
{file = "cffi-1.13.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:63424daa6955e6b4c70dc2755897f5be1d719eabe71b2625948b222775ed5c43"},
{file = "cffi-1.13.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:33142ae9807665fa6511cfa9857132b2c3ee6ddffb012b3f0933fc11e1e830d5"},
{file = "cffi-1.13.1-cp36-cp36m-win32.whl", hash = "sha256:e55b5a746fb77f10c83e8af081979351722f6ea48facea79d470b3731c7b2891"},
{file = "cffi-1.13.1-cp36-cp36m-win_amd64.whl", hash = "sha256:47368f69fe6529f8f49a5d146ddee713fc9057e31d61e8b6dc86a6a5e38cecc1"},
{file = "cffi-1.13.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:4895640844f17bec32943995dc8c96989226974dfeb9dd121cc45d36e0d0c434"},
{file = "cffi-1.13.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:00d890313797d9fe4420506613384b43099ad7d2b905c0752dbcc3a6f14d80fa"},
{file = "cffi-1.13.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:a40ed527bffa2b7ebe07acc5a3f782da072e262ca994b4f2085100b5a444bbb2"},
{file = "cffi-1.13.1-cp37-cp37m-win32.whl", hash = "sha256:6381a7d8b1ebd0bc27c3bc85bc1bfadbb6e6f756b4d4db0aa1425c3719ba26b4"},
{file = "cffi-1.13.1-cp37-cp37m-win_amd64.whl", hash = "sha256:1e389e069450609c6ffa37f21f40cce36f9be7643bbe5051ab1de99d5a779526"},
{file = "cffi-1.13.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6381ab708158c4e1639da1f2a7679a9bbe3e5a776fc6d1fd808076f0e3145331"},
{file = "cffi-1.13.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:0cf9e550ac6c5e57b713437e2f4ac2d7fd0cd10336525a27224f5fc1ec2ee59a"},
{file = "cffi-1.13.1-cp38-cp38-win32.whl", hash = "sha256:819f8d5197c2684524637f940445c06e003c4a541f9983fd30d6deaa2a5487d8"},
{file = "cffi-1.13.1-cp38-cp38-win_amd64.whl", hash = "sha256:263242b6ace7f9cd4ea401428d2d45066b49a700852334fd55311bde36dcda14"},
{file = "cffi-1.13.1.tar.gz", hash = "sha256:558b3afef987cf4b17abd849e7bedf64ee12b28175d564d05b628a0f9355599b"},
{file = "cffi-1.13.2-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:3c9fff570f13480b201e9ab69453108f6d98244a7f495e91b6c654a47486ba43"},
{file = "cffi-1.13.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:2c5e309ec482556397cb21ede0350c5e82f0eb2621de04b2633588d118da4396"},
{file = "cffi-1.13.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:19db0cdd6e516f13329cba4903368bff9bb5a9331d3410b1b448daaadc495e54"},
{file = "cffi-1.13.2-cp27-cp27m-win32.whl", hash = "sha256:5c4fae4e9cdd18c82ba3a134be256e98dc0596af1e7285a3d2602c97dcfa5159"},
{file = "cffi-1.13.2-cp27-cp27m-win_amd64.whl", hash = "sha256:32a262e2b90ffcfdd97c7a5e24a6012a43c61f1f5a57789ad80af1d26c6acd97"},
{file = "cffi-1.13.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:4a43c91840bda5f55249413037b7a9b79c90b1184ed504883b72c4df70778579"},
{file = "cffi-1.13.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8169cf44dd8f9071b2b9248c35fc35e8677451c52f795daa2bb4643f32a540bc"},
{file = "cffi-1.13.2-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:71a608532ab3bd26223c8d841dde43f3516aa5d2bf37b50ac410bb5e99053e8f"},
{file = "cffi-1.13.2-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:7f627141a26b551bdebbc4855c1157feeef18241b4b8366ed22a5c7d672ef858"},
{file = "cffi-1.13.2-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:0b49274afc941c626b605fb59b59c3485c17dc776dc3cc7cc14aca74cc19cc42"},
{file = "cffi-1.13.2-cp34-cp34m-win32.whl", hash = "sha256:4424e42199e86b21fc4db83bd76909a6fc2a2aefb352cb5414833c030f6ed71b"},
{file = "cffi-1.13.2-cp34-cp34m-win_amd64.whl", hash = "sha256:7d4751da932caaec419d514eaa4215eaf14b612cff66398dd51129ac22680b20"},
{file = "cffi-1.13.2-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:ccb032fda0873254380aa2bfad2582aedc2959186cce61e3a17abc1a55ff89c3"},
{file = "cffi-1.13.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:dcd65317dd15bc0451f3e01c80da2216a31916bdcffd6221ca1202d96584aa25"},
{file = "cffi-1.13.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:135f69aecbf4517d5b3d6429207b2dff49c876be724ac0c8bf8e1ea99df3d7e5"},
{file = "cffi-1.13.2-cp35-cp35m-win32.whl", hash = "sha256:7b93a885bb13073afb0aa73ad82059a4c41f4b7d8eb8368980448b52d4c7dc2c"},
{file = "cffi-1.13.2-cp35-cp35m-win_amd64.whl", hash = "sha256:e570d3ab32e2c2861c4ebe6ffcad6a8abf9347432a37608fe1fbd157b3f0036b"},
{file = "cffi-1.13.2-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:0e3ea92942cb1168e38c05c1d56b0527ce31f1a370f6117f1d490b8dcd6b3a04"},
{file = "cffi-1.13.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5ecfa867dea6fabe2a58f03ac9186ea64da1386af2159196da51c4904e11d652"},
{file = "cffi-1.13.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:291f7c42e21d72144bb1c1b2e825ec60f46d0a7468f5346841860454c7aa8f57"},
{file = "cffi-1.13.2-cp36-cp36m-win32.whl", hash = "sha256:62f2578358d3a92e4ab2d830cd1c2049c9c0d0e6d3c58322993cc341bdeac22e"},
{file = "cffi-1.13.2-cp36-cp36m-win_amd64.whl", hash = "sha256:fd43a88e045cf992ed09fa724b5315b790525f2676883a6ea64e3263bae6549d"},
{file = "cffi-1.13.2-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:d75c461e20e29afc0aee7172a0950157c704ff0dd51613506bd7d82b718e7410"},
{file = "cffi-1.13.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:aa00d66c0fab27373ae44ae26a66a9e43ff2a678bf63a9c7c1a9a4d61172827a"},
{file = "cffi-1.13.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:2e9c80a8c3344a92cb04661115898a9129c074f7ab82011ef4b612f645939f12"},
{file = "cffi-1.13.2-cp37-cp37m-win32.whl", hash = "sha256:d754f39e0d1603b5b24a7f8484b22d2904fa551fe865fd0d4c3332f078d20d4e"},
{file = "cffi-1.13.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6471a82d5abea994e38d2c2abc77164b4f7fbaaf80261cb98394d5793f11b12a"},
{file = "cffi-1.13.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:74a1d8c85fb6ff0b30fbfa8ad0ac23cd601a138f7509dc617ebc65ef305bb98d"},
{file = "cffi-1.13.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:42194f54c11abc8583417a7cf4eaff544ce0de8187abaf5d29029c91b1725ad3"},
{file = "cffi-1.13.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:415bdc7ca8c1c634a6d7163d43fb0ea885a07e9618a64bda407e04b04333b7db"},
{file = "cffi-1.13.2-cp38-cp38-win32.whl", hash = "sha256:6d4f18483d040e18546108eb13b1dfa1000a089bcf8529e30346116ea6240506"},
{file = "cffi-1.13.2-cp38-cp38-win_amd64.whl", hash = "sha256:2781e9ad0e9d47173c0093321bb5435a9dfae0ed6a762aabafa13108f5f7b2ba"},
{file = "cffi-1.13.2.tar.gz", hash = "sha256:599a1e8ff057ac530c9ad1778293c665cb81a791421f46922d80a86473c13346"},
]
cfgv = [
{file = "cfgv-2.0.1-py2.py3-none-any.whl", hash = "sha256:fbd93c9ab0a523bf7daec408f3be2ed99a980e20b2d19b50fc184ca6b820d289"},
......@@ -1341,12 +1429,14 @@ click = [
{file = "Click-7.0.tar.gz", hash = "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"},
]
clikit = [
{file = "clikit-0.4.0-py2.py3-none-any.whl", hash = "sha256:9f07b56216bc1068f8ed441a5e67a3881fe503cfd6a3a7874f59f72be417cdda"},
{file = "clikit-0.4.0.tar.gz", hash = "sha256:6819a5b2a78523be485ea9e1ef380ae8d02da4bffed540f7881fdaafbff41390"},
{file = "clikit-0.4.1-py2.py3-none-any.whl", hash = "sha256:80b0bfee42310a715773dded69590c4c33fa9fc9a351fa7c262cb67f21d0758f"},
{file = "clikit-0.4.1.tar.gz", hash = "sha256:8ae4766b974d7b1983e39d501da9a0aadf118a907a0c9b50714d027c8b59ea81"},
]
colorama = [
{file = "colorama-0.4.1-py2.py3-none-any.whl", hash = "sha256:f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"},
{file = "colorama-0.4.1.tar.gz", hash = "sha256:05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d"},
{file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"},
{file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"},
]
configparser = [
{file = "configparser-4.0.2-py2.py3-none-any.whl", hash = "sha256:254c1d9c79f60c45dfde850850883d5aaa7f19a23f13561243a050d5a7c3fe4c"},
......@@ -1389,6 +1479,37 @@ coverage = [
{file = "coverage-4.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:23cc09ed395b03424d1ae30dcc292615c1372bfba7141eb85e11e50efaa6b351"},
{file = "coverage-4.5.4-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:141f08ed3c4b1847015e2cd62ec06d35e67a3ac185c26f7635f4406b90afa9c5"},
{file = "coverage-4.5.4.tar.gz", hash = "sha256:e07d9f1a23e9e93ab5c62902833bf3e4b1f65502927379148b6622686223125c"},
{file = "coverage-5.0.2-cp27-cp27m-macosx_10_12_x86_64.whl", hash = "sha256:511ec0c00840e12fb4e852e4db58fa6a01ca4da72f36a9766fae344c3d502033"},
{file = "coverage-5.0.2-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:d22b4297e7e4225ccf01f1aa55e7a96412ea0796b532dd614c3fcbafa341128e"},
{file = "coverage-5.0.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:593853aa1ac6dcc6405324d877544c596c9d948ef20d2e9512a0f5d2d3202356"},
{file = "coverage-5.0.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:e65a5aa1670db6263f19fdc03daee1d7dbbadb5cb67fd0a1f16033659db13c1d"},
{file = "coverage-5.0.2-cp27-cp27m-win32.whl", hash = "sha256:d4a2b578a7a70e0c71f662705262f87a456f1e6c1e40ada7ea699abaf070a76d"},
{file = "coverage-5.0.2-cp27-cp27m-win_amd64.whl", hash = "sha256:28f7f73b34a05e23758e860a89a7f649b85c6749e252eff60ebb05532d180e86"},
{file = "coverage-5.0.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7d1cc7acc9ce55179616cf72154f9e648136ea55987edf84addbcd9886ffeba2"},
{file = "coverage-5.0.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:2d0cb9b1fe6ad0d915d45ad3d87f03a38e979093a98597e755930db1f897afae"},
{file = "coverage-5.0.2-cp35-cp35m-macosx_10_12_x86_64.whl", hash = "sha256:bfe102659e2ec13b86c7f3b1db6c9a4e7beea4255058d006351339e6b342d5d2"},
{file = "coverage-5.0.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:23688ff75adfa8bfa2a67254d889f9bdf9302c27241d746e17547c42c732d3f4"},
{file = "coverage-5.0.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:1bf7ba2af1d373a1750888724f84cffdfc697738f29a353c98195f98fc011509"},
{file = "coverage-5.0.2-cp35-cp35m-win32.whl", hash = "sha256:569f9ee3025682afda6e9b0f5bb14897c0db03f1a1dc088b083dd36e743f92bb"},
{file = "coverage-5.0.2-cp35-cp35m-win_amd64.whl", hash = "sha256:cf908840896f7aa62d0ec693beb53264b154f972eb8226fb864ac38975590c4f"},
{file = "coverage-5.0.2-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:eaad65bd20955131bcdb3967a4dea66b4e4d4ca488efed7c00d91ee0173387e8"},
{file = "coverage-5.0.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:225e79a5d485bc1642cb7ba02281419c633c216cdc6b26c26494ba959f09e69f"},
{file = "coverage-5.0.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:bd82b684bb498c60ef47bb1541a50e6d006dde8579934dcbdbc61d67d1ea70d9"},
{file = "coverage-5.0.2-cp36-cp36m-win32.whl", hash = "sha256:7ca3db38a61f3655a2613ee2c190d63639215a7a736d3c64cc7bbdb002ce6310"},
{file = "coverage-5.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:47874b4711c5aeb295c31b228a758ce3d096be83dc37bd56da48ed99efb8813b"},
{file = "coverage-5.0.2-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:955ec084f549128fa2702f0b2dc696392001d986b71acd8fd47424f28289a9c3"},
{file = "coverage-5.0.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:1f4ee8e2e4243971618bc16fcc4478317405205f135e95226c2496e2a3b8dbbf"},
{file = "coverage-5.0.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f45fba420b94165c17896861bb0e8b27fb7abdcedfeb154895d8553df90b7b00"},
{file = "coverage-5.0.2-cp37-cp37m-win32.whl", hash = "sha256:cca38ded59105f7705ef6ffe1e960b8db6c7d8279c1e71654a4775ab4454ca15"},
{file = "coverage-5.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:cb2b74c123f65e8166f7e1265829a6c8ed755c3cd16d7f50e75a83456a5f3fd7"},
{file = "coverage-5.0.2-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:53e7438fef0c97bc248f88ba1edd10268cd94d5609970aaf87abbe493691af87"},
{file = "coverage-5.0.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c1e4e39e43057396a5e9d069bfbb6ffeee892e40c5d2effbd8cd71f34ee66c4d"},
{file = "coverage-5.0.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5b0a07158360d22492f9abd02a0f2ee7981b33f0646bf796598b7673f6bbab14"},
{file = "coverage-5.0.2-cp38-cp38m-win32.whl", hash = "sha256:88b51153657612aea68fa684a5b88037597925260392b7bb4509d4f9b0bdd889"},
{file = "coverage-5.0.2-cp38-cp38m-win_amd64.whl", hash = "sha256:189aac76d6e0d7af15572c51892e7326ee451c076c5a50a9d266406cd6c49708"},
{file = "coverage-5.0.2-cp39-cp39m-win32.whl", hash = "sha256:d095a7b473f8a95f7efe821f92058c8a2ecfb18f8db6677ae3819e15dc11aaae"},
{file = "coverage-5.0.2-cp39-cp39m-win_amd64.whl", hash = "sha256:ddeb42a3d5419434742bf4cc71c9eaa22df3b76808e23a82bd0b0bd360f1a9f1"},
{file = "coverage-5.0.2.tar.gz", hash = "sha256:b251c7092cbb6d789d62dc9c9e7c4fb448c9138b51285c36aeb72462cad3600e"},
]
cryptography = [
{file = "cryptography-2.8-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:fb81c17e0ebe3358486cd8cc3ad78adbae58af12fc2bf2bc0bb84e8090fa5ce8"},
......@@ -1450,16 +1571,16 @@ httpretty = [
{file = "httpretty-0.9.7.tar.gz", hash = "sha256:66216f26b9d2c52e81808f3e674a6fb65d4bf719721394a1a9be926177e55fbe"},
]
identify = [
{file = "identify-1.4.7-py2.py3-none-any.whl", hash = "sha256:4f1fe9a59df4e80fcb0213086fcf502bc1765a01ea4fe8be48da3b65afd2a017"},
{file = "identify-1.4.7.tar.gz", hash = "sha256:d8919589bd2a5f99c66302fec0ef9027b12ae150b0b0213999ad3f695fc7296e"},
{file = "identify-1.4.9-py2.py3-none-any.whl", hash = "sha256:72e9c4ed3bc713c7045b762b0d2e2115c572b85abfc1f4604f5a4fd4c6642b71"},
{file = "identify-1.4.9.tar.gz", hash = "sha256:6f44e637caa40d1b4cb37f6ed3b262ede74901d28b1cc5b1fc07360871edd65d"},
]
idna = [
{file = "idna-2.8-py2.py3-none-any.whl", hash = "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"},
{file = "idna-2.8.tar.gz", hash = "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407"},
]
importlib-metadata = [
{file = "importlib_metadata-0.23-py2.py3-none-any.whl", hash = "sha256:d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af"},
{file = "importlib_metadata-0.23.tar.gz", hash = "sha256:aa18d7378b00b40847790e7c27e11673d7fed219354109d0e7b9e5b25dc3ad26"},
{file = "importlib_metadata-1.1.3-py2.py3-none-any.whl", hash = "sha256:7c7f8ac40673f507f349bef2eed21a0e5f01ddf5b2a7356a6c65eb2099b53764"},
{file = "importlib_metadata-1.1.3.tar.gz", hash = "sha256:7a99fb4084ffe6dae374961ba7a6521b79c1d07c658ab3a28aa264ee1d1b14e3"},
]
importlib-resources = [
{file = "importlib_resources-1.0.2-py2.py3-none-any.whl", hash = "sha256:6e2783b2538bd5a14678284a3962b0660c715e5a0f10243fd5e00a4b5974f50b"},
......@@ -1470,22 +1591,22 @@ ipaddress = [
{file = "ipaddress-1.0.23.tar.gz", hash = "sha256:b7f8e0369580bb4a24d5ba1d7cc29660a4a6987763faf1d8a8046830e020e7e2"},
]
jeepney = [
{file = "jeepney-0.4.1-py3-none-any.whl", hash = "sha256:f6a3f93464a0cf052f4e87da3c8b3ed1e27696758fb9739c63d3a74d9a1b6774"},
{file = "jeepney-0.4.1.tar.gz", hash = "sha256:13806f91a96e9b2623fd2a81b950d763ee471454aafd9eb6d75dbe7afce428fb"},
{file = "jeepney-0.4.2-py3-none-any.whl", hash = "sha256:6f45dce1125cf6c58a1c88123d3831f36a789f9204fbad3172eac15f8ccd08d0"},
{file = "jeepney-0.4.2.tar.gz", hash = "sha256:0ba6d8c597e9bef1ebd18aaec595f942a264e25c1a48f164d46120eacaa2e9bb"},
]
jinja2 = [
{file = "Jinja2-2.10.3-py2.py3-none-any.whl", hash = "sha256:74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f"},
{file = "Jinja2-2.10.3.tar.gz", hash = "sha256:9fe95f19286cfefaa917656583d020be14e7859c6b0252588391e47db34527de"},
]
jsonschema = [
{file = "jsonschema-3.1.1-py2.py3-none-any.whl", hash = "sha256:94c0a13b4a0616458b42529091624e66700a17f847453e52279e35509a5b7631"},
{file = "jsonschema-3.1.1.tar.gz", hash = "sha256:2fa0684276b6333ff3c0b1b27081f4b2305f0a36cf702a23db50edb141893c3f"},
{file = "jsonschema-3.2.0-py2.py3-none-any.whl", hash = "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163"},
{file = "jsonschema-3.2.0.tar.gz", hash = "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a"},
]
keyring = [
{file = "keyring-18.0.1-py2.py3-none-any.whl", hash = "sha256:7b29ebfcf8678c4da531b2478a912eea01e80007e5ddca9ee0c7038cb3489ec6"},
{file = "keyring-18.0.1.tar.gz", hash = "sha256:67d6cc0132bd77922725fae9f18366bb314fd8f95ff4d323a4df41890a96a838"},
{file = "keyring-19.2.0-py2.py3-none-any.whl", hash = "sha256:f5bb20ea6c57c2360daf0c591931c9ea0d7660a8d9e32ca84d63273f131ea605"},
{file = "keyring-19.2.0.tar.gz", hash = "sha256:91037ccaf0c9a112a76f7740e4a416b9457a69b66c2799421581bee710a974b3"},
{file = "keyring-20.0.1-py2.py3-none-any.whl", hash = "sha256:c674f032424b4bffc62abeac5523ec49cc84aed07a480c3233e0baf618efc15c"},
{file = "keyring-20.0.1.tar.gz", hash = "sha256:963bfa7f090269d30bdc5e25589e5fd9dad2cf2a7c6f176a7f2386910e5d0d8d"},
]
livereload = [
{file = "livereload-2.6.1-py2.py3-none-any.whl", hash = "sha256:78d55f2c268a8823ba499305dcac64e28ddeb9a92571e12d543cd304faf5817b"},
......@@ -1501,6 +1622,9 @@ markdown = [
{file = "Markdown-3.1.1-py2.py3-none-any.whl", hash = "sha256:56a46ac655704b91e5b7e6326ce43d5ef72411376588afa1dd90e881b83c7e8c"},
{file = "Markdown-3.1.1.tar.gz", hash = "sha256:2e50876bcdd74517e7b71f3e7a76102050edec255b3983403f1a63e7c8a41e7a"},
]
markdown-include = [
{file = "markdown-include-0.5.1.tar.gz", hash = "sha256:72a45461b589489a088753893bc95c5fa5909936186485f4ed55caa57d10250f"},
]
markupsafe = [
{file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"},
{file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"},
......@@ -1545,6 +1669,8 @@ more-itertools = [
{file = "more_itertools-5.0.0-py3-none-any.whl", hash = "sha256:fe7a7cae1ccb57d33952113ff4fa1bc5f879963600ed74918f1236e212ee50b9"},
{file = "more-itertools-7.2.0.tar.gz", hash = "sha256:409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832"},
{file = "more_itertools-7.2.0-py3-none-any.whl", hash = "sha256:92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4"},
{file = "more-itertools-8.0.2.tar.gz", hash = "sha256:b84b238cce0d9adad5ed87e745778d20a3f8487d0f0cb8b8a586816c7496458d"},
{file = "more_itertools-8.0.2-py3-none-any.whl", hash = "sha256:c833ef592a0324bcc6a60e48440da07645063c453880c9477ceb22490aec1564"},
]
msgpack = [
{file = "msgpack-0.6.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:774f5edc3475917cd95fe593e625d23d8580f9b48b570d8853d06cac171cd170"},
......@@ -1570,11 +1696,11 @@ msgpack = [
{file = "msgpack-0.6.2.tar.gz", hash = "sha256:ea3c2f859346fcd55fc46e96885301d9c2f7a36d453f5d8f2967840efa1e1830"},
]
nodeenv = [
{file = "nodeenv-1.3.3.tar.gz", hash = "sha256:ad8259494cf1c9034539f6cced78a1da4840a4b157e23640bc4a0c0546b0cb7a"},
{file = "nodeenv-1.3.4-py2.py3-none-any.whl", hash = "sha256:561057acd4ae3809e665a9aaaf214afff110bbb6a6d5c8a96121aea6878408b3"},
]
packaging = [
{file = "packaging-19.2-py2.py3-none-any.whl", hash = "sha256:d9551545c6d761f3def1677baf08ab2a3ca17c56879e70fecba2fc4dde4ed108"},
{file = "packaging-19.2.tar.gz", hash = "sha256:28b924174df7a2fa32c1953825ff29c61e2f5e082343165438812f00d3a7fc47"},
{file = "packaging-20.0-py2.py3-none-any.whl", hash = "sha256:aec3fdbb8bc9e4bb65f0634b9f551ced63983a529d6a8931817d52fdd0816ddb"},
{file = "packaging-20.0.tar.gz", hash = "sha256:fe1d8331dfa7cc0a883b49d75fc76380b2ab2734b220fbb87d774e4fd4b851f8"},
]
pastel = [
{file = "pastel-0.1.1-py2.py3-none-any.whl", hash = "sha256:a904e1659512cc9880a028f66de77cc813a4c32f7ceb68725cbc8afad57ef7ef"},
......@@ -1585,7 +1711,8 @@ pathlib2 = [
{file = "pathlib2-2.3.5.tar.gz", hash = "sha256:6cd9a47b597b37cc57de1c05e56fb1a1c9cc9fab04fe78c29acd090418529868"},
]
pathspec = [
{file = "pathspec-0.6.0.tar.gz", hash = "sha256:e285ccc8b0785beadd4c18e5708b12bb8fcf529a1e61215b3feff1d1e559ea5c"},
{file = "pathspec-0.7.0-py2.py3-none-any.whl", hash = "sha256:163b0632d4e31cef212976cf57b43d9fd6b0bac6e67c26015d611a647d5e7424"},
{file = "pathspec-0.7.0.tar.gz", hash = "sha256:562aa70af2e0d434367d9790ad37aed893de47f1693e4201fd1d3dca15d19b96"},
]
pep562 = [
{file = "pep562-1.0-py2.py3-none-any.whl", hash = "sha256:d2a48b178ebf5f8dd31709cc26a19808ef794561fa2fe50ea01ea2bad4d667ef"},
......@@ -1600,22 +1727,22 @@ pkginfo = [
{file = "pkginfo-1.5.0.1.tar.gz", hash = "sha256:7424f2c8511c186cd5424bbf31045b77435b37a8d604990b79d4e70d741148bb"},
]
pluggy = [
{file = "pluggy-0.13.0-py2.py3-none-any.whl", hash = "sha256:0db4b7601aae1d35b4a033282da476845aa19185c1e6964b25cf324b5e4ec3e6"},
{file = "pluggy-0.13.0.tar.gz", hash = "sha256:fa5fa1622fa6dd5c030e9cad086fa19ef6a0cf6d7a2d12318e10cb49d6d68f34"},
{file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
{file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
]
pre-commit = [
{file = "pre_commit-1.18.3-py2.py3-none-any.whl", hash = "sha256:fa78ff96e8e9ac94c748388597693f18b041a181c94a4f039ad20f45287ba44a"},
{file = "pre_commit-1.18.3.tar.gz", hash = "sha256:1d3c0587bda7c4e537a46c27f2c84aa006acc18facf9970bf947df596ce91f3f"},
{file = "pre_commit-1.20.0-py2.py3-none-any.whl", hash = "sha256:c2e4810d2d3102d354947907514a78c5d30424d299dc0fe48f5aa049826e9b50"},
{file = "pre_commit-1.20.0.tar.gz", hash = "sha256:9f152687127ec90642a2cc3e4d9e1e6240c4eb153615cb02aa1ad41d331cbb6e"},
{file = "pre_commit-1.21.0-py2.py3-none-any.whl", hash = "sha256:f92a359477f3252452ae2e8d3029de77aec59415c16ae4189bcfba40b757e029"},
{file = "pre_commit-1.21.0.tar.gz", hash = "sha256:8f48d8637bdae6fa70cc97db9c1dd5aa7c5c8bf71968932a380628c25978b850"},
]
ptyprocess = [
{file = "ptyprocess-0.6.0-py2.py3-none-any.whl", hash = "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"},
{file = "ptyprocess-0.6.0.tar.gz", hash = "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0"},
]
py = [
{file = "py-1.8.0-py2.py3-none-any.whl", hash = "sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa"},
{file = "py-1.8.0.tar.gz", hash = "sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"},
{file = "py-1.8.1-py2.py3-none-any.whl", hash = "sha256:c20fdd83a5dbc0af9efd622bee9a5564e278f6380fffcacc43ba6f43db2813b0"},
{file = "py-1.8.1.tar.gz", hash = "sha256:5e27081401262157467ad6e7f851b7aa402c5852dbcb3dae06768434de5752aa"},
]
pycparser = [
{file = "pycparser-2.19.tar.gz", hash = "sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3"},
......@@ -1623,8 +1750,8 @@ pycparser = [
pygments = [
{file = "Pygments-2.3.1-py2.py3-none-any.whl", hash = "sha256:e8218dd399a61674745138520d0d4cf2621d7e032439341bc3f647bff125818d"},
{file = "Pygments-2.3.1.tar.gz", hash = "sha256:5ffada19f6203563680669ee7f53b64dabbeb100eb51b61996085e99c03b284a"},
{file = "Pygments-2.4.2-py2.py3-none-any.whl", hash = "sha256:71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127"},
{file = "Pygments-2.4.2.tar.gz", hash = "sha256:881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297"},
{file = "Pygments-2.5.2-py2.py3-none-any.whl", hash = "sha256:2a3fe295e54a20164a9df49c75fa58526d3be48e14aceba6d6b1e8ac0bfd6f1b"},
{file = "Pygments-2.5.2.tar.gz", hash = "sha256:98c8aa5a9f778fcd1026a17361ddaf7330d1b7c62ae97c3bb0ae73e0b9b6b0fe"},
]
pygments-github-lexers = [
{file = "pygments-github-lexers-0.0.5.tar.gz", hash = "sha256:aaca57e77cd6fcfce8d6ee97a998962eebf7fbb810519a8ebde427c62823e133"},
......@@ -1637,27 +1764,27 @@ pylev = [
pymdown-extensions = [
{file = "pymdown-extensions-6.0.tar.gz", hash = "sha256:6cf0cf36b5a03b291ace22dc2f320f4789ce56fbdb6635a3be5fadbf5d7694dd"},
{file = "pymdown_extensions-6.0-py2.py3-none-any.whl", hash = "sha256:25b0a7967fa697b5035e23340a48594e3e93acb10b06d74574218ace3347d1df"},
{file = "pymdown-extensions-6.1.tar.gz", hash = "sha256:960486dea995f1759dfd517aa140b3d851cd7b44d4c48d276fd2c74fc4e1bce9"},
{file = "pymdown_extensions-6.1-py2.py3-none-any.whl", hash = "sha256:24c1a0afbae101c4e2b2675ff4dd936470a90133f93398b9cbe0c855e2d2ec10"},
{file = "pymdown-extensions-6.2.1.tar.gz", hash = "sha256:3bbe6048275f8a0d13a0fe44e0ea201e67268aa7bb40c2544eef16abbf168f7b"},
{file = "pymdown_extensions-6.2.1-py2.py3-none-any.whl", hash = "sha256:dce5e17b93be0572322b7d06c9a13c13a9d98694d6468277911d50ca87d26f29"},
]
pyparsing = [
{file = "pyparsing-2.4.2-py2.py3-none-any.whl", hash = "sha256:d9338df12903bbf5d65a0e4e87c2161968b10d2e489652bb47001d82a9b028b4"},
{file = "pyparsing-2.4.2.tar.gz", hash = "sha256:6f98a7b9397e206d78cc01df10131398f1c8b8510a2f4d97d9abd82e1aacdd80"},
{file = "pyparsing-2.4.6-py2.py3-none-any.whl", hash = "sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec"},
{file = "pyparsing-2.4.6.tar.gz", hash = "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f"},
]
pyrsistent = [
{file = "pyrsistent-0.14.11.tar.gz", hash = "sha256:3ca82748918eb65e2d89f222b702277099aca77e34843c5eb9d52451173970e2"},
]
pytest = [
{file = "pytest-4.6.6-py2.py3-none-any.whl", hash = "sha256:5d0d20a9a66e39b5845ab14f8989f3463a7aa973700e6cdf02db69da9821e738"},
{file = "pytest-4.6.6.tar.gz", hash = "sha256:692d9351353ef709c1126266579edd4fd469dcf6b5f4f583050f72161d6f3592"},
{file = "pytest-4.6.9-py2.py3-none-any.whl", hash = "sha256:c77a5f30a90e0ce24db9eaa14ddfd38d4afb5ea159309bdd2dae55b931bc9324"},
{file = "pytest-4.6.9.tar.gz", hash = "sha256:19e8f75eac01dd3f211edd465b39efbcbdc8fc5f7866d7dd49fedb30d8adf339"},
]
pytest-cov = [
{file = "pytest-cov-2.8.1.tar.gz", hash = "sha256:cc6742d8bac45070217169f5f72ceee1e0e55b0221f54bcf24845972d3a47f2b"},
{file = "pytest_cov-2.8.1-py2.py3-none-any.whl", hash = "sha256:cdbdef4f870408ebdbfeb44e63e07eb18bb4619fae852f6e760645fa36172626"},
]
pytest-mock = [
{file = "pytest-mock-1.11.2.tar.gz", hash = "sha256:ea502c3891599c26243a3a847ccf0b1d20556678c528f86c98e3cd6d40c5cf11"},
{file = "pytest_mock-1.11.2-py2.py3-none-any.whl", hash = "sha256:b3514caac35fe3f05555923eabd9546abce11571cc2ddf7d8615959d04f2c89e"},
{file = "pytest-mock-1.13.0.tar.gz", hash = "sha256:e24a911ec96773022ebcc7030059b57cd3480b56d4f5d19b7c370ec635e6aed5"},
{file = "pytest_mock-1.13.0-py2.py3-none-any.whl", hash = "sha256:67e414b3caef7bff6fc6bd83b22b5bc39147e4493f483c2679bc9d4dc485a94d"},
]
pytest-sugar = [
{file = "pytest-sugar-0.9.2.tar.gz", hash = "sha256:fcd87a74b2bce5386d244b49ad60549bfbc4602527797fac167da147983f58ab"},
......@@ -1668,32 +1795,51 @@ pywin32-ctypes = [
{file = "pywin32_ctypes-0.2.0-py2.py3-none-any.whl", hash = "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"},
]
pyyaml = [
{file = "PyYAML-5.1.2-cp27-cp27m-win32.whl", hash = "sha256:5124373960b0b3f4aa7df1707e63e9f109b5263eca5976c66e08b1c552d4eaf8"},
{file = "PyYAML-5.1.2-cp27-cp27m-win_amd64.whl", hash = "sha256:f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8"},
{file = "PyYAML-5.1.2-cp34-cp34m-win32.whl", hash = "sha256:0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9"},
{file = "PyYAML-5.1.2-cp34-cp34m-win_amd64.whl", hash = "sha256:5ca4f10adbddae56d824b2c09668e91219bb178a1eee1faa56af6f99f11bf696"},
{file = "PyYAML-5.1.2-cp35-cp35m-win32.whl", hash = "sha256:bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41"},
{file = "PyYAML-5.1.2-cp35-cp35m-win_amd64.whl", hash = "sha256:87ae4c829bb25b9fe99cf71fbb2140c448f534e24c998cc60f39ae4f94396a73"},
{file = "PyYAML-5.1.2-cp36-cp36m-win32.whl", hash = "sha256:9de9919becc9cc2ff03637872a440195ac4241c80536632fffeb6a1e25a74299"},
{file = "PyYAML-5.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:a5a85b10e450c66b49f98846937e8cfca1db3127a9d5d1e31ca45c3d0bef4c5b"},
{file = "PyYAML-5.1.2-cp37-cp37m-win32.whl", hash = "sha256:b0997827b4f6a7c286c01c5f60384d218dca4ed7d9efa945c3e1aa623d5709ae"},
{file = "PyYAML-5.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:7907be34ffa3c5a32b60b95f4d95ea25361c951383a894fec31be7252b2b6f34"},
{file = "PyYAML-5.1.2-cp38-cp38m-win32.whl", hash = "sha256:7ec9b2a4ed5cad025c2278a1e6a19c011c80a3caaac804fd2d329e9cc2c287c9"},
{file = "PyYAML-5.1.2-cp38-cp38m-win_amd64.whl", hash = "sha256:b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681"},
{file = "PyYAML-5.1.2.tar.gz", hash = "sha256:01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4"},
{file = "PyYAML-5.2-cp27-cp27m-win32.whl", hash = "sha256:35ace9b4147848cafac3db142795ee42deebe9d0dad885ce643928e88daebdcc"},
{file = "PyYAML-5.2-cp27-cp27m-win_amd64.whl", hash = "sha256:ebc4ed52dcc93eeebeae5cf5deb2ae4347b3a81c3fa12b0b8c976544829396a4"},
{file = "PyYAML-5.2-cp35-cp35m-win32.whl", hash = "sha256:38a4f0d114101c58c0f3a88aeaa44d63efd588845c5a2df5290b73db8f246d15"},
{file = "PyYAML-5.2-cp35-cp35m-win_amd64.whl", hash = "sha256:483eb6a33b671408c8529106df3707270bfacb2447bf8ad856a4b4f57f6e3075"},
{file = "PyYAML-5.2-cp36-cp36m-win32.whl", hash = "sha256:7f38e35c00e160db592091751d385cd7b3046d6d51f578b29943225178257b31"},
{file = "PyYAML-5.2-cp36-cp36m-win_amd64.whl", hash = "sha256:0e7f69397d53155e55d10ff68fdfb2cf630a35e6daf65cf0bdeaf04f127c09dc"},
{file = "PyYAML-5.2-cp37-cp37m-win32.whl", hash = "sha256:e4c015484ff0ff197564917b4b4246ca03f411b9bd7f16e02a2f586eb48b6d04"},
{file = "PyYAML-5.2-cp37-cp37m-win_amd64.whl", hash = "sha256:4b6be5edb9f6bb73680f5bf4ee08ff25416d1400fbd4535fe0069b2994da07cd"},
{file = "PyYAML-5.2-cp38-cp38-win32.whl", hash = "sha256:8100c896ecb361794d8bfdb9c11fce618c7cf83d624d73d5ab38aef3bc82d43f"},
{file = "PyYAML-5.2-cp38-cp38-win_amd64.whl", hash = "sha256:2e9f0b7c5914367b0916c3c104a024bb68f269a486b9d04a2e8ac6f6597b7803"},
{file = "PyYAML-5.2.tar.gz", hash = "sha256:c0ee8eca2c582d29c3c2ec6e2c4f703d1b7f1fb10bc72317355a746057e7346c"},
{file = "PyYAML-5.3-cp27-cp27m-win32.whl", hash = "sha256:940532b111b1952befd7db542c370887a8611660d2b9becff75d39355303d82d"},
{file = "PyYAML-5.3-cp27-cp27m-win_amd64.whl", hash = "sha256:059b2ee3194d718896c0ad077dd8c043e5e909d9180f387ce42012662a4946d6"},
{file = "PyYAML-5.3-cp35-cp35m-win32.whl", hash = "sha256:4fee71aa5bc6ed9d5f116327c04273e25ae31a3020386916905767ec4fc5317e"},
{file = "PyYAML-5.3-cp35-cp35m-win_amd64.whl", hash = "sha256:dbbb2379c19ed6042e8f11f2a2c66d39cceb8aeace421bfc29d085d93eda3689"},
{file = "PyYAML-5.3-cp36-cp36m-win32.whl", hash = "sha256:e3a057b7a64f1222b56e47bcff5e4b94c4f61faac04c7c4ecb1985e18caa3994"},
{file = "PyYAML-5.3-cp36-cp36m-win_amd64.whl", hash = "sha256:74782fbd4d4f87ff04159e986886931456a1894c61229be9eaf4de6f6e44b99e"},
{file = "PyYAML-5.3-cp37-cp37m-win32.whl", hash = "sha256:24521fa2890642614558b492b473bee0ac1f8057a7263156b02e8b14c88ce6f5"},
{file = "PyYAML-5.3-cp37-cp37m-win_amd64.whl", hash = "sha256:1cf708e2ac57f3aabc87405f04b86354f66799c8e62c28c5fc5f88b5521b2dbf"},
{file = "PyYAML-5.3-cp38-cp38-win32.whl", hash = "sha256:70024e02197337533eef7b85b068212420f950319cc8c580261963aefc75f811"},
{file = "PyYAML-5.3-cp38-cp38-win_amd64.whl", hash = "sha256:cb1f2f5e426dc9f07a7681419fe39cee823bb74f723f36f70399123f439e9b20"},
{file = "PyYAML-5.3.tar.gz", hash = "sha256:e9f45bd5b92c7974e59bcd2dcc8631a6b6cc380a904725fce7bc08872e691615"},
]
regex = [
{file = "regex-2019.08.19-cp27-none-win32.whl", hash = "sha256:1e9f9bc44ca195baf0040b1938e6801d2f3409661c15fe57f8164c678cfc663f"},
{file = "regex-2019.08.19-cp27-none-win_amd64.whl", hash = "sha256:93f6c9da57e704e128d90736430c5c59dd733327882b371b0cae8833106c2a21"},
{file = "regex-2019.08.19-cp35-none-win32.whl", hash = "sha256:c5c8999b3a341b21ac2c6ec704cfcccbc50f1fedd61b6a8ee915ca7fd4b0a557"},
{file = "regex-2019.08.19-cp35-none-win_amd64.whl", hash = "sha256:f2b37b5b2c2a9d56d9e88efef200ec09c36c7f323f9d58d0b985a90923df386d"},
{file = "regex-2019.08.19-cp36-none-win32.whl", hash = "sha256:d96479257e8e4d1d7800adb26bf9c5ca5bab1648a1eddcac84d107b73dc68327"},
{file = "regex-2019.08.19-cp36-none-win_amd64.whl", hash = "sha256:a46f27d267665016acb3ec8c6046ec5eae8cf80befe85ba47f43c6f5ec636dcd"},
{file = "regex-2019.08.19-cp37-none-win32.whl", hash = "sha256:d4d1829cf97632673aa49f378b0a2c3925acd795148c5ace8ef854217abbee89"},
{file = "regex-2019.08.19-cp37-none-win_amd64.whl", hash = "sha256:fe765b809a1f7ce642c2edeee351e7ebd84391640031ba4b60af8d91a9045890"},
{file = "regex-2019.08.19-cp38-none-win32.whl", hash = "sha256:f20f4912daf443220436759858f96fefbfc6c6ba9e67835fd6e4e9b73582791a"},
{file = "regex-2019.08.19-cp38-none-win_amd64.whl", hash = "sha256:835ccdcdc612821edf132c20aef3eaaecfb884c9454fdc480d5887562594ac61"},
{file = "regex-2019.08.19.tar.gz", hash = "sha256:587b62d48ca359d2d4f02d486f1f0aa9a20fbaf23a9d4198c4bed72ab2f6c849"},
{file = "regex-2020.1.8-cp27-cp27m-win32.whl", hash = "sha256:4e8f02d3d72ca94efc8396f8036c0d3bcc812aefc28ec70f35bb888c74a25161"},
{file = "regex-2020.1.8-cp27-cp27m-win_amd64.whl", hash = "sha256:e6c02171d62ed6972ca8631f6f34fa3281d51db8b326ee397b9c83093a6b7242"},
{file = "regex-2020.1.8-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:4eae742636aec40cf7ab98171ab9400393360b97e8f9da67b1867a9ee0889b26"},
{file = "regex-2020.1.8-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:bd25bb7980917e4e70ccccd7e3b5740614f1c408a642c245019cff9d7d1b6149"},
{file = "regex-2020.1.8-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:3e77409b678b21a056415da3a56abfd7c3ad03da71f3051bbcdb68cf44d3c34d"},
{file = "regex-2020.1.8-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:07b39bf943d3d2fe63d46281d8504f8df0ff3fe4c57e13d1656737950e53e525"},
{file = "regex-2020.1.8-cp36-cp36m-win32.whl", hash = "sha256:23e2c2c0ff50f44877f64780b815b8fd2e003cda9ce817a7fd00dea5600c84a0"},
{file = "regex-2020.1.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27429b8d74ba683484a06b260b7bb00f312e7c757792628ea251afdbf1434003"},
{file = "regex-2020.1.8-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0e182d2f097ea8549a249040922fa2b92ae28be4be4895933e369a525ba36576"},
{file = "regex-2020.1.8-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e3cd21cc2840ca67de0bbe4071f79f031c81418deb544ceda93ad75ca1ee9f7b"},
{file = "regex-2020.1.8-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:ecc6de77df3ef68fee966bb8cb4e067e84d4d1f397d0ef6fce46913663540d77"},
{file = "regex-2020.1.8-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:26ff99c980f53b3191d8931b199b29d6787c059f2e029b2b0c694343b1708c35"},
{file = "regex-2020.1.8-cp37-cp37m-win32.whl", hash = "sha256:7bcd322935377abcc79bfe5b63c44abd0b29387f267791d566bbb566edfdd146"},
{file = "regex-2020.1.8-cp37-cp37m-win_amd64.whl", hash = "sha256:10671601ee06cf4dc1bc0b4805309040bb34c9af423c12c379c83d7895622bb5"},
{file = "regex-2020.1.8-cp38-cp38-manylinux1_i686.whl", hash = "sha256:98b8ed7bb2155e2cbb8b76f627b2fd12cf4b22ab6e14873e8641f266e0fb6d8f"},
{file = "regex-2020.1.8-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6a6ba91b94427cd49cd27764679024b14a96874e0dc638ae6bdd4b1a3ce97be1"},
{file = "regex-2020.1.8-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:6a6ae17bf8f2d82d1e8858a47757ce389b880083c4ff2498dba17c56e6c103b9"},
{file = "regex-2020.1.8-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:0932941cdfb3afcbc26cc3bcf7c3f3d73d5a9b9c56955d432dbf8bbc147d4c5b"},
{file = "regex-2020.1.8-cp38-cp38-win32.whl", hash = "sha256:d58e4606da2a41659c84baeb3cfa2e4c87a74cec89a1e7c56bee4b956f9d7461"},
{file = "regex-2020.1.8-cp38-cp38-win_amd64.whl", hash = "sha256:e7c7661f7276507bce416eaae22040fd91ca471b5b33c13f8ff21137ed6f248c"},
{file = "regex-2020.1.8.tar.gz", hash = "sha256:d0f424328f9822b0323b3b6f2e4b9c90960b24743d220763c7f07071e0778351"},
]
requests = [
{file = "requests-2.21.0-py2.py3-none-any.whl", hash = "sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b"},
......@@ -1720,16 +1866,16 @@ scandir = [
]
secretstorage = [
{file = "SecretStorage-2.3.1.tar.gz", hash = "sha256:3af65c87765323e6f64c83575b05393f9e003431959c9395d1791d51497f29b6"},
{file = "SecretStorage-3.1.1-py3-none-any.whl", hash = "sha256:7a119fb52a88e398dbb22a4b3eb39b779bfbace7e4153b7bc6e5954d86282a8a"},
{file = "SecretStorage-3.1.1.tar.gz", hash = "sha256:20c797ae48a4419f66f8d28fc221623f11fc45b6828f96bdb1ad9990acb59f92"},
{file = "SecretStorage-3.1.2-py3-none-any.whl", hash = "sha256:b5ec909dde94d4ae2fa26af7c089036997030f0cf0a5cb372b4cccabd81c143b"},
{file = "SecretStorage-3.1.2.tar.gz", hash = "sha256:15da8a989b65498e29be338b3b279965f1b8f09b9668bd8010da183024c8bff6"},
]
shellingham = [
{file = "shellingham-1.3.1-py2.py3-none-any.whl", hash = "sha256:77d37a4fd287c1e663006f7ecf1b9deca9ad492d0082587bd813c44eb49e4e62"},
{file = "shellingham-1.3.1.tar.gz", hash = "sha256:985b23bbd1feae47ca6a6365eacd314d93d95a8a16f8f346945074c28fe6f3e0"},
]
six = [
{file = "six-1.12.0-py2.py3-none-any.whl", hash = "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c"},
{file = "six-1.12.0.tar.gz", hash = "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"},
{file = "six-1.13.0-py2.py3-none-any.whl", hash = "sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd"},
{file = "six-1.13.0.tar.gz", hash = "sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66"},
]
subprocess32 = [
{file = "subprocess32-3.5.4-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:88e37c1aac5388df41cc8a8456bb49ebffd321a3ad4d70358e3518176de3a56b"},
......@@ -1764,8 +1910,10 @@ tornado = [
{file = "tornado-6.0.3.tar.gz", hash = "sha256:c845db36ba616912074c5b1ee897f8e0124df269468f25e4fe21fe72f6edd7a9"},
]
tox = [
{file = "tox-3.14.0-py2.py3-none-any.whl", hash = "sha256:0bc216b6a2e6afe764476b4a07edf2c1dab99ed82bb146a1130b2e828f5bff5e"},
{file = "tox-3.14.0.tar.gz", hash = "sha256:c4f6b319c20ba4913dbfe71ebfd14ff95d1853c4231493608182f66e566ecfe1"},
{file = "tox-3.12.1-py2.py3-none-any.whl", hash = "sha256:f5c8e446b51edd2ea97df31d4ded8c8b72e7d6c619519da6bb6084b9dd5770f9"},
{file = "tox-3.12.1.tar.gz", hash = "sha256:f87fd33892a2df0950e5e034def9468988b8d008c7e9416be665fcc0dd45b14f"},
{file = "tox-3.14.3-py2.py3-none-any.whl", hash = "sha256:806d0a9217584558cc93747a945a9d9bff10b141a5287f0c8429a08828a22192"},
{file = "tox-3.14.3.tar.gz", hash = "sha256:06ba73b149bf838d5cd25dc30c2dd2671ae5b2757cf98e5c41a35fe449f131b3"},
]
typed-ast = [
{file = "typed_ast-1.4.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e"},
......@@ -1797,16 +1945,16 @@ typing = [
urllib3 = [
{file = "urllib3-1.24.3-py2.py3-none-any.whl", hash = "sha256:a637e5fae88995b256e3409dc4d52c2e2e0ba32c42a6365fee8bbd2238de3cfb"},
{file = "urllib3-1.24.3.tar.gz", hash = "sha256:2393a695cd12afedd0dcb26fe5d50d0cf248e5a66f75dbd89a3d4eb333a61af4"},
{file = "urllib3-1.25.6-py2.py3-none-any.whl", hash = "sha256:3de946ffbed6e6746608990594d08faac602528ac7015ac28d33cee6a45b7398"},
{file = "urllib3-1.25.6.tar.gz", hash = "sha256:9a107b99a5393caf59c7aa3c1249c16e6879447533d0887f4336dde834c7be86"},
{file = "urllib3-1.25.7-py2.py3-none-any.whl", hash = "sha256:a8a318824cc77d1fd4b2bec2ded92646630d7fe8619497b142c84a9e6f5a7293"},
{file = "urllib3-1.25.7.tar.gz", hash = "sha256:f3c5fd51747d450d4dcf6f923c81f78f811aab8205fda64b0aba34a4e48b0745"},
]
virtualenv = [
{file = "virtualenv-16.7.7-py2.py3-none-any.whl", hash = "sha256:11cb4608930d5fd3afb545ecf8db83fa50e1f96fc4fca80c94b07d2c83146589"},
{file = "virtualenv-16.7.7.tar.gz", hash = "sha256:d257bb3773e48cac60e475a19b608996c73f4d333b3ba2e4e57d5ac6134e0136"},
{file = "virtualenv-16.7.9-py2.py3-none-any.whl", hash = "sha256:55059a7a676e4e19498f1aad09b8313a38fcc0cdbe4fdddc0e9b06946d21b4bb"},
{file = "virtualenv-16.7.9.tar.gz", hash = "sha256:0d62c70883c0342d59c11d0ddac0d954d0431321a41ab20851facf2b222598f3"},
]
wcwidth = [
{file = "wcwidth-0.1.7-py2.py3-none-any.whl", hash = "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"},
{file = "wcwidth-0.1.7.tar.gz", hash = "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e"},
{file = "wcwidth-0.1.8-py2.py3-none-any.whl", hash = "sha256:8fd29383f539be45b20bd4df0dc29c20ba48654a41e661925e612311e9f3c603"},
{file = "wcwidth-0.1.8.tar.gz", hash = "sha256:f28b3e8a6483e5d49e7f8949ac1a78314e740333ae305b4ba5defd3e74fb37a8"},
]
webencodings = [
{file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"},
......
__version__ = "1.0.0b3"
__version__ = "1.0.1"
......@@ -12,5 +12,5 @@ class AboutCommand(Command):
"""<info>Poetry - Package Management for Python</info>
<comment>Poetry is a dependency manager tracking local dependencies of your projects and libraries.
See <fg=blue>https://github.com/sdispater/poetry</> for more information.</comment>"""
See <fg=blue>https://github.com/python-poetry/poetry</> for more information.</comment>"""
)
......@@ -5,8 +5,6 @@ from cleo import argument
from cleo import option
from poetry.factory import Factory
from poetry.utils.helpers import keyring_repository_password_del
from poetry.utils.helpers import keyring_repository_password_set
from .command import Command
......@@ -181,11 +179,14 @@ To remove a repository (repo is a short alias for repositories):
# handle auth
m = re.match(r"^(http-basic|pypi-token)\.(.+)", self.argument("key"))
if m:
from poetry.utils.password_manager import PasswordManager
password_manager = PasswordManager(config)
if self.option("unset"):
keyring_repository_password_del(config, m.group(2))
config.auth_config_source.remove_property(
"{}.{}".format(m.group(1), m.group(2))
)
if m.group(1) == "http-basic":
password_manager.delete_http_password(m.group(2))
elif m.group(1) == "pypi-token":
password_manager.delete_pypi_token(m.group(2))
return 0
......@@ -203,15 +204,7 @@ To remove a repository (repo is a short alias for repositories):
username = values[0]
password = values[1]
property_value = dict(username=username)
try:
keyring_repository_password_set(m.group(2), username, password)
except RuntimeError:
property_value.update(password=password)
config.auth_config_source.add_property(
"{}.{}".format(m.group(1), m.group(2)), property_value
)
password_manager.set_http_password(m.group(2), username, password)
elif m.group(1) == "pypi-token":
if len(values) != 1:
raise ValueError(
......@@ -220,9 +213,7 @@ To remove a repository (repo is a short alias for repositories):
token = values[0]
config.auth_config_source.add_property(
"{}.{}".format(m.group(1), m.group(2)), token
)
password_manager.set_pypi_token(m.group(2), token)
return 0
......
......@@ -28,10 +28,11 @@ class DebugResolveCommand(InitCommand):
loggers = ["poetry.repositories.pypi_repository"]
def handle(self):
from poetry.io.null_io import NullIO
from poetry.packages import ProjectPackage
from poetry.puzzle import Solver
from poetry.repositories.pool import Pool
from poetry.repositories.repository import Repository
from poetry.semver import parse_constraint
from poetry.utils.env import EnvManager
packages = self.argument("package")
......@@ -99,20 +100,30 @@ class DebugResolveCommand(InitCommand):
return 0
env = EnvManager(self.poetry).get()
current_python_version = parse_constraint(
".".join(str(v) for v in env.version_info)
)
table = self.table([], style="borderless")
rows = []
if self.option("install"):
env = EnvManager(self.poetry).get()
current_python_version = ".".join(str(v) for v in env.version_info)
pool = Pool()
locked_repository = Repository()
for op in ops:
locked_repository.add_package(op.package)
pool.add_repository(locked_repository)
with package.with_python_versions(current_python_version):
solver = Solver(package, pool, Repository(), Repository(), NullIO())
ops = solver.solve()
for op in ops:
pkg = op.package
if self.option("install"):
if not pkg.python_constraint.allows(
current_python_version
) or not env.is_valid_for_marker(pkg.marker):
continue
row = ["<c1>{}</c1>".format(pkg.name), "<b>{}</b>".format(pkg.version), ""]
row = [
"<c1>{}</c1>".format(pkg.name),
"<b>{}</b>".format(pkg.version),
"",
]
if not pkg.marker.is_any():
row[2] = str(pkg.marker)
......
......@@ -147,8 +147,8 @@ The <c1>init</c1> command creates a basic <comment>pyproject.toml</> file in the
"You can specify a package in the following forms:\n"
" - A single name (<b>requests</b>)\n"
" - A name and a constraint (<b>requests ^2.23.0</b>)\n"
" - A git url (<b>https://github.com/sdispater/poetry.git</b>)\n"
" - A git url with a revision (<b>https://github.com/sdispater/poetry.git@develop</b>)\n"
" - A git url (<b>git+https://github.com/python-poetry/poetry.git</b>)\n"
" - A git url with a revision (<b>git+https://github.com/python-poetry/poetry.git#develop</b>)\n"
" - A file path (<b>../my-package/my-package.whl</b>)\n"
" - A directory (<b>../my-package/</b>)\n"
" - An url (<b>https://example.com/packages/my-package-0.1.0.tar.gz</b>)\n"
......@@ -365,22 +365,21 @@ The <c1>init</c1> command creates a basic <comment>pyproject.toml</> file in the
if url_parsed.scheme and url_parsed.netloc:
# Url
if url_parsed.scheme in ["git+https", "git+ssh"]:
url = requirement.lstrip("git+")
rev = None
if "@" in url:
url, rev = url.split("@")
from poetry.vcs.git import Git
from poetry.vcs.git import ParsedUrl
pair = OrderedDict(
[("name", url.split("/")[-1].rstrip(".git")), ("git", url)]
)
if rev:
pair["rev"] = rev
parsed = ParsedUrl.parse(requirement)
url = Git.normalize_url(requirement)
pair = OrderedDict([("name", parsed.name), ("git", url.url)])
if parsed.rev:
pair["rev"] = url.revision
if extras:
pair["extras"] = extras
package = Provider.get_package_from_vcs(
"git", url, reference=pair.get("rev")
"git", url.url, reference=pair.get("rev")
)
pair["name"] = package.name
result.append(pair)
......@@ -426,6 +425,11 @@ The <c1>init</c1> command creates a basic <comment>pyproject.toml</> file in the
require = OrderedDict()
if " " in pair:
name, version = pair.split(" ", 2)
extras_m = re.search(r"\[([\w\d,-_]+)\]$", name)
if extras_m:
extras = [e.strip() for e in extras_m.group(1).split(",")]
name, _ = name.split("[")
require["name"] = name
if version != "latest":
require["version"] = version
......
......@@ -6,8 +6,10 @@ class LockCommand(EnvCommand):
name = "lock"
description = "Locks the project dependencies."
help = """The <info>lock</info> command reads the <comment>pyproject.toml</> file from
the current directory, processes it, and locks the depdencies in the <comment>poetry.lock</> file.
help = """
The <info>lock</info> command reads the <comment>pyproject.toml</> file from the
current directory, processes it, and locks the dependencies in the <comment>poetry.lock</>
file.
<info>poetry lock</info>
"""
......
......@@ -3,6 +3,8 @@ import sys
from cleo import argument
from cleo import option
from poetry.utils.helpers import module_name
from .command import Command
......@@ -76,6 +78,6 @@ class NewCommand(Command):
self.line(
"Created package <info>{}</> in <fg=blue>{}</>".format(
name, path.relative_to(Path.cwd())
module_name(name), path.relative_to(Path.cwd())
)
)
......@@ -172,6 +172,13 @@ lists all packages available."""
# Non installed in non decorated mode
install_marker = " (!)"
if (
show_latest
and self.option("outdated")
and latest_statuses[locked.pretty_name] == "up-to-date"
):
continue
line = "<fg={}>{:{}}{}</>".format(
color, name, name_length - len(install_marker), install_marker
)
......@@ -183,9 +190,6 @@ lists all packages available."""
latest = latest_packages[locked.pretty_name]
update_status = latest_statuses[locked.pretty_name]
if self.option("outdated") and update_status == "up-to-date":
continue
if write_latest:
color = "green"
if update_status == "semver-safe-update":
......
......@@ -7,7 +7,7 @@ class VersionCommand(Command):
name = "version"
description = (
"Shows the version of the project or bumps it when a valid"
"Shows the version of the project or bumps it when a valid "
"bump rule is provided."
)
......@@ -59,7 +59,7 @@ patch, minor, major, prepatch, preminor, premajor, prerelease.
self.poetry.file.write(content)
else:
self.line(
"Project (<comment>{}</>) version is <info>{}</>".format(
"<comment>{}</> <info>{}</>".format(
self.poetry.package.name, self.poetry.package.pretty_version
)
)
......
import logging
from typing import Any
from cleo.config import ApplicationConfig as BaseApplicationConfig
from clikit.api.application.application import Application
from clikit.api.args.raw_args import RawArgs
......@@ -43,15 +45,15 @@ class ApplicationConfig(BaseApplicationConfig):
self.add_event_listener(PRE_HANDLE, self.set_env)
def register_command_loggers(
self, event, event_name, _ # type: PreHandleEvent # type: str
): # type: (...) -> None
self, event, event_name, _
): # type: (PreHandleEvent, str, Any) -> None
command = event.command.config.handler
if not isinstance(command, Command):
return
io = event.io
loggers = ["poetry.packages.package"]
loggers = ["poetry.packages.package", "poetry.utils.password_manager"]
loggers += command.loggers
......@@ -72,7 +74,7 @@ class ApplicationConfig(BaseApplicationConfig):
logger.setLevel(level)
def set_env(self, event, event_name, _): # type: (PreHandleEvent, str, _) -> None
def set_env(self, event, event_name, _): # type: (PreHandleEvent, str, Any) -> None
from poetry.utils.env import EnvManager
command = event.command.config.handler # type: EnvCommand
......
......@@ -233,7 +233,8 @@ class Factory:
): # type: (Dict[str, str], Config) -> LegacyRepository
from .repositories.auth import Auth
from .repositories.legacy_repository import LegacyRepository
from .utils.helpers import get_client_cert, get_cert, get_http_basic_auth
from .utils.helpers import get_client_cert, get_cert
from .utils.password_manager import PasswordManager
if "url" in source:
# PyPI-like repository
......@@ -242,11 +243,12 @@ class Factory:
else:
raise RuntimeError("Unsupported source specified")
password_manager = PasswordManager(auth_config)
name = source["name"]
url = source["url"]
credentials = get_http_basic_auth(auth_config, name)
credentials = password_manager.get_http_auth(name)
if credentials:
auth = Auth(url, credentials[0], credentials[1])
auth = Auth(url, credentials["username"], credentials["password"])
else:
auth = None
......@@ -298,7 +300,7 @@ class Factory:
result["warnings"].append(
'The "{}" dependency specifies '
'the "allows-prereleases" property, which is deprecated. '
'Use "allow-preleases" instead.'.format(name)
'Use "allow-prereleases" instead.'.format(name)
)
# Checking for scripts with extras
......
......@@ -234,7 +234,7 @@ class Installer:
# Execute operations
actual_ops = [op for op in ops if not op.skipped]
if not actual_ops and (self._execute_operations or self._dry_run):
self._io.write_line("Nothing to install or update")
self._io.write_line("No dependencies to install or update")
if actual_ops and (self._execute_operations or self._dry_run):
installs = []
......
......@@ -40,7 +40,10 @@ class PipInstaller(BaseInstaller):
args = ["install", "--no-deps"]
if package.source_type == "legacy" and package.source_url:
if (
package.source_type not in {"git", "directory", "file", "url"}
and package.source_url
):
repository = self._pool.repository(package.source_reference)
parsed = urlparse.urlparse(package.source_url)
if parsed.scheme == "http":
......@@ -70,7 +73,7 @@ class PipInstaller(BaseInstaller):
if update:
args.append("-U")
if package.files and not package.source_type:
if package.files and not package.source_url:
# Format as a requirements.txt
# We need to create a requirements.txt file
# for each package in order to check hashes.
......@@ -93,7 +96,12 @@ class PipInstaller(BaseInstaller):
self.run(*args)
def update(self, _, target):
def update(self, package, target):
if package.source_type != target.source_type:
# If the source type has changed, we remove the current
# package to avoid perpetual updates in some cases
self.remove(package)
self.install(target, update=True)
def remove(self, package):
......@@ -112,7 +120,7 @@ class PipInstaller(BaseInstaller):
raise
def run(self, *args, **kwargs): # type: (...) -> str
return self._env.run("python", "-m", "pip", *args, **kwargs)
return self._env.run_pip(*args, **kwargs)
def requirement(self, package, formatted=False):
if formatted and not package.source_type:
......
......@@ -11,7 +11,6 @@ from typing import Union
from clikit.api.io.flags import VERY_VERBOSE
from poetry.utils._compat import Path
from poetry.utils._compat import basestring
from poetry.utils._compat import glob
from poetry.utils._compat import lru_cache
from poetry.utils._compat import to_str
......@@ -84,15 +83,6 @@ class Builder(object):
explicitely_excluded = set()
for excluded_glob in self._package.exclude:
excluded_path = Path(self._path, excluded_glob)
try:
is_dir = excluded_path.is_dir()
except OSError:
# On Windows, testing if a path with a glob is a directory will raise an OSError
is_dir = False
if is_dir:
excluded_glob = Path(excluded_glob, "**/*")
for excluded in glob(
Path(self._path, excluded_glob).as_posix(), recursive=True
......@@ -112,10 +102,18 @@ class Builder(object):
return result
def is_excluded(self, filepath): # type: (Union[str, Path]) -> bool
if not isinstance(filepath, basestring):
filepath = filepath.as_posix()
exclude_path = Path(filepath)
while True:
if exclude_path.as_posix() in self.find_excluded_files():
return True
if len(exclude_path.parts) > 1:
exclude_path = exclude_path.parent
else:
break
return filepath in self.find_excluded_files()
return False
def find_files_to_add(self, exclude_build=True): # type: (bool) -> list
"""
......@@ -164,7 +162,7 @@ class Builder(object):
)
to_add.append(license_file.relative_to(self._path))
# If a README is specificed we need to include it
# If a README is specified we need to include it
# to avoid errors
if "readme" in self._poetry.local_config:
readme = self._path / self._poetry.local_config["readme"]
......
......@@ -31,16 +31,14 @@ class EditableBuilder(Builder):
try:
if self._env.pip_version < Version(19, 0):
self._env.run("python", "-m", "pip", "install", "-e", str(self._path))
self._env.run_pip("install", "-e", str(self._path))
else:
# Temporarily rename pyproject.toml
shutil.move(
str(self._poetry.file), str(self._poetry.file.with_suffix(".tmp"))
)
try:
self._env.run(
"python", "-m", "pip", "install", "-e", str(self._path)
)
self._env.run_pip("install", "-e", str(self._path))
finally:
shutil.move(
str(self._poetry.file.with_suffix(".tmp")),
......
......@@ -2,6 +2,7 @@
import os
import re
import tarfile
import time
from collections import defaultdict
from copy import copy
......@@ -83,12 +84,14 @@ class SdistBuilder(Builder):
setup = self.build_setup()
tar_info = tarfile.TarInfo(pjoin(tar_dir, "setup.py"))
tar_info.size = len(setup)
tar_info.mtime = time.time()
tar.addfile(tar_info, BytesIO(setup))
pkg_info = self.build_pkg_info()
tar_info = tarfile.TarInfo(pjoin(tar_dir, "PKG-INFO"))
tar_info.size = len(pkg_info)
tar_info.mtime = time.time()
tar.addfile(tar_info, BytesIO(pkg_info))
finally:
tar.close()
......@@ -234,7 +237,7 @@ class SdistBuilder(Builder):
if from_top_level == ".":
continue
is_subpkg = "__init__.py" in filenames
is_subpkg = any([filename.endswith(".py") for filename in filenames])
if is_subpkg:
subpkg_paths.add(from_top_level)
parts = from_top_level.split(os.sep)
......
......@@ -115,9 +115,9 @@ class WheelBuilder(Builder):
return
lib = lib[0]
excluded = self.find_excluded_files()
for pkg in lib.glob("**/*"):
if pkg.is_dir() or pkg in excluded:
if pkg.is_dir() or self.is_excluded(pkg):
continue
rel_path = str(pkg.relative_to(lib))
......@@ -132,7 +132,7 @@ class WheelBuilder(Builder):
self._add_file(wheel, pkg, rel_path)
def _copy_module(self, wheel):
excluded = self.find_excluded_files()
to_add = []
for include in self._module.includes:
......@@ -153,7 +153,9 @@ class WheelBuilder(Builder):
else:
rel_file = file.relative_to(self._path)
if rel_file.as_posix() in excluded:
if self.is_excluded(rel_file.as_posix()) and isinstance(
include, PackageInclude
):
continue
if file.suffix == ".pyc":
......
......@@ -2,7 +2,7 @@ import logging
from poetry.utils.helpers import get_cert
from poetry.utils.helpers import get_client_cert
from poetry.utils.helpers import get_http_basic_auth
from poetry.utils.password_manager import PasswordManager
from .uploader import Uploader
......@@ -20,6 +20,7 @@ class Publisher:
self._package = poetry.package
self._io = io
self._uploader = Uploader(poetry, io)
self._password_manager = PasswordManager(poetry.config)
@property
def files(self):
......@@ -60,21 +61,21 @@ class Publisher:
if not (username and password):
# Check if we have a token first
token = self._poetry.config.get("pypi-token.{}".format(repository_name))
token = self._password_manager.get_pypi_token(repository_name)
if token:
logger.debug("Found an API token for {}.".format(repository_name))
username = "__token__"
password = token
else:
auth = get_http_basic_auth(self._poetry.config, repository_name)
auth = self._password_manager.get_http_auth(repository_name)
if auth:
logger.debug(
"Found authentication information for {}.".format(
repository_name
)
)
username = auth[0]
password = auth[1]
username = auth["username"]
password = auth["password"]
resolved_client_cert = client_cert or get_client_cert(
self._poetry.config, repository_name
......
......@@ -12,7 +12,6 @@ class PackageInclude(Include):
base = base / source
super(PackageInclude, self).__init__(base, include, formats=formats)
self.check_elements()
@property
......@@ -35,6 +34,8 @@ class PackageInclude(Include):
return self.check_elements()
def check_elements(self): # type: () -> PackageInclude
root = self._elements[0]
if not self._elements:
raise ValueError(
"{} does not contain any element".format(self._base / self._include)
......@@ -44,20 +45,24 @@ class PackageInclude(Include):
# Probably glob
self._is_package = True
# The __init__.py file should be first
root = self._elements[0]
if root.name != "__init__.py":
raise ValueError("{} is not a package.".format(root))
# Packages no longer need an __init__.py in python3, but there must
# at least be one .py file for it to be considered a package
if not any([element.suffix == ".py" for element in self._elements]):
raise ValueError("{} is not a package.".format(root.name))
self._package = root.parent.name
else:
if self._elements[0].is_dir():
if root.is_dir():
# If it's a directory, we include everything inside it
self._package = self._elements[0].name
self._elements = sorted(list(self._elements[0].glob("**/*")))
self._package = root.name
self._elements = sorted(list(root.glob("**/*")))
if not any([element.suffix == ".py" for element in self._elements]):
raise ValueError("{} is not a package.".format(root.name))
self._is_package = True
else:
self._package = self._elements[0].stem
self._package = root.stem
self._is_module = True
return self
......@@ -31,18 +31,27 @@ class _Writer:
def write(self):
buffer = []
required_python_version = None
required_python_version_notification = False
for incompatibility in self._root.external_incompatibilities:
if isinstance(incompatibility.cause, PythonCause):
required_python_version = incompatibility.cause.root_python_version
break
if not required_python_version_notification:
buffer.append(
"The current project's Python requirement ({}) "
"is not compatible with some of the required "
"packages Python requirement:".format(
incompatibility.cause.root_python_version
)
)
required_python_version_notification = True
if required_python_version is not None:
buffer.append(
"The current project must support the following Python versions: {}".format(
required_python_version
buffer.append(
" - {} requires Python {}".format(
incompatibility.terms[0].dependency.name,
incompatibility.cause.python_version,
)
)
)
if required_python_version_notification:
buffer.append("")
if isinstance(self._root.cause, ConflictCause):
......
......@@ -25,6 +25,8 @@ from .vcs_dependency import VCSDependency
def dependency_from_pep_508(name):
from poetry.vcs.git import ParsedUrl
# Removing comments
parts = name.split("#", 1)
name = parts[0].strip()
......@@ -46,6 +48,8 @@ def dependency_from_pep_508(name):
if is_url(name):
link = Link(name)
elif req.url:
link = Link(req.url)
else:
p, extras = strip_extras(path)
if os.path.isdir(p) and (os.path.sep in name or name.startswith(".")):
......@@ -74,10 +78,15 @@ def dependency_from_pep_508(name):
version = m.group("ver")
dep = Dependency(name, version)
else:
name = link.egg_fragment
name = req.name or link.egg_fragment
if link.scheme == "git":
if link.scheme.startswith("git+"):
url = ParsedUrl.parse(link.url)
dep = VCSDependency(name, "git", url.url, rev=url.rev)
elif link.scheme == "git":
dep = VCSDependency(name, "git", link.url_without_fragment)
elif link.scheme in ["http", "https"]:
dep = URLDependency(name, link.url_without_fragment)
else:
dep = Dependency(name, "*")
else:
......
......@@ -55,6 +55,7 @@ class Dependency(object):
self._python_constraint = parse_constraint("*")
self._transitive_python_versions = None
self._transitive_python_constraint = None
self._transitive_marker = None
self._extras = []
self._in_extras = []
......@@ -118,6 +119,17 @@ class Dependency(object):
self._transitive_python_constraint = parse_constraint(value)
@property
def transitive_marker(self):
if self._transitive_marker is None:
return self.marker
return self._transitive_marker
@transitive_marker.setter
def transitive_marker(self, value):
self._transitive_marker = value
@property
def python_constraint(self):
return self._python_constraint
......
......@@ -74,6 +74,17 @@ class DirectoryDependency(Dependency):
def develop(self):
return self._develop
@property
def base_pep_508_name(self): # type: () -> str
requirement = self.pretty_name
if self.extras:
requirement += "[{}]".format(",".join(self.extras))
requirement += " @ {}".format(self._path)
return requirement
def supports_poetry(self):
return self._supports_poetry
......
......@@ -49,6 +49,17 @@ class FileDependency(Dependency):
def full_path(self):
return self._full_path.resolve()
@property
def base_pep_508_name(self): # type: () -> str
requirement = self.pretty_name
if self.extras:
requirement += "[{}]".format(",".join(self.extras))
requirement += " @ {}".format(self._path)
return requirement
def is_file(self):
return True
......
......@@ -8,6 +8,7 @@ from tomlkit import document
from tomlkit import inline_table
from tomlkit import item
from tomlkit import table
from tomlkit.exceptions import TOMLKitError
import poetry.packages
import poetry.repositories
......@@ -137,8 +138,11 @@ class Locker(object):
package.add_dependency(dep_name, constraint)
if "develop" in info:
package.develop = info["develop"]
if "source" in info:
package.source_type = info["source"]["type"]
package.source_type = info["source"].get("type", "")
package.source_url = info["source"]["url"]
package.source_reference = info["source"]["reference"]
......@@ -217,7 +221,10 @@ class Locker(object):
if not self._lock.exists():
raise RuntimeError("No lockfile found. Unable to read locked packages")
return self._lock.read()
try:
return self._lock.read()
except TOMLKitError as e:
raise RuntimeError("Unable to read the lock file ({}).".format(e))
def _lock_packages(
self, packages
......@@ -290,11 +297,14 @@ class Locker(object):
data["dependencies"] = dependencies
if package.source_type:
if package.source_url:
data["source"] = {
"type": package.source_type,
"url": package.source_url,
"reference": package.source_reference,
}
if package.source_type:
data["source"]["type"] = package.source_type
if package.source_type == "directory":
data["develop"] = package.develop
return data
......@@ -279,7 +279,7 @@ class Package(object):
message = (
'The "{}" dependency specifies '
'the "allows-prereleases" property, which is deprecated. '
'Use "allow-preleases" instead.'.format(name)
'Use "allow-prereleases" instead.'.format(name)
)
warn(message, DeprecationWarning)
logger.warning(message)
......@@ -297,6 +297,7 @@ class Package(object):
branch=constraint.get("branch", None),
tag=constraint.get("tag", None),
rev=constraint.get("rev", None),
category=category,
optional=optional,
)
elif "file" in constraint:
......
......@@ -5,8 +5,13 @@ import re
from poetry.packages.constraints.constraint import Constraint
from poetry.packages.constraints.multi_constraint import MultiConstraint
from poetry.packages.constraints.union_constraint import UnionConstraint
from poetry.semver import EmptyConstraint
from poetry.semver import Version
from poetry.semver import VersionConstraint
from poetry.semver import VersionRange
from poetry.semver import VersionUnion
from poetry.semver import parse_constraint
from poetry.version.markers import BaseMarker
from poetry.version.markers import MarkerUnion
from poetry.version.markers import MultiMarker
from poetry.version.markers import SingleMarker
......@@ -236,3 +241,66 @@ def create_nested_marker(name, constraint):
marker = '{} {} "{}"'.format(name, op, version)
return marker
def get_python_constraint_from_marker(
marker,
): # type: (BaseMarker) -> VersionConstraint
python_marker = marker.only("python_version")
if python_marker.is_any():
return VersionRange()
if python_marker.is_empty():
return EmptyConstraint()
markers = convert_markers(marker)
ors = []
for or_ in markers["python_version"]:
ands = []
for op, version in or_:
# Expand python version
if op == "==":
version = "~" + version
op = ""
elif op == "!=":
version += ".*"
elif op in ("<=", ">"):
parsed_version = Version.parse(version)
if parsed_version.precision == 1:
if op == "<=":
op = "<"
version = parsed_version.next_major.text
elif op == ">":
op = ">="
version = parsed_version.next_major.text
elif parsed_version.precision == 2:
if op == "<=":
op = "<"
version = parsed_version.next_minor.text
elif op == ">":
op = ">="
version = parsed_version.next_minor.text
elif op in ("in", "not in"):
versions = []
for v in re.split("[ ,]+", version):
split = v.split(".")
if len(split) in [1, 2]:
split.append("*")
op_ = "" if op == "in" else "!="
else:
op_ = "==" if op == "in" else "!="
versions.append(op_ + ".".join(split))
glue = " || " if op == "in" else ", "
if versions:
ands.append(glue.join(versions))
continue
ands.append("{}{}".format(op, version))
ors.append(" ".join(ands))
return parse_constraint(" || ".join(ors))
from poetry.vcs import git
from .dependency import Dependency
......@@ -7,7 +9,15 @@ class VCSDependency(Dependency):
"""
def __init__(
self, name, vcs, source, branch=None, tag=None, rev=None, optional=False
self,
name,
vcs,
source,
branch=None,
tag=None,
rev=None,
category="main",
optional=False,
):
self._vcs = vcs
self._source = source
......@@ -21,7 +31,7 @@ class VCSDependency(Dependency):
self._rev = rev
super(VCSDependency, self).__init__(
name, "*", optional=optional, allows_prereleases=True
name, "*", category=category, optional=optional, allows_prereleases=True
)
@property
......@@ -65,11 +75,17 @@ class VCSDependency(Dependency):
@property
def base_pep_508_name(self): # type: () -> str
requirement = self.pretty_name
parsed_url = git.ParsedUrl.parse(self._source)
if self.extras:
requirement += "[{}]".format(",".join(self.extras))
requirement += " @ {}+{}@{}".format(self._vcs, self._source, self.reference)
if parsed_url.protocol is not None:
requirement += " @ {}+{}@{}".format(self._vcs, self._source, self.reference)
else:
requirement += " @ {}+ssh://{}@{}".format(
self._vcs, parsed_url.format(), self.reference
)
return requirement
......
......@@ -6,6 +6,7 @@ import time
from contextlib import contextmanager
from tempfile import mkdtemp
from typing import Any
from typing import List
from typing import Optional
......@@ -27,6 +28,7 @@ from poetry.packages import PackageCollection
from poetry.packages import URLDependency
from poetry.packages import VCSDependency
from poetry.packages import dependency_from_pep_508
from poetry.packages.utils.utils import get_python_constraint_from_marker
from poetry.repositories import Pool
from poetry.utils._compat import PY35
from poetry.utils._compat import OrderedDict
......@@ -61,9 +63,7 @@ class Provider:
UNSAFE_PACKAGES = {"setuptools", "distribute", "pip"}
def __init__(
self, package, pool, io # type: Package # type: Pool
): # type: (...) -> None
def __init__(self, package, pool, io): # type: (Package, Pool, Any) -> None
self._package = package
self._pool = pool
self._io = io
......@@ -275,6 +275,7 @@ class Provider:
)
package.source_url = dependency.path.as_posix()
package.develop = dependency.develop
if dependency.base is not None:
package.root_dir = dependency.base.as_posix()
......@@ -489,14 +490,15 @@ class Provider:
if not package.python_constraint.allows_all(
self._package.python_constraint
):
intersection = package.python_constraint.intersect(
package.dependency.transitive_python_constraint
transitive_python_constraint = get_python_constraint_from_marker(
package.dependency.transitive_marker
)
difference = package.dependency.transitive_python_constraint.difference(
intersection
intersection = package.python_constraint.intersect(
transitive_python_constraint
)
difference = transitive_python_constraint.difference(intersection)
if (
package.dependency.transitive_python_constraint.is_any()
transitive_python_constraint.is_any()
or self._package.python_constraint.intersect(
package.dependency.python_constraint
).is_empty()
......@@ -604,7 +606,7 @@ class Provider:
new_markers = []
for dep in _deps:
marker = dep.marker.without_extras()
if marker.is_empty():
if marker.is_any():
# No marker or only extras
continue
......@@ -671,13 +673,28 @@ class Provider:
raise CompatibilityError(*python_constraints)
# Modifying dependencies as needed
clean_dependencies = []
for dep in dependencies:
if not package.dependency.transitive_marker.without_extras().is_any():
marker_intersection = package.dependency.transitive_marker.without_extras().intersect(
dep.marker.without_extras()
)
if marker_intersection.is_empty():
# The dependency is not needed, since the markers specified
# for the current package selection are not compatible with
# the markers for the current dependency, so we skip it
continue
dep.transitive_marker = marker_intersection
if not package.dependency.python_constraint.is_any():
dep.transitive_python_versions = str(
dep.python_constraint.intersect(
package.dependency.python_constraint
)
python_constraint_intersection = dep.python_constraint.intersect(
package.dependency.python_constraint
)
if python_constraint_intersection.is_empty():
# This dependency is not needed under current python constraint.
continue
dep.transitive_python_versions = str(python_constraint_intersection)
if (package.dependency.is_directory() or package.dependency.is_file()) and (
dep.is_directory() or dep.is_file()
......@@ -691,8 +708,9 @@ class Provider:
# TODO: Improve the way we set the correct relative path for dependencies
dep._path = relative
clean_dependencies.append(dep)
package.requires = dependencies
package.requires = clean_dependencies
return package
......
......@@ -56,12 +56,20 @@ class Solver:
installed = True
if pkg.source_type == "git" and package.source_type == "git":
from poetry.vcs.git import Git
# Trying to find the currently installed version
pkg_source_url = Git.normalize_url(pkg.source_url)
package_source_url = Git.normalize_url(package.source_url)
for locked in self._locked.packages:
if locked.name != pkg.name or locked.source_type != "git":
continue
locked_source_url = Git.normalize_url(locked.source_url)
if (
locked.name == pkg.name
and locked.source_type == pkg.source_type
and locked.source_url == pkg.source_url
and locked_source_url == pkg_source_url
and locked.source_reference == pkg.source_reference
):
pkg = Package(pkg.name, locked.version)
......@@ -70,7 +78,7 @@ class Solver:
pkg.source_reference = locked.source_reference
break
if pkg.source_url != package.source_url or (
if pkg_source_url != package_source_url or (
pkg.source_reference != package.source_reference
and not pkg.source_reference.startswith(
package.source_reference
......@@ -84,6 +92,8 @@ class Solver:
elif package.version != pkg.version:
# Checking version
operations.append(Update(pkg, package))
elif package.source_type != pkg.source_type:
operations.append(Update(pkg, package))
else:
operations.append(Install(package).skip("Already installed"))
......@@ -212,8 +222,12 @@ class Solver:
else:
category = dep.category
optional = dep.is_optional() and not dep.is_activated()
intersection = previous["marker"].intersect(previous_dep.marker)
intersection = intersection.intersect(package.marker)
intersection = (
previous["marker"]
.without_extras()
.intersect(previous_dep.transitive_marker.without_extras())
)
intersection = intersection.intersect(package.marker.without_extras())
marker = intersection
......
import re
from poetry.packages import Package
from poetry.utils._compat import Path
from poetry.utils._compat import metadata
from poetry.utils.env import Env
from .repository import Repository
......@@ -15,30 +15,52 @@ class InstalledRepository(Repository):
For now, it uses the pip "freeze" command.
"""
repo = cls()
seen = set()
for entry in env.sys_path:
for distribution in sorted(
metadata.distributions(path=[entry]), key=lambda d: str(d._path),
):
name = distribution.metadata["name"]
version = distribution.metadata["version"]
package = Package(name, version, version)
package.description = distribution.metadata.get("summary", "")
if package.name in seen:
continue
seen.add(package.name)
repo.add_package(package)
path = Path(str(distribution._path))
is_standard_package = True
try:
path.relative_to(env.site_packages)
except ValueError:
is_standard_package = False
if is_standard_package:
continue
src_path = env.path / "src"
# A VCS dependency should have been installed
# in the src directory. If not, it's a path dependency
try:
path.relative_to(src_path)
from poetry.vcs.git import Git
git = Git()
revision = git.rev_parse("HEAD", src_path / package.name).strip()
url = git.remote_url(src_path / package.name)
freeze_output = env.run("python", "-m", "pip", "freeze")
for line in freeze_output.split("\n"):
if "==" in line:
name, version = re.split("={2,3}", line)
repo.add_package(Package(name, version, version))
elif line.startswith("-e "):
line = line[3:].strip()
if line.startswith("git+"):
url = line.lstrip("git+")
if "@" in url:
url, rev = url.rsplit("@", 1)
else:
rev = "master"
name = url.split("/")[-1].rstrip(".git")
if "#egg=" in rev:
rev, name = rev.split("#egg=")
package = Package(name, "0.0.0")
package.source_type = "git"
package.source_url = url
package.source_reference = rev
repo.add_package(package)
package.source_reference = revision
except ValueError:
package.source_type = "directory"
package.source_url = str(path.parent)
return repo
......@@ -269,7 +269,6 @@ class LegacyRepository(PyPiRepository):
for version in versions:
package = Package(name, version)
package.source_type = "legacy"
package.source_url = self._url
if extras is not None:
......@@ -314,7 +313,6 @@ class LegacyRepository(PyPiRepository):
if release_info["requires_python"]:
package.python_versions = release_info["requires_python"]
package.source_type = "legacy"
package.source_url = self._url
package.source_reference = self.name
......
......@@ -8,10 +8,12 @@ from typing import Union
from cachecontrol import CacheControl
from cachecontrol.caches.file_cache import FileCache
from cachecontrol.controller import logger as cache_control_logger
from cachy import CacheManager
from html5lib.html5parser import parse
from requests import get
from requests import session
from requests.exceptions import TooManyRedirects
from poetry.locations import CACHE_DIR
from poetry.packages import Package
......@@ -39,12 +41,14 @@ except ImportError:
import urlparse
cache_control_logger.setLevel(logging.ERROR)
logger = logging.getLogger(__name__)
class PyPiRepository(Repository):
CACHE_VERSION = parse_constraint("1.0.0b2")
CACHE_VERSION = parse_constraint("1.0.0")
def __init__(self, url="https://pypi.org/", disable_cache=False, fallback=True):
self._url = url
......@@ -63,9 +67,8 @@ class PyPiRepository(Repository):
}
)
self._session = CacheControl(
session(), cache=FileCache(str(release_cache_dir / "_http"))
)
self._cache_control_cache = FileCache(str(release_cache_dir / "_http"))
self._session = CacheControl(session(), cache=self._cache_control_cache)
self._inspector = Inspector()
super(PyPiRepository, self).__init__()
......@@ -357,7 +360,14 @@ class PyPiRepository(Repository):
return data
def _get(self, endpoint): # type: (str) -> Union[dict, None]
json_response = self._session.get(self._url + endpoint)
try:
json_response = self._session.get(self._url + endpoint)
except TooManyRedirects:
# Cache control redirect loop.
# We try to remove the cache and try again
self._cache_control_cache.delete(self._url + endpoint)
json_response = self._session.get(self._url + endpoint)
if json_response.status_code == 404:
return None
......
......@@ -76,14 +76,12 @@ def parse_single_constraint(constraint): # type: (str) -> VersionConstraint
version = Version.parse(m.group(1))
if precision == 2:
low = version
high = version.stable.next_major
else:
low = Version(version.major, version.minor, version.patch)
high = version.stable.next_minor
return VersionRange(
low, high, include_min=True, always_include_max_prerelease=True
version, high, include_min=True, always_include_max_prerelease=True
)
# Caret range
......
......@@ -12,6 +12,13 @@ except ImportError:
from glob import glob
try:
from importlib import metadata
import zipfile as zipp
except ImportError:
import importlib_metadata as metadata
import zipp
try:
import urllib.parse as urlparse
except ImportError:
import urlparse
......
......@@ -94,6 +94,13 @@ import sys
print('.'.join([str(s) for s in sys.version_info[:3]]))
"""
GET_SYS_PATH = """\
import json
import sys
print(json.dumps(sys.path))
"""
CREATE_VENV_COMMAND = """\
path = {!r}
......@@ -183,7 +190,7 @@ class EnvManager(object):
try:
python_version = decode(
subprocess.check_output(
" ".join(
list_to_shell_command(
[
python,
"-c",
......@@ -201,6 +208,24 @@ class EnvManager(object):
patch = python_version.text
create = False
is_root_venv = self._poetry.config.get("virtualenvs.in-project")
# If we are required to create the virtual environment in the root folder,
# create or recreate it if needed
if is_root_venv:
create = False
venv = self._poetry.file.parent / ".venv"
if venv.exists():
# We need to check if the patch version is correct
_venv = VirtualEnv(venv)
current_patch = ".".join(str(v) for v in _venv.version_info[:3])
if patch != current_patch:
create = True
self.create_venv(io, executable=python, force=create)
return self.get(reload=True)
envs = tomlkit.document()
base_env_name = self.generate_env_name(self._poetry.package.name, str(cwd))
if envs_file.exists():
......@@ -402,7 +427,7 @@ class EnvManager(object):
try:
python_version = decode(
subprocess.check_output(
" ".join(
list_to_shell_command(
[
python,
"-c",
......@@ -448,6 +473,10 @@ class EnvManager(object):
cwd = self._poetry.file.parent
env = self.get(reload=True)
if not env.is_sane():
force = True
if env.is_venv() and not force:
# Already inside a virtualenv.
return env
......@@ -469,21 +498,22 @@ class EnvManager(object):
python_patch = ".".join([str(v) for v in sys.version_info[:3]])
python_minor = ".".join([str(v) for v in sys.version_info[:2]])
if executable:
python_minor = decode(
python_patch = decode(
subprocess.check_output(
" ".join(
list_to_shell_command(
[
executable,
"-c",
"\"import sys; print('.'.join([str(s) for s in sys.version_info[:2]]))\"",
"\"import sys; print('.'.join([str(s) for s in sys.version_info[:3]]))\"",
]
),
shell=True,
).strip()
)
python_minor = ".".join(python_patch.split(".")[:2])
supported_python = self._poetry.package.python_constraint
if not supported_python.allows(Version.parse(python_minor)):
if not supported_python.allows(Version.parse(python_patch)):
# The currently activated or chosen Python version
# is not compatible with the Python constraint specified
# for the project.
......@@ -492,7 +522,7 @@ class EnvManager(object):
# Otherwise, we try to find a compatible Python version.
if executable:
raise NoCompatiblePythonVersionFound(
self._poetry.package.python_versions, python_minor
self._poetry.package.python_versions, python_patch
)
io.write_line(
......@@ -527,7 +557,7 @@ class EnvManager(object):
try:
python_patch = decode(
subprocess.check_output(
" ".join(
list_to_shell_command(
[
python,
"-c",
......@@ -580,6 +610,12 @@ class EnvManager(object):
self.build_venv(str(venv), executable=executable)
else:
if force:
if not env.is_sane():
io.write_line(
"<warning>The virtual environment found in {} seems to be broken.</warning>".format(
env.path
)
)
io.write_line(
"Recreating virtualenv <c1>{}</> in {}".format(name, str(venv))
)
......@@ -612,7 +648,9 @@ class EnvManager(object):
# Create virtualenv by using an external executable
try:
p = subprocess.Popen(
" ".join([executable, "-"]), stdin=subprocess.PIPE, shell=True
list_to_shell_command([executable, "-"]),
stdin=subprocess.PIPE,
shell=True,
)
p.communicate(encode(CREATE_VENV_COMMAND.format(path)))
except CalledProcessError as e:
......@@ -677,6 +715,7 @@ class Env(object):
self._marker_env = None
self._pip_version = None
self._site_packages = None
@property
def path(self): # type: () -> Path
......@@ -732,15 +771,29 @@ class Env(object):
@property
def site_packages(self): # type: () -> Path
if self._is_windows:
return self._path / "Lib" / "site-packages"
return (
self._path
/ "lib"
/ "python{}.{}".format(*self.version_info[:2])
/ "site-packages"
)
if self._site_packages is None:
site_packages = []
dist_packages = []
for entry in self.sys_path:
entry = Path(entry)
if entry.name == "site-packages":
site_packages.append(entry)
elif entry.name == "dist-packages":
dist_packages.append(entry)
if not site_packages and not dist_packages:
raise RuntimeError("Unable to find the site-packages directory")
if site_packages:
self._site_packages = site_packages[0]
else:
self._site_packages = dist_packages[0]
return self._site_packages
@property
def sys_path(self): # type: () -> List[str]
raise NotImplementedError()
@classmethod
def get_base_prefix(cls): # type: () -> Path
......@@ -761,6 +814,9 @@ class Env(object):
def get_marker_env(self): # type: () -> Dict[str, Any]
raise NotImplementedError()
def get_pip_command(self): # type: () -> List[str]
raise NotImplementedError()
def config_var(self, var): # type: (str) -> Any
raise NotImplementedError()
......@@ -777,12 +833,19 @@ class Env(object):
return True
def run(self, bin, *args, **kwargs):
bin = self._bin(bin)
cmd = [bin] + list(args)
return self._run(cmd, **kwargs)
def run_pip(self, *args, **kwargs):
pip = self.get_pip_command()
cmd = pip + list(args)
return self._run(cmd, **kwargs)
def _run(self, cmd, **kwargs):
"""
Run a command inside the Python environment.
"""
bin = self._bin(bin)
cmd = [bin] + list(args)
shell = kwargs.get("shell", False)
call = kwargs.pop("call", False)
input_ = kwargs.pop("input_", None)
......@@ -865,12 +928,21 @@ class SystemEnv(Env):
A system (i.e. not a virtualenv) Python environment.
"""
@property
def sys_path(self): # type: () -> List[str]
return sys.path
def get_version_info(self): # type: () -> Tuple[int]
return sys.version_info
def get_python_implementation(self): # type: () -> str
return platform.python_implementation()
def get_pip_command(self): # type: () -> List[str]
# If we're not in a venv, assume the interpreter we're running on
# has a pip and use that
return [sys.executable, "-m", "pip"]
def get_marker_env(self): # type: () -> Dict[str, Any]
if hasattr(sys, "implementation"):
info = sys.implementation.version
......@@ -931,6 +1003,12 @@ class VirtualEnv(Env):
if base is None:
self._base = Path(self.run("python", "-", input_=GET_BASE_PREFIX).strip())
@property
def sys_path(self): # type: () -> List[str]
output = self.run("python", "-", input_=GET_SYS_PATH)
return json.loads(output)
def get_version_info(self): # type: () -> Tuple[int]
output = self.run("python", "-", input_=GET_PYTHON_VERSION)
......@@ -939,6 +1017,11 @@ class VirtualEnv(Env):
def get_python_implementation(self): # type: () -> str
return self.marker_env["platform_python_implementation"]
def get_pip_command(self): # type: () -> List[str]
# We're in a virtualenv that is known to be sane,
# so assume that we have a functional pip
return [self._bin("pip")]
def get_marker_env(self): # type: () -> Dict[str, Any]
output = self.run("python", "-", input_=GET_ENVIRONMENT_INFO)
......@@ -963,7 +1046,7 @@ class VirtualEnv(Env):
return value
def get_pip_version(self): # type: () -> Version
output = self.run("python", "-m", "pip", "--version").strip()
output = self.run_pip("--version").strip()
m = re.match("pip (.+?)(?: from .+)?$", output)
if not m:
return Version.parse("0.0")
......@@ -977,7 +1060,7 @@ class VirtualEnv(Env):
# A virtualenv is considered sane if both "python" and "pip" exist.
return os.path.exists(self._bin("python")) and os.path.exists(self._bin("pip"))
def run(self, bin, *args, **kwargs):
def _run(self, cmd, **kwargs):
with self.temp_environ():
os.environ["PATH"] = self._updated_path()
os.environ["VIRTUAL_ENV"] = str(self._path)
......@@ -985,7 +1068,7 @@ class VirtualEnv(Env):
self.unset_env("PYTHONHOME")
self.unset_env("__PYVENV_LAUNCHER__")
return super(VirtualEnv, self).run(bin, *args, **kwargs)
return super(VirtualEnv, self)._run(cmd, **kwargs)
def execute(self, bin, *args, **kwargs):
with self.temp_environ():
......@@ -1024,11 +1107,11 @@ class NullEnv(SystemEnv):
self._execute = execute
self.executed = []
def run(self, bin, *args, **kwargs):
self.executed.append([bin] + list(args))
def _run(self, cmd, **kwargs):
self.executed.append(cmd)
if self._execute:
return super(NullEnv, self).run(bin, *args, **kwargs)
return super(NullEnv, self)._run(cmd, **kwargs)
def execute(self, bin, *args, **kwargs):
self.executed.append([bin] + list(args))
......@@ -1049,6 +1132,7 @@ class MockEnv(NullEnv):
os_name="posix",
is_venv=False,
pip_version="19.1",
sys_path=None,
**kwargs
):
super(MockEnv, self).__init__(**kwargs)
......@@ -1059,6 +1143,7 @@ class MockEnv(NullEnv):
self._os_name = os_name
self._is_venv = is_venv
self._pip_version = Version.parse(pip_version)
self._sys_path = sys_path
@property
def version_info(self): # type: () -> Tuple[int]
......@@ -1080,5 +1165,12 @@ class MockEnv(NullEnv):
def pip_version(self):
return self._pip_version
@property
def sys_path(self):
if self._sys_path is None:
return super(MockEnv, self).sys_path
return self._sys_path
def is_venv(self): # type: () -> bool
return self._is_venv
......@@ -54,7 +54,7 @@ class Exporter(object):
extras=None,
with_credentials=False,
): # type: (Path, Union[IO, str], bool, bool, bool) -> None
indexes = []
indexes = set()
content = ""
packages = self._poetry.locker.locked_repository(dev).packages
......@@ -94,7 +94,7 @@ class Exporter(object):
dependency.marker = package.marker
line = "{}".format(package.source_url)
if package.develop:
if package.develop and package.source_type == "directory":
line = "-e " + line
else:
dependency = package.to_dependency()
......@@ -104,8 +104,11 @@ class Exporter(object):
if ";" in requirement:
line += "; {}".format(requirement.split(";")[1].strip())
if package.source_type == "legacy" and package.source_url:
indexes.append(package.source_url)
if (
package.source_type not in {"git", "directory", "file", "url"}
and package.source_url
):
indexes.add(package.source_url)
if package.files and with_hashes:
hashes = []
......@@ -131,10 +134,9 @@ class Exporter(object):
content += line
if indexes:
# If we have extra indexes, we add them to the begin
# of the output
# If we have extra indexes, we add them to the beginning of the output
indexes_header = ""
for index in indexes:
for index in sorted(indexes):
repository = [
r
for r in self._poetry.pool.repositories
......
......@@ -8,11 +8,6 @@ from contextlib import contextmanager
from typing import List
from typing import Optional
from keyring import delete_password
from keyring import get_password
from keyring import set_password
from keyring.errors import KeyringError
from poetry.config.config import Config
from poetry.utils._compat import Path
from poetry.version import Version
......@@ -95,53 +90,6 @@ def parse_requires(requires): # type: (str) -> List[str]
return requires_dist
def keyring_service_name(repository_name): # type: (str) -> str
return "{}-{}".format("poetry-repository", repository_name)
def keyring_repository_password_get(
repository_name, username
): # type: (str, str) -> Optional[str]
try:
return get_password(keyring_service_name(repository_name), username)
except (RuntimeError, KeyringError):
return None
def keyring_repository_password_set(
repository_name, username, password
): # type: (str, str, str) -> None
try:
set_password(keyring_service_name(repository_name), username, password)
except (RuntimeError, KeyringError):
raise RuntimeError("Failed to store password in keyring")
def keyring_repository_password_del(
config, repository_name
): # type: (Config, str) -> None
try:
repo_auth = config.get("http-basic.{}".format(repository_name))
if repo_auth and "username" in repo_auth:
delete_password(
keyring_service_name(repository_name), repo_auth["username"]
)
except (RuntimeError, KeyringError):
pass
def get_http_basic_auth(
config, repository_name
): # type: (Config, str) -> Optional[tuple]
repo_auth = config.get("http-basic.{}".format(repository_name))
if repo_auth:
username, password = repo_auth["username"], repo_auth.get("password")
if password is None:
password = keyring_repository_password_get(repository_name, username)
return username, password
return None
def get_cert(config, repository_name): # type: (Config, str) -> Optional[Path]
cert = config.get("certificates.{}.cert".format(repository_name))
if cert:
......@@ -159,11 +107,17 @@ def get_client_cert(config, repository_name): # type: (Config, str) -> Optional
def _on_rm_error(func, path, exc_info):
if not os.path.exists(path):
return
os.chmod(path, stat.S_IWRITE)
func(path)
def safe_rmtree(path):
if Path(path).is_symlink():
return os.unlink(str(path))
shutil.rmtree(path, onerror=_on_rm_error)
......
import logging
logger = logging.getLogger(__name__)
class PasswordManagerError(Exception):
pass
class KeyRingError(Exception):
pass
class KeyRing:
def __init__(self, namespace):
self._namespace = namespace
self._is_available = True
self._check()
def is_available(self):
return self._is_available
def get_password(self, name, username):
if not self.is_available():
return
import keyring
import keyring.errors
name = self.get_entry_name(name)
try:
return keyring.get_password(name, username)
except (RuntimeError, keyring.errors.KeyringError):
raise KeyRingError(
"Unable to retrieve the password for {} from the key ring".format(name)
)
def set_password(self, name, username, password):
if not self.is_available():
return
import keyring
import keyring.errors
name = self.get_entry_name(name)
try:
keyring.set_password(name, username, password)
except (RuntimeError, keyring.errors.KeyringError) as e:
raise KeyRingError(
"Unable to store the password for {} in the key ring: {}".format(
name, str(e)
)
)
def delete_password(self, name, username):
if not self.is_available():
return
import keyring
import keyring.errors
name = self.get_entry_name(name)
try:
keyring.delete_password(name, username)
except (RuntimeError, keyring.errors.KeyringError):
raise KeyRingError(
"Unable to delete the password for {} from the key ring".format(name)
)
def get_entry_name(self, name):
return "{}-{}".format(self._namespace, name)
def _check(self):
try:
import keyring
except Exception as e:
logger.debug("An error occurred while importing keyring: {}".format(str(e)))
self._is_available = False
return
backend = keyring.get_keyring()
name = backend.name.split(" ")[0]
if name == "fail":
logger.debug("No suitable keyring backend found")
self._is_available = False
elif "plaintext" in backend.name.lower():
logger.debug("Only a plaintext keyring backend is available. Not using it.")
self._is_available = False
elif name == "chainer":
try:
import keyring.backend
backends = keyring.backend.get_all_keyring()
self._is_available = any(
[
b.name.split(" ")[0] not in ["chainer", "fail"]
and "plaintext" not in b.name.lower()
for b in backends
]
)
except Exception:
self._is_available = False
if not self._is_available:
logger.warning("No suitable keyring backends were found")
class PasswordManager:
def __init__(self, config):
self._config = config
self._keyring = KeyRing("poetry-repository")
if not self._keyring.is_available():
logger.warning("Using a plaintext file to store and retrieve credentials")
@property
def keyring(self):
return self._keyring
def set_pypi_token(self, name, token):
if not self._keyring.is_available():
self._config.auth_config_source.add_property(
"pypi-token.{}".format(name), token
)
else:
self._keyring.set_password(name, "__token__", token)
def get_pypi_token(self, name):
if not self._keyring.is_available():
return self._config.get("pypi-token.{}".format(name))
return self._keyring.get_password(name, "__token__")
def delete_pypi_token(self, name):
if not self._keyring.is_available():
return self._config.auth_config_source.remove_property(
"pypi-token.{}".format(name)
)
self._keyring.delete_password(name, "__token__")
def get_http_auth(self, name):
auth = self._config.get("http-basic.{}".format(name))
if not auth:
return None
username, password = auth["username"], auth.get("password")
if password is None:
password = self._keyring.get_password(name, username)
return {
"username": username,
"password": password,
}
def set_http_password(self, name, username, password):
auth = {"username": username}
if not self._keyring.is_available():
auth["password"] = password
else:
self._keyring.set_password(name, username, password)
self._config.auth_config_source.add_property("http-basic.{}".format(name), auth)
def delete_http_password(self, name):
auth = self.get_http_auth(name)
if not auth or "username" not in auth:
return
try:
self._keyring.delete_password(name, auth["username"])
except KeyRingError:
pass
self._config.auth_config_source.remove_property("http-basic.{}".format(name))
......@@ -181,10 +181,11 @@ class SetupReader(object):
continue
func = value.func
if not isinstance(func, ast.Name):
continue
if func.id != "setup":
if not (isinstance(func, ast.Name) and func.id == "setup") and not (
isinstance(func, ast.Attribute)
and func.value.id == "setuptools"
and func.attr == "setup"
):
continue
return value, elements
......
......@@ -58,7 +58,9 @@ class Shell:
self._path, ["-i"], dimensions=(terminal.height, terminal.width)
)
c.setecho(False)
if self._name == "zsh":
c.setecho(False)
activate_script = self._get_activate_script()
bin_dir = "Scripts" if WINDOWS else "bin"
activate_path = env.path / bin_dir / activate_script
......
......@@ -2,9 +2,103 @@
import re
import subprocess
from collections import namedtuple
from poetry.utils._compat import decode
PATTERNS = [
re.compile(
r"(git\+)?"
r"((?P<protocol>\w+)://)"
r"((?P<user>\w+)@)?"
r"(?P<resource>[\w.\-]+)"
r"(:(?P<port>\d+))?"
r"(?P<pathname>(/(?P<owner>\w+)/)"
r"((?P<projects>([\w\-/]+)/)?(?P<name>[\w\-]+)(\.git|/)?)?)"
r"([@#](?P<rev>[^@#]+))?"
r"$"
),
re.compile(
r"^(git\+)?"
r"(?P<protocol>https?|git|ssh|rsync|file)://"
r"(?:(?P<user>.+)@)*"
r"(?P<resource>[a-z0-9_.-]*)"
r"(:?P<port>[\d]+)?"
r"(?P<pathname>[:/]((?P<owner>[\w\-]+)/(?P<projects>([\w\-/]+)/)?)?"
r"((?P<name>[\w\-.]+?)(\.git|/)?)?)"
r"([@#](?P<rev>[^@#]+))?"
r"$"
),
re.compile(
r"^(?:(?P<user>.+)@)*"
r"(?P<resource>[a-z0-9_.-]*)[:]*"
r"(?P<port>[\d]+)?"
r"(?P<pathname>/?(?P<owner>.+)/(?P<projects>([\w\-/]+)/)?(?P<name>.+).git)"
r"([@#](?P<rev>[^@#]+))?"
r"$"
),
re.compile(
r"((?P<user>\w+)@)?"
r"(?P<resource>[\w.\-]+)"
r"[:/]{1,2}"
r"(?P<pathname>((?P<owner>\w+)/)?"
r"(?P<projects>([\w\-/]+)/)?"
r"((?P<name>[\w\-]+)(\.git|/)?)?)"
r"([@#](?P<rev>[^@#]+))?"
r"$"
),
]
class ParsedUrl:
def __init__(self, protocol, resource, pathname, user, port, name, rev):
self.protocol = protocol
self.resource = resource
self.pathname = pathname
self.user = user
self.port = port
self.name = name
self.rev = rev
@classmethod
def parse(cls, url): # type: () -> ParsedUrl
for pattern in PATTERNS:
m = pattern.match(url)
if m:
groups = m.groupdict()
return ParsedUrl(
groups.get("protocol"),
groups.get("resource"),
groups.get("pathname"),
groups.get("user"),
groups.get("port"),
groups.get("name"),
groups.get("rev"),
)
raise ValueError('Invalid git url "{}"'.format(url))
@property
def url(self): # type: () -> str
return "{}{}{}{}{}".format(
"{}://".format(self.protocol) if self.protocol else "",
"{}@".format(self.user) if self.user else "",
self.resource,
":{}".format(self.port) if self.port else "",
"/" + self.pathname.lstrip(":/"),
)
def format(self):
return "{}".format(self.url, "#{}".format(self.rev) if self.rev else "",)
def __str__(self): # type: () -> str
return self.format()
GitUrl = namedtuple("GitUrl", ["url", "revision"])
class GitConfig:
def __init__(self, requires_git_presence=False):
self._config = {}
......@@ -36,6 +130,30 @@ class Git:
self._config = GitConfig(requires_git_presence=True)
self._work_dir = work_dir
@classmethod
def normalize_url(cls, url): # type: (str) -> GitUrl
parsed = ParsedUrl.parse(url)
formatted = re.sub(r"^git\+", "", url)
if parsed.rev:
formatted = re.sub(r"[#@]{}$".format(parsed.rev), "", formatted)
altered = parsed.format() != formatted
if altered:
if re.match(r"^git\+https?", url) and re.match(
r"^/?:[^0-9]", parsed.pathname
):
normalized = re.sub(r"git\+(.*:[^:]+):(.*)", "\\1/\\2", url)
elif re.match(r"^git\+file", url):
normalized = re.sub(r"git\+", "", url)
else:
normalized = re.sub(r"^(?:git\+)?ssh://", "", url)
else:
normalized = parsed.format()
return GitUrl(re.sub(r"#[^#]*$", "", normalized), parsed.rev)
@property
def config(self): # type: () -> GitConfig
return self._config
......@@ -93,9 +211,35 @@ class Git:
args += ["ls-files", "--others", "-i", "--exclude-standard"]
output = self.run(*args)
return output.split("\n")
return output.strip().split("\n")
def remote_urls(self, folder=None): # type: (...) -> dict
output = self.run(
"config", "--get-regexp", r"remote\..*\.url", folder=folder
).strip()
urls = {}
for url in output.splitlines():
name, url = url.split(" ", 1)
urls[name.strip()] = url.strip()
return urls
def remote_url(self, folder=None): # type: (...) -> str
urls = self.remote_urls(folder=folder)
return urls.get("remote.origin.url", urls[list(urls.keys())[0]])
def run(self, *args, **kwargs): # type: (...) -> str
folder = kwargs.pop("folder", None)
if folder:
args = (
"--git-dir",
(folder / ".git").as_posix(),
"--work-tree",
folder.as_posix(),
) + args
def run(self, *args): # type: (...) -> str
return decode(
subprocess.check_output(["git"] + list(args), stderr=subprocess.STDOUT)
)
).strip()
......@@ -175,6 +175,12 @@ class BaseMarker(object):
def without_extras(self): # type: () -> BaseMarker
raise NotImplementedError()
def exclude(self, marker_name): # type: (str) -> BaseMarker
raise NotImplementedError()
def only(self, marker_name): # type: (str) -> BaseMarker
raise NotImplementedError()
def __repr__(self):
return "<{} {}>".format(self.__class__.__name__, str(self))
......@@ -198,12 +204,27 @@ class AnyMarker(BaseMarker):
def without_extras(self):
return self
def exclude(self, marker_name): # type: (str) -> AnyMarker
return self
def only(self, marker_name): # type: (str) -> AnyMarker
return self
def __str__(self):
return ""
def __repr__(self):
return "<AnyMarker>"
def __hash__(self):
return hash(("<any>", "<any>"))
def __eq__(self, other):
if not isinstance(other, BaseMarker):
return NotImplemented
return isinstance(other, AnyMarker)
class EmptyMarker(BaseMarker):
def intersect(self, other):
......@@ -224,12 +245,27 @@ class EmptyMarker(BaseMarker):
def without_extras(self):
return self
def exclude(self, marker_name): # type: (str) -> EmptyMarker
return self
def only(self, marker_name): # type: (str) -> EmptyMarker
return self
def __str__(self):
return "<empty>"
def __repr__(self):
return "<EmptyMarker>"
def __hash__(self):
return hash(("<empty>", "<empty>"))
def __eq__(self, other):
if not isinstance(other, BaseMarker):
return NotImplemented
return isinstance(other, EmptyMarker)
class SingleMarker(BaseMarker):
......@@ -329,7 +365,7 @@ class SingleMarker(BaseMarker):
if self == other:
return self
return MarkerUnion(self, other)
return MarkerUnion.of(self, other)
return other.union(self)
......@@ -343,7 +379,16 @@ class SingleMarker(BaseMarker):
return self._constraint.allows(self._parser(environment[self._name]))
def without_extras(self):
if self.name == "extra":
return self.exclude("extra")
def exclude(self, marker_name): # type: (str) -> BaseMarker
if self.name == marker_name:
return AnyMarker()
return self
def only(self, marker_name): # type: (str) -> BaseMarker
if self.name != marker_name:
return EmptyMarker()
return self
......@@ -392,7 +437,7 @@ class MultiMarker(BaseMarker):
markers = _flatten_markers(markers, MultiMarker)
for marker in markers:
if marker in new_markers or marker.is_empty():
if marker in new_markers:
continue
if isinstance(marker, SingleMarker):
......@@ -408,11 +453,9 @@ class MultiMarker(BaseMarker):
intersection = mark.constraint.intersect(marker.constraint)
if intersection == mark.constraint:
intersected = True
break
elif intersection == marker.constraint:
new_markers[i] = marker
intersected = True
break
elif intersection.is_empty():
return EmptyMarker()
......@@ -421,9 +464,12 @@ class MultiMarker(BaseMarker):
new_markers.append(marker)
if not new_markers:
if any(m.is_empty() for m in new_markers) or not new_markers:
return EmptyMarker()
if len(new_markers) == 1 and new_markers[0].is_any():
return AnyMarker()
return MultiMarker(*new_markers)
@property
......@@ -443,7 +489,7 @@ class MultiMarker(BaseMarker):
def union(self, other):
if isinstance(other, (SingleMarker, MultiMarker)):
return MarkerUnion(self, other)
return MarkerUnion.of(self, other)
return other.union(self)
......@@ -455,10 +501,32 @@ class MultiMarker(BaseMarker):
return True
def without_extras(self):
return self.exclude("extra")
def exclude(self, marker_name): # type: (str) -> BaseMarker
new_markers = []
for m in self._markers:
marker = m.without_extras()
if isinstance(m, SingleMarker) and m.name == marker_name:
# The marker is not relevant since it must be excluded
continue
marker = m.exclude(marker_name)
if not marker.is_empty():
new_markers.append(marker)
return self.of(*new_markers)
def only(self, marker_name): # type: (str) -> BaseMarker
new_markers = []
for m in self._markers:
if isinstance(m, SingleMarker) and m.name != marker_name:
# The marker is not relevant since it's not one we want
continue
marker = m.only(marker_name)
if not marker.is_empty():
new_markers.append(marker)
......@@ -493,17 +561,24 @@ class MultiMarker(BaseMarker):
class MarkerUnion(BaseMarker):
def __init__(self, *markers):
self._markers = []
self._markers = list(markers)
@property
def markers(self):
return self._markers
markers = _flatten_markers(markers, MarkerUnion)
@classmethod
def of(cls, *markers): # type: (tuple) -> MarkerUnion
flattened_markers = _flatten_markers(markers, MarkerUnion)
for marker in markers:
if marker in self._markers:
markers = []
for marker in flattened_markers:
if marker in markers:
continue
if isinstance(marker, SingleMarker) and marker.name == "python_version":
intersected = False
for i, mark in enumerate(self._markers):
for i, mark in enumerate(markers):
if (
not isinstance(mark, SingleMarker)
or isinstance(mark, SingleMarker)
......@@ -516,18 +591,19 @@ class MarkerUnion(BaseMarker):
intersected = True
break
elif intersection == marker.constraint:
self._markers[i] = marker
markers[i] = marker
intersected = True
break
if intersected:
continue
self._markers.append(marker)
markers.append(marker)
@property
def markers(self):
return self._markers
if any(m.is_any() for m in markers):
return AnyMarker()
return MarkerUnion(*markers)
def append(self, marker):
if marker in self._markers:
......@@ -557,7 +633,7 @@ class MarkerUnion(BaseMarker):
if not intersection.is_empty():
new_markers.append(intersection)
return MarkerUnion(*new_markers)
return MarkerUnion.of(*new_markers)
def union(self, other):
if other.is_any():
......@@ -568,7 +644,7 @@ class MarkerUnion(BaseMarker):
new_markers = self._markers + [other]
return MarkerUnion(*new_markers)
return MarkerUnion.of(*new_markers)
def validate(self, environment):
for m in self._markers:
......@@ -578,15 +654,37 @@ class MarkerUnion(BaseMarker):
return False
def without_extras(self):
return self.exclude("extra")
def exclude(self, marker_name): # type: (str) -> BaseMarker
new_markers = []
for m in self._markers:
marker = m.without_extras()
if isinstance(m, SingleMarker) and m.name == marker_name:
# The marker is not relevant since it must be excluded
continue
marker = m.exclude(marker_name)
if not marker.is_empty():
new_markers.append(marker)
return self.of(*new_markers)
def only(self, marker_name): # type: (str) -> BaseMarker
new_markers = []
for m in self._markers:
if isinstance(m, SingleMarker) and m.name != marker_name:
# The marker is not relevant since it's not one we want
continue
marker = m.only(marker_name)
if not marker.is_empty():
new_markers.append(marker)
return MarkerUnion(*new_markers)
return self.of(*new_markers)
def __eq__(self, other):
if not isinstance(other, MarkerUnion):
......@@ -602,7 +700,15 @@ class MarkerUnion(BaseMarker):
return h
def __str__(self):
return " or ".join(str(m) for m in self._markers)
return " or ".join(
str(m) for m in self._markers if not m.is_any() and not m.is_empty()
)
def is_any(self):
return any(m.is_any() for m in self._markers)
def is_empty(self):
return all(m.is_empty() for m in self._markers)
def parse_marker(marker):
......@@ -654,4 +760,4 @@ def _compact_markers(markers):
if len(groups) == 1:
return groups[0]
return MarkerUnion(*groups)
return MarkerUnion.of(*groups)
......@@ -28,7 +28,7 @@ from .markers import parse_marker
try:
import urllib.parse as urlparse
except ImportError:
from urlparse import urlparse
import urlparse
LEGACY_REGEX = r"""
......
......@@ -35,8 +35,7 @@ class VersionSelector(object):
dependency = Dependency(package_name, constraint)
# Select highest version if we have many
package = candidates[0]
package = None
for candidate in candidates:
if (
candidate.is_prerelease()
......@@ -47,9 +46,11 @@ class VersionSelector(object):
continue
# Select highest version of the two
if package.version < candidate.version:
if package is None or package.version < candidate.version:
package = candidate
if package is None:
return False
return package
def find_recommended_require_version(self, package):
......
[tool.poetry]
name = "poetry"
version = "1.0.0b3"
version = "1.0.1"
description = "Python dependency management and packaging made easy."
authors = [
"Sébastien Eustace <sebastien@eustace.io>"
......@@ -9,9 +9,9 @@ license = "MIT"
readme = "README.md"
homepage = "https://poetry.eustace.io/"
repository = "https://github.com/sdispater/poetry"
documentation = "https://poetry.eustace.io/docs"
homepage = "https://python-poetry.org/"
repository = "https://github.com/python-poetry/poetry"
documentation = "https://python-poetry.org/docs"
keywords = ["packaging", "dependency", "poetry"]
......@@ -24,7 +24,7 @@ classifiers = [
[tool.poetry.dependencies]
python = "~2.7 || ^3.4"
cleo = "^0.7.6"
clikit = "^0.4.0"
clikit = "^0.4.1"
requests = "^2.18"
cachy = "^0.3.0"
requests-toolbelt = "^0.8.0"
......@@ -46,15 +46,16 @@ pathlib2 = { version = "^2.3", python = "~2.7 || ~3.4" }
# Use glob2 for Python 2.7 and 3.4
glob2 = { version = "^0.6", python = "~2.7 || ~3.4" }
# Use virtualenv for Python 2.7 since venv does not exist
virtualenv = { version = "^16.0", python = "~2.7" }
virtualenv = { version = "^16.7.9", python = "~2.7" }
# functools32 is needed for Python 2.7
functools32 = { version = "^3.2.3", python = "~2.7" }
keyring = [
{ version = "^18.0", python = "~2.7 || ~3.4" },
{ version = "^19.0", python = "^3.5" }
{ version = "^18.0.1", python = "~2.7 || ~3.4" },
{ version = "^20.0.1", python = "^3.5" }
]
# Use subprocess32 for Python 2.7 and 3.4
subprocess32 = { version = "^3.5", python = "~2.7 || ~3.4" }
importlib-metadata = {version = "~1.1.3", python = "<3.8"}
[tool.poetry.dev-dependencies]
pytest = "^4.1"
......@@ -69,11 +70,20 @@ pre-commit = "^1.10"
tox = "^3.0"
pytest-sugar = "^0.9.2"
httpretty = "^0.9.6"
markdown-include = "^0.5.1"
[tool.poetry.scripts]
poetry = "poetry.console:main"
[build-system]
requires = ["intreehooks"]
build-backend = "intreehooks:loader"
[tool.intreehooks]
build-backend = "poetry.masonry.api"
[tool.isort]
line_length = 88
force_single_line = true
......
......@@ -219,6 +219,9 @@ class MakeReleaseCommand(Command):
subprocess.check_output(
[python, "-V"], stderr=subprocess.STDOUT, shell=WINDOWS
)
if version == "3.4" and WINDOWS:
continue
subprocess.check_output([python, "-m", "pip", "install", "pip", "-U"])
except subprocess.CalledProcessError:
raise RuntimeError("Python {} is not available".format(version))
......
......@@ -10,15 +10,9 @@ import pytest
from poetry.config.config import Config as BaseConfig
from poetry.config.dict_config_source import DictConfigSource
from poetry.utils._compat import PY2
from poetry.utils._compat import WINDOWS
from poetry.utils._compat import Path
try:
import urllib.parse as urlparse
except ImportError:
import urlparse
from tests.helpers import mock_clone
from tests.helpers import mock_download
class Config(BaseConfig):
......@@ -58,6 +52,11 @@ def auth_config_source():
@pytest.fixture
def config(config_source, auth_config_source, mocker):
import keyring
from keyring.backends.fail import Keyring
keyring.set_keyring(Keyring())
c = Config()
c.merge(config_source.config)
c.set_config_source(config_source)
......@@ -69,47 +68,6 @@ def config(config_source, auth_config_source, mocker):
return c
def mock_clone(_, source, dest):
# Checking source to determine which folder we need to copy
parts = urlparse.urlparse(source)
folder = (
Path(__file__).parent
/ "fixtures"
/ "git"
/ parts.netloc
/ parts.path.lstrip("/").rstrip(".git")
)
if dest.exists():
shutil.rmtree(str(dest))
shutil.copytree(str(folder), str(dest))
def mock_download(self, url, dest):
parts = urlparse.urlparse(url)
fixtures = Path(__file__).parent / "fixtures"
fixture = fixtures / parts.path.lstrip("/")
if dest.exists():
os.unlink(str(dest))
# Python2 does not support os.symlink on Windows whereas Python3 does. os.symlink requires either administrative
# privileges or developer mode on Win10, throwing an OSError is neither is active.
if WINDOWS:
if PY2:
shutil.copyfile(str(fixture), str(dest))
else:
try:
os.symlink(str(fixture), str(dest))
except OSError:
shutil.copyfile(str(fixture), str(dest))
else:
os.symlink(str(fixture), str(dest))
@pytest.fixture(autouse=True)
def download_mock(mocker):
# Patch download to not download anything but to just copy from fixtures
......
......@@ -11,7 +11,7 @@ def test_about(app):
Poetry - Package Management for Python
Poetry is a dependency manager tracking local dependencies of your projects and libraries.
See https://github.com/sdispater/poetry for more information.
See https://github.com/python-poetry/poetry for more information.
"""
assert expected == tester.io.fetch_output()
......@@ -109,7 +109,7 @@ def test_add_constraint_with_extras(app, repo, installer):
repo.add_package(cachy1)
repo.add_package(get_package("msgpack-python", "0.5.3"))
tester.execute("cachy[msgpack]^0.1.0")
tester.execute("cachy[msgpack]>=0.1.0,<0.2.0")
expected = """\
......@@ -264,6 +264,42 @@ Package operations: 4 installs, 0 updates, 0 removals
}
def test_add_git_ssh_constraint(app, repo, installer):
command = app.find("add")
tester = CommandTester(command)
repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cleo", "0.6.5"))
tester.execute("git+ssh://git@github.com/demo/demo.git@develop")
expected = """\
Updating dependencies
Resolving dependencies...
Writing lock file
Package operations: 2 installs, 0 updates, 0 removals
- Installing pendulum (1.4.4)
- Installing demo (0.1.2 9cf87a2)
"""
assert expected == tester.io.fetch_output()
assert len(installer.installs) == 2
content = app.poetry.file.read()["tool"]["poetry"]
assert "demo" in content["dependencies"]
assert content["dependencies"]["demo"] == {
"git": "ssh://git@github.com/demo/demo.git",
"rev": "develop",
}
def test_add_directory_constraint(app, repo, installer, mocker):
p = mocker.patch("poetry.utils._compat.Path.cwd")
p.return_value = Path(__file__) / ".."
......@@ -456,7 +492,7 @@ def test_add_url_constraint_wheel(app, repo, installer, mocker):
repo.add_package(get_package("pendulum", "1.4.4"))
tester.execute(
"https://poetry.eustace.io/distributions/demo-0.1.0-py2.py3-none-any.whl"
"https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl"
)
expected = """\
......@@ -470,7 +506,7 @@ Writing lock file
Package operations: 2 installs, 0 updates, 0 removals
- Installing pendulum (1.4.4)
- Installing demo (0.1.0 https://poetry.eustace.io/distributions/demo-0.1.0-py2.py3-none-any.whl)
- Installing demo (0.1.0 https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl)
"""
assert expected == tester.io.fetch_output()
......@@ -481,7 +517,7 @@ Package operations: 2 installs, 0 updates, 0 removals
assert "demo" in content["dependencies"]
assert content["dependencies"]["demo"] == {
"url": "https://poetry.eustace.io/distributions/demo-0.1.0-py2.py3-none-any.whl"
"url": "https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl"
}
......@@ -494,7 +530,7 @@ def test_add_url_constraint_wheel_with_extras(app, repo, installer, mocker):
repo.add_package(get_package("tomlkit", "0.5.5"))
tester.execute(
"https://poetry.eustace.io/distributions/demo-0.1.0-py2.py3-none-any.whl[foo,bar]"
"https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl[foo,bar]"
)
expected = """\
......@@ -510,7 +546,7 @@ Package operations: 4 installs, 0 updates, 0 removals
- Installing cleo (0.6.5)
- Installing pendulum (1.4.4)
- Installing tomlkit (0.5.5)
- Installing demo (0.1.0 https://poetry.eustace.io/distributions/demo-0.1.0-py2.py3-none-any.whl)
- Installing demo (0.1.0 https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl)
"""
assert expected == tester.io.fetch_output()
......@@ -521,7 +557,7 @@ Package operations: 4 installs, 0 updates, 0 removals
assert "demo" in content["dependencies"]
assert content["dependencies"]["demo"] == {
"url": "https://poetry.eustace.io/distributions/demo-0.1.0-py2.py3-none-any.whl",
"url": "https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl",
"extras": ["foo", "bar"],
}
......
......@@ -36,14 +36,14 @@ def test_check_invalid(app, mocker):
Error: u'description' is a required property
Error: INVALID is not a valid license
Warning: A wildcard Python dependency is ambiguous. Consider specifying a more explicit one.
Warning: The "pendulum" dependency specifies the "allows-prereleases" property, which is deprecated. Use "allow-preleases" instead.
Warning: The "pendulum" dependency specifies the "allows-prereleases" property, which is deprecated. Use "allow-prereleases" instead.
"""
else:
expected = """\
Error: 'description' is a required property
Error: INVALID is not a valid license
Warning: A wildcard Python dependency is ambiguous. Consider specifying a more explicit one.
Warning: The "pendulum" dependency specifies the "allows-prereleases" property, which is deprecated. Use "allow-preleases" instead.
Warning: The "pendulum" dependency specifies the "allows-prereleases" property, which is deprecated. Use "allow-prereleases" instead.
"""
assert expected == tester.io.fetch_output()
......@@ -26,9 +26,9 @@ license = "MIT"
readme = "README.rst"
homepage = "https://poetry.eustace.io"
repository = "https://github.com/sdispater/poetry"
documentation = "https://poetry.eustace.io/docs"
homepage = "https://python-poetry.org"
repository = "https://github.com/python-poetry/poetry"
documentation = "https://python-poetry.org/docs"
keywords = ["packaging", "dependency", "poetry"]
......
......@@ -379,6 +379,178 @@ cachy 0.1.0 0.2.0 Cachy package
assert expected == tester.io.fetch_output()
def test_show_outdated_with_only_up_to_date_packages(app, poetry, installed, repo):
command = app.find("show")
tester = CommandTester(command)
cachy_020 = get_package("cachy", "0.2.0")
cachy_020.description = "Cachy package"
installed.add_package(cachy_020)
repo.add_package(cachy_020)
poetry.locker.mock_lock_data(
{
"package": [
{
"name": "cachy",
"version": "0.2.0",
"description": "Cachy package",
"category": "main",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"hashes": {"cachy": []},
},
}
)
tester.execute("--outdated")
expected = ""
assert expected == tester.io.fetch_output()
def test_show_outdated_has_prerelease_but_not_allowed(app, poetry, installed, repo):
command = app.find("show")
tester = CommandTester(command)
cachy_010 = get_package("cachy", "0.1.0")
cachy_010.description = "Cachy package"
cachy_020 = get_package("cachy", "0.2.0")
cachy_020.description = "Cachy package"
cachy_030dev = get_package("cachy", "0.3.0.dev123")
cachy_030dev.description = "Cachy package"
pendulum_200 = get_package("pendulum", "2.0.0")
pendulum_200.description = "Pendulum package"
installed.add_package(cachy_010)
installed.add_package(pendulum_200)
# sorting isn't used, so this has to be the first element to
# replicate the issue in PR #1548
repo.add_package(cachy_030dev)
repo.add_package(cachy_010)
repo.add_package(cachy_020)
repo.add_package(pendulum_200)
poetry.locker.mock_lock_data(
{
"package": [
{
"name": "cachy",
"version": "0.1.0",
"description": "Cachy package",
"category": "main",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
{
"name": "pendulum",
"version": "2.0.0",
"description": "Pendulum package",
"category": "main",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"hashes": {"cachy": [], "pendulum": []},
},
}
)
tester.execute("--outdated")
expected = """\
cachy 0.1.0 0.2.0 Cachy package
"""
assert expected == tester.io.fetch_output()
def test_show_outdated_has_prerelease_and_allowed(app, poetry, installed, repo):
command = app.find("show")
tester = CommandTester(command)
cachy_010dev = get_package("cachy", "0.1.0.dev1")
cachy_010dev.description = "Cachy package"
cachy_020 = get_package("cachy", "0.2.0")
cachy_020.description = "Cachy package"
cachy_030dev = get_package("cachy", "0.3.0.dev123")
cachy_030dev.description = "Cachy package"
pendulum_200 = get_package("pendulum", "2.0.0")
pendulum_200.description = "Pendulum package"
installed.add_package(cachy_010dev)
installed.add_package(pendulum_200)
# sorting isn't used, so this has to be the first element to
# replicate the issue in PR #1548
repo.add_package(cachy_030dev)
repo.add_package(cachy_010dev)
repo.add_package(cachy_020)
repo.add_package(pendulum_200)
poetry.locker.mock_lock_data(
{
"package": [
{
"name": "cachy",
"version": "0.1.0.dev1",
"description": "Cachy package",
"category": "main",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
{
"name": "pendulum",
"version": "2.0.0",
"description": "Pendulum package",
"category": "main",
"optional": False,
"platform": "*",
"python-versions": "*",
"checksum": [],
},
],
"metadata": {
"python-versions": "*",
"platform": "*",
"content-hash": "123456789",
"hashes": {"cachy": [], "pendulum": []},
},
}
)
tester.execute("--outdated")
expected = """\
cachy 0.1.0.dev1 0.3.0.dev123 Cachy package
"""
assert expected == tester.io.fetch_output()
def test_show_outdated_formatting(app, poetry, installed, repo):
command = app.find("show")
tester = CommandTester(command)
......
......@@ -44,4 +44,4 @@ def test_version_show(app):
command = app.find("version")
tester = CommandTester(command)
tester.execute()
assert "Project (simple-project) version is 1.2.3\n" == tester.io.fetch_output()
assert "simple-project 1.2.3\n" == tester.io.fetch_output()
import os
import shutil
import pytest
......@@ -13,16 +12,10 @@ from poetry.poetry import Poetry as BasePoetry
from poetry.repositories import Pool
from poetry.repositories import Repository as BaseRepository
from poetry.repositories.exceptions import PackageNotFound
from poetry.utils._compat import PY2
from poetry.utils._compat import WINDOWS
from poetry.utils._compat import Path
from poetry.utils.toml_file import TomlFile
try:
import urllib.parse as urlparse
except ImportError:
import urlparse
from tests.helpers import mock_clone
from tests.helpers import mock_download
@pytest.fixture()
......@@ -30,45 +23,6 @@ def installer():
return NoopInstaller()
def mock_clone(self, source, dest):
# Checking source to determine which folder we need to copy
parts = urlparse.urlparse(source)
folder = (
Path(__file__).parent.parent
/ "fixtures"
/ "git"
/ parts.netloc
/ parts.path.lstrip("/").rstrip(".git")
)
shutil.rmtree(str(dest))
shutil.copytree(str(folder), str(dest))
def mock_download(self, url, dest):
parts = urlparse.urlparse(url)
fixtures = Path(__file__).parent.parent / "fixtures"
fixture = fixtures / parts.path.lstrip("/")
if dest.exists():
shutil.rmtree(str(dest))
# Python2 does not support os.symlink on Windows whereas Python3 does. os.symlink requires either administrative
# privileges or developer mode on Win10, throwing an OSError is neither is active.
if WINDOWS:
if PY2:
shutil.copyfile(str(fixture), str(dest))
else:
try:
os.symlink(str(fixture), str(dest))
except OSError:
shutil.copyfile(str(fixture), str(dest))
else:
os.symlink(str(fixture), str(dest))
@pytest.fixture
def installed():
return BaseRepository()
......
......@@ -9,9 +9,9 @@ license = "MIT"
readme = "README.rst"
homepage = "https://poetry.eustace.io/"
repository = "https://github.com/sdispater/poetry"
documentation = "https://poetry.eustace.io/docs"
homepage = "https://python-poetry.org/"
repository = "https://github.com/python-poetry/poetry"
documentation = "https://python-poetry.org/docs"
keywords = ["packaging", "dependency", "poetry"]
......
......@@ -7,9 +7,9 @@ authors = [
]
license = "MIT"
homepage = "https://poetry.eustace.io"
repository = "https://github.com/sdispater/poetry"
documentation = "https://poetry.eustace.io/docs"
homepage = "https://python-poetry.org"
repository = "https://github.com/python-poetry/poetry"
documentation = "https://python-poetry.org/docs"
keywords = ["packaging", "dependency", "poetry"]
......
......@@ -9,9 +9,9 @@ license = "MIT"
readme = "README.rst"
homepage = "https://poetry.eustace.io"
repository = "https://github.com/sdispater/poetry"
documentation = "https://poetry.eustace.io/docs"
homepage = "https://python-poetry.org"
repository = "https://github.com/python-poetry/poetry"
documentation = "https://python-poetry.org/docs"
keywords = ["packaging", "dependency", "poetry"]
......
......@@ -9,9 +9,9 @@ license = "MIT"
readme = "README.rst"
homepage = "https://poetry.eustace.io"
repository = "https://github.com/sdispater/poetry"
documentation = "https://poetry.eustace.io/docs"
homepage = "https://python-poetry.org"
repository = "https://github.com/python-poetry/poetry"
documentation = "https://python-poetry.org/docs"
keywords = ["packaging", "dependency", "poetry"]
......
......@@ -9,9 +9,9 @@ license = "MIT"
readme = "README.rst"
homepage = "https://poetry.eustace.io"
repository = "https://github.com/sdispater/poetry"
documentation = "https://poetry.eustace.io/docs"
homepage = "https://python-poetry.org"
repository = "https://github.com/python-poetry/poetry"
documentation = "https://python-poetry.org/docs"
keywords = ["packaging", "dependency", "poetry"]
......
......@@ -9,9 +9,9 @@ license = "MIT"
readme = "README.rst"
homepage = "https://poetry.eustace.io"
repository = "https://github.com/sdispater/poetry"
documentation = "https://poetry.eustace.io/docs"
homepage = "https://python-poetry.org"
repository = "https://github.com/python-poetry/poetry"
documentation = "https://python-poetry.org/docs"
keywords = ["packaging", "dependency", "poetry"]
......
[virtualenvs]
in-project = false
create = false
......@@ -9,9 +9,9 @@ license = "MIT"
readme = "README.rst"
homepage = "https://poetry.eustace.io"
repository = "https://github.com/sdispater/poetry"
documentation = "https://poetry.eustace.io/docs"
homepage = "https://python-poetry.org"
repository = "https://github.com/python-poetry/poetry"
documentation = "https://python-poetry.org/docs"
keywords = ["packaging", "dependency", "poetry"]
......
......@@ -9,9 +9,9 @@ license = "MIT"
readme = "README.rst"
homepage = "https://poetry.eustace.io"
repository = "https://github.com/sdispater/poetry"
documentation = "https://poetry.eustace.io/docs"
homepage = "https://python-poetry.org"
repository = "https://github.com/python-poetry/poetry"
documentation = "https://python-poetry.org/docs"
keywords = ["packaging", "dependency", "poetry"]
......
import os
import shutil
from poetry.packages import Dependency
from poetry.packages import Package
from poetry.utils._compat import PY2
from poetry.utils._compat import WINDOWS
from poetry.utils._compat import Path
from poetry.utils._compat import urlparse
from poetry.vcs.git import ParsedUrl
FIXTURE_PATH = Path(__file__).parent / "fixtures"
......@@ -27,3 +34,57 @@ def fixture(path=None):
return FIXTURE_PATH / path
else:
return FIXTURE_PATH
def copy_or_symlink(source, dest):
if dest.exists():
if dest.is_symlink():
os.unlink(str(dest))
elif dest.is_dir():
shutil.rmtree(str(dest))
else:
os.unlink(str(dest))
# Python2 does not support os.symlink on Windows whereas Python3 does.
# os.symlink requires either administrative privileges or developer mode on Win10,
# throwing an OSError if neither is active.
if WINDOWS:
if PY2:
if source.is_dir():
shutil.copytree(str(source), str(dest))
else:
shutil.copyfile(str(source), str(dest))
else:
try:
os.symlink(str(source), str(dest), target_is_directory=source.is_dir())
except OSError:
if source.is_dir():
shutil.copytree(str(source), str(dest))
else:
shutil.copyfile(str(source), str(dest))
else:
os.symlink(str(source), str(dest))
def mock_clone(_, source, dest):
# Checking source to determine which folder we need to copy
parsed = ParsedUrl.parse(source)
folder = (
Path(__file__).parent
/ "fixtures"
/ "git"
/ parsed.resource
/ parsed.pathname.lstrip("/").rstrip(".git")
)
copy_or_symlink(folder, dest)
def mock_download(self, url, dest):
parts = urlparse.urlparse(url)
fixtures = Path(__file__).parent / "fixtures"
fixture = fixtures / parts.path.lstrip("/")
copy_or_symlink(fixture, dest)
[[package]]
category = "main"
description = ""
develop = true
name = "project-with-extras"
optional = false
python-versions = "*"
......@@ -18,6 +19,7 @@ url = "tests/fixtures/directory/project_with_transitive_directory_dependencies/.
[[package]]
category = "main"
description = ""
develop = true
name = "project-with-transitive-directory-dependencies"
optional = false
python-versions = "*"
......
......@@ -9,6 +9,7 @@ version = "1.4.4"
[[package]]
category = "main"
description = ""
develop = true
name = "project-with-extras"
optional = false
python-versions = "*"
......
......@@ -9,6 +9,7 @@ python-versions = "*"
[[package]]
name = "my-package"
version = "0.1.2"
develop = true
description = "Demo project."
category = "main"
optional = false
......
......@@ -23,6 +23,7 @@ C = "1.5"
[[package]]
name = "C"
version = "1.5"
marker = "python_version >= \"2.7\""
description = ""
category = "main"
optional = false
......
......@@ -29,6 +29,7 @@ version = "1.4.4"
[[package]]
category = "main"
description = ""
develop = true
name = "project-with-transitive-file-dependencies"
optional = false
python-versions = "*"
......
......@@ -9,7 +9,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[package.source]
type = "url"
reference = ""
url = "https://poetry.eustace.io/distributions/demo-0.1.0-py2.py3-none-any.whl"
url = "https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl"
[package.dependencies]
pendulum = ">=1.4.4"
......
......@@ -25,6 +25,9 @@ from tests.repositories.test_legacy_repository import (
from tests.repositories.test_pypi_repository import MockRepository
fixtures_dir = Path("tests/fixtures")
class Installer(BaseInstaller):
def _get_installer(self):
return NoopInstaller()
......@@ -662,7 +665,7 @@ def test_installer_with_pypi_repository(package, locker, installed):
def test_run_installs_with_local_file(installer, locker, repo, package):
file_path = Path("tests/fixtures/distributions/demo-0.1.0-py2.py3-none-any.whl")
file_path = fixtures_dir / "distributions/demo-0.1.0-py2.py3-none-any.whl"
package.add_dependency("demo", {"file": str(file_path)})
repo.add_package(get_package("pendulum", "1.4.4"))
......@@ -677,8 +680,8 @@ def test_run_installs_with_local_file(installer, locker, repo, package):
def test_run_installs_wheel_with_no_requires_dist(installer, locker, repo, package):
file_path = Path(
"tests/fixtures/wheel_with_no_requires_dist/demo-0.1.0-py2.py3-none-any.whl"
file_path = (
fixtures_dir / "wheel_with_no_requires_dist/demo-0.1.0-py2.py3-none-any.whl"
)
package.add_dependency("demo", {"file": str(file_path)})
......@@ -694,7 +697,7 @@ def test_run_installs_wheel_with_no_requires_dist(installer, locker, repo, packa
def test_run_installs_with_local_poetry_directory_and_extras(
installer, locker, repo, package, tmpdir
):
file_path = Path("tests/fixtures/project_with_extras")
file_path = fixtures_dir / "project_with_extras"
package.add_dependency(
"project-with-extras", {"path": str(file_path), "extras": ["extras_a"]}
)
......@@ -713,8 +716,8 @@ def test_run_installs_with_local_poetry_directory_and_extras(
def test_run_installs_with_local_poetry_directory_transitive(
installer, locker, repo, package, tmpdir
):
file_path = Path(
"tests/fixtures/directory/project_with_transitive_directory_dependencies/"
file_path = (
fixtures_dir / "directory/project_with_transitive_directory_dependencies/"
)
package.add_dependency(
"project-with-transitive-directory-dependencies", {"path": str(file_path)}
......@@ -735,9 +738,7 @@ def test_run_installs_with_local_poetry_directory_transitive(
def test_run_installs_with_local_poetry_file_transitive(
installer, locker, repo, package, tmpdir
):
file_path = Path(
"tests/fixtures/directory/project_with_transitive_file_dependencies/"
)
file_path = fixtures_dir / "directory/project_with_transitive_file_dependencies/"
package.add_dependency(
"project-with-transitive-file-dependencies", {"path": str(file_path)}
)
......@@ -757,7 +758,7 @@ def test_run_installs_with_local_poetry_file_transitive(
def test_run_installs_with_local_setuptools_directory(
installer, locker, repo, package, tmpdir
):
file_path = Path("tests/fixtures/project_with_setup/")
file_path = fixtures_dir / "project_with_setup/"
package.add_dependency("my-package", {"path": str(file_path)})
repo.add_package(get_package("pendulum", "1.4.4"))
......@@ -1182,8 +1183,8 @@ def test_run_install_duplicate_dependencies_different_constraints_with_lock_upda
"checksum": [],
"dependencies": {
"B": [
{"version": "^1.0", "python": "<4.0"},
{"version": "^2.0", "python": ">=4.0"},
{"version": "^1.0", "python": "<2.7"},
{"version": "^2.0", "python": ">=2.7"},
]
},
},
......@@ -1196,7 +1197,7 @@ def test_run_install_duplicate_dependencies_different_constraints_with_lock_upda
"python-versions": "*",
"checksum": [],
"dependencies": {"C": "1.2"},
"requirements": {"python": "<4.0"},
"requirements": {"python": "<2.7"},
},
{
"name": "B",
......@@ -1207,7 +1208,7 @@ def test_run_install_duplicate_dependencies_different_constraints_with_lock_upda
"python-versions": "*",
"checksum": [],
"dependencies": {"C": "1.5"},
"requirements": {"python": ">=4.0"},
"requirements": {"python": ">=2.7"},
},
{
"name": "C",
......@@ -1516,7 +1517,7 @@ def test_installer_can_install_dependencies_from_forced_source(
def test_run_installs_with_url_file(installer, locker, repo, package):
url = "https://poetry.eustace.io/distributions/demo-0.1.0-py2.py3-none-any.whl"
url = "https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl"
package.add_dependency("demo", {"url": url})
repo.add_package(get_package("pendulum", "1.4.4"))
......
......@@ -9,9 +9,9 @@ license = "MIT"
readme = "README.rst"
homepage = "https://poetry.eustace.io/"
repository = "https://github.com/sdispater/poetry"
documentation = "https://poetry.eustace.io/docs"
homepage = "https://python-poetry.org/"
repository = "https://github.com/python-poetry/poetry"
documentation = "https://python-poetry.org/docs"
keywords = ["packaging", "dependency", "poetry"]
......
......@@ -12,9 +12,9 @@ license = "MIT"
readme = "README.rst"
homepage = "https://poetry.eustace.io/"
repository = "https://github.com/sdispater/poetry"
documentation = "https://poetry.eustace.io/docs"
homepage = "https://python-poetry.org/"
repository = "https://github.com/python-poetry/poetry"
documentation = "https://python-poetry.org/docs"
keywords = ["packaging", "dependency", "poetry"]
......@@ -50,4 +50,4 @@ my-2nd-script = "my_package:main2"
extra-script = {callable = "my_package.extra:main", extras = ["time"]}
[tool.poetry.urls]
"Issue Tracker" = "https://github.com/sdispater/poetry/issues"
"Issue Tracker" = "https://github.com/python-poetry/poetry/issues"
......@@ -9,9 +9,9 @@ license = "MIT"
readme = "README.rst"
homepage = "https://poetry.eustace.io/"
repository = "https://github.com/sdispater/poetry"
documentation = "https://poetry.eustace.io/docs"
homepage = "https://python-poetry.org/"
repository = "https://github.com/python-poetry/poetry"
documentation = "https://python-poetry.org/docs"
keywords = ["packaging", "dependency", "poetry"]
......
......@@ -11,9 +11,9 @@ readme = "README.rst"
exclude = ["my_package/data/data1.txt"]
homepage = "https://poetry.eustace.io/"
repository = "https://github.com/sdispater/poetry"
documentation = "https://poetry.eustace.io/docs"
homepage = "https://python-poetry.org/"
repository = "https://github.com/python-poetry/poetry"
documentation = "https://python-poetry.org/docs"
keywords = ["packaging", "dependency", "poetry"]
......
......@@ -9,11 +9,12 @@ license = "MIT"
readme = "README.rst"
exclude = ["my_package/data/", "**/*/item*"]
exclude = ["**/data/", "**/*/item*"]
include = ["my_package/data/data2.txt"]
homepage = "https://poetry.eustace.io/"
repository = "https://github.com/sdispater/poetry"
documentation = "https://poetry.eustace.io/docs"
homepage = "https://python-poetry.org/"
repository = "https://github.com/python-poetry/poetry"
documentation = "https://python-poetry.org/docs"
keywords = ["packaging", "dependency", "poetry"]
......
......@@ -9,6 +9,6 @@ license = "MIT"
readme = "README.rst"
homepage = "https://poetry.eustace.io/"
homepage = "https://python-poetry.org/"
build = "build.py"
......@@ -9,9 +9,9 @@ license = "MIT"
readme = "README.rst"
homepage = "https://poetry.eustace.io/"
repository = "https://github.com/sdispater/poetry"
documentation = "https://poetry.eustace.io/docs"
homepage = "https://python-poetry.org/"
repository = "https://github.com/python-poetry/poetry"
documentation = "https://python-poetry.org/docs"
keywords = ["packaging", "dependency", "poetry"]
......
"""Test fixture for https://github.com/sdispater/poetry/issues/756"""
"""Test fixture for https://github.com/python-poetry/poetry/issues/756"""
......@@ -9,7 +9,7 @@ license = "MIT"
readme = "README.rst"
homepage = "https://poetry.eustace.io/"
homepage = "https://python-poetry.org/"
[tool.poetry.dependencies]
......
......@@ -9,4 +9,4 @@ license = "MIT"
readme = "README.rst"
homepage = "https://poetry.eustace.io/"
homepage = "https://python-poetry.org/"
......@@ -9,7 +9,7 @@ license = "MIT"
readme = "README.rst"
homepage = "https://poetry.eustace.io/"
homepage = "https://python-poetry.org/"
[tool.poetry.dependencies]
......
......@@ -9,7 +9,7 @@ license = "MIT"
readme = "README.rst"
homepage = "https://poetry.eustace.io/"
homepage = "https://python-poetry.org/"
[tool.poetry.dependencies]
......
......@@ -9,7 +9,7 @@ license = "MIT"
readme = "README.rst"
homepage = "https://poetry.eustace.io/"
homepage = "https://python-poetry.org/"
[tool.poetry.dependencies]
python = "*"
......@@ -9,6 +9,6 @@ license = "MIT"
readme = "README.rst"
homepage = "https://poetry.eustace.io/"
homepage = "https://python-poetry.org/"
build = "build.py"
......@@ -9,9 +9,9 @@ license = "MIT"
readme = "README.rst"
homepage = "https://poetry.eustace.io/"
repository = "https://github.com/sdispater/poetry"
documentation = "https://poetry.eustace.io/docs"
homepage = "https://python-poetry.org/"
repository = "https://github.com/python-poetry/poetry"
documentation = "https://python-poetry.org/docs"
keywords = ["packaging", "dependency", "poetry"]
......
......@@ -7,9 +7,9 @@ authors = [
]
license = "MIT"
homepage = "https://poetry.eustace.io/"
repository = "https://github.com/sdispater/poetry"
documentation = "https://poetry.eustace.io/docs"
homepage = "https://python-poetry.org/"
repository = "https://github.com/python-poetry/poetry"
documentation = "https://python-poetry.org/docs"
keywords = ["packaging", "dependency", "poetry"]
......@@ -21,4 +21,4 @@ classifiers = [
# Requirements
[tool.poetry.dependencies]
python = "^3.6"
demo = { url = "https://poetry.eustace.io/distributions/demo-0.1.0-py2.py3-none-any.whl" }
demo = { url = "https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl" }
......@@ -7,9 +7,9 @@ authors = [
]
license = "MIT"
homepage = "https://poetry.eustace.io/"
repository = "https://github.com/sdispater/poetry"
documentation = "https://poetry.eustace.io/docs"
homepage = "https://python-poetry.org/"
repository = "https://github.com/python-poetry/poetry"
documentation = "https://python-poetry.org/docs"
keywords = ["packaging", "dependency", "poetry"]
......
......@@ -81,7 +81,7 @@ def test_get_metadata_content():
assert parsed["Keywords"] == "packaging,dependency,poetry"
assert parsed["Requires-Python"] == ">=3.6,<4.0"
assert parsed["License"] == "MIT"
assert parsed["Home-page"] == "https://poetry.eustace.io/"
assert parsed["Home-page"] == "https://python-poetry.org/"
classifiers = parsed.get_all("Classifier")
assert classifiers == [
......@@ -106,9 +106,9 @@ def test_get_metadata_content():
urls = parsed.get_all("Project-URL")
assert urls == [
"Documentation, https://poetry.eustace.io/docs",
"Issue Tracker, https://github.com/sdispater/poetry/issues",
"Repository, https://github.com/sdispater/poetry",
"Documentation, https://python-poetry.org/docs",
"Issue Tracker, https://github.com/python-poetry/poetry/issues",
"Repository, https://github.com/python-poetry/poetry",
]
......@@ -154,6 +154,6 @@ def test_metadata_with_url_dependencies():
requires_dist = metadata["Requires-Dist"]
assert (
"demo @ https://poetry.eustace.io/distributions/demo-0.1.0-py2.py3-none-any.whl"
"demo @ https://python-poetry.org/distributions/demo-0.1.0-py2.py3-none-any.whl"
== requires_dist
)
......@@ -206,7 +206,7 @@ Metadata-Version: 2.1
Name: my-package
Version: 1.2.3
Summary: Some description.
Home-page: https://poetry.eustace.io/
Home-page: https://python-poetry.org/
License: MIT
Keywords: packaging,dependency,poetry
Author: Sébastien Eustace
......@@ -225,9 +225,9 @@ Provides-Extra: time
Requires-Dist: cachy[msgpack] (>=0.2.0,<0.3.0)
Requires-Dist: cleo (>=0.6,<0.7)
Requires-Dist: pendulum (>=1.4,<2.0); (python_version ~= "2.7" and sys_platform == "win32" or python_version in "3.4 3.5") and (extra == "time")
Project-URL: Documentation, https://poetry.eustace.io/docs
Project-URL: Issue Tracker, https://github.com/sdispater/poetry/issues
Project-URL: Repository, https://github.com/sdispater/poetry
Project-URL: Documentation, https://python-poetry.org/docs
Project-URL: Issue Tracker, https://github.com/python-poetry/poetry/issues
Project-URL: Repository, https://github.com/python-poetry/poetry
Description-Content-Type: text/x-rst
My Package
......@@ -265,6 +265,7 @@ def test_complete_no_vcs():
"my_package/sub_pkg1/__init__.py",
"my_package/sub_pkg2/__init__.py",
"my_package/sub_pkg2/data2/data.json",
"my_package/sub_pkg3/foo.py",
"my_package-1.2.3.dist-info/entry_points.txt",
"my_package-1.2.3.dist-info/LICENSE",
"my_package-1.2.3.dist-info/WHEEL",
......@@ -309,7 +310,7 @@ Metadata-Version: 2.1
Name: my-package
Version: 1.2.3
Summary: Some description.
Home-page: https://poetry.eustace.io/
Home-page: https://python-poetry.org/
License: MIT
Keywords: packaging,dependency,poetry
Author: Sébastien Eustace
......@@ -328,9 +329,9 @@ Provides-Extra: time
Requires-Dist: cachy[msgpack] (>=0.2.0,<0.3.0)
Requires-Dist: cleo (>=0.6,<0.7)
Requires-Dist: pendulum (>=1.4,<2.0); (python_version ~= "2.7" and sys_platform == "win32" or python_version in "3.4 3.5") and (extra == "time")
Project-URL: Documentation, https://poetry.eustace.io/docs
Project-URL: Issue Tracker, https://github.com/sdispater/poetry/issues
Project-URL: Repository, https://github.com/sdispater/poetry
Project-URL: Documentation, https://python-poetry.org/docs
Project-URL: Issue Tracker, https://github.com/python-poetry/poetry/issues
Project-URL: Repository, https://github.com/python-poetry/poetry
Description-Content-Type: text/x-rst
My Package
......
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import sys
from clikit.io import NullIO
from poetry.factory import Factory
......@@ -15,14 +17,13 @@ fixtures_dir = Path(__file__).parent / "fixtures"
def test_build_should_delegate_to_pip_for_non_pure_python_packages(tmp_dir, mocker):
move = mocker.patch("shutil.move")
tmp_dir = Path(tmp_dir)
env = MockEnv(path=tmp_dir, pip_version="18.1", execute=False)
env.site_packages.mkdir(parents=True)
env = MockEnv(path=tmp_dir, pip_version="18.1", execute=False, sys_path=[])
module_path = fixtures_dir / "extended"
builder = EditableBuilder(Factory().create_poetry(module_path), env, NullIO())
builder.build()
expected = [["python", "-m", "pip", "install", "-e", str(module_path)]]
expected = [[sys.executable, "-m", "pip", "install", "-e", str(module_path)]]
assert expected == env.executed
assert 0 == move.call_count
......@@ -31,14 +32,13 @@ def test_build_should_delegate_to_pip_for_non_pure_python_packages(tmp_dir, mock
def test_build_should_temporarily_remove_the_pyproject_file(tmp_dir, mocker):
move = mocker.patch("shutil.move")
tmp_dir = Path(tmp_dir)
env = MockEnv(path=tmp_dir, pip_version="19.1", execute=False)
env.site_packages.mkdir(parents=True)
env = MockEnv(path=tmp_dir, pip_version="19.1", execute=False, sys_path=[])
module_path = fixtures_dir / "extended"
builder = EditableBuilder(Factory().create_poetry(module_path), env, NullIO())
builder.build()
expected = [["python", "-m", "pip", "install", "-e", str(module_path)]]
expected = [[sys.executable, "-m", "pip", "install", "-e", str(module_path)]]
assert expected == env.executed
assert 2 == move.call_count
......
......@@ -124,6 +124,7 @@ def test_make_setup():
"my_package",
"my_package.sub_pkg1",
"my_package.sub_pkg2",
"my_package.sub_pkg3",
]
assert ns["install_requires"] == ["cachy[msgpack]>=0.2.0,<0.3.0", "cleo>=0.6,<0.7"]
assert ns["entry_points"] == {
......@@ -178,6 +179,7 @@ def test_find_files_to_add():
Path("my_package/sub_pkg1/__init__.py"),
Path("my_package/sub_pkg2/__init__.py"),
Path("my_package/sub_pkg2/data2/data.json"),
Path("my_package/sub_pkg3/foo.py"),
Path("pyproject.toml"),
]
)
......@@ -213,7 +215,12 @@ def test_find_packages():
pkg_dir, packages, pkg_data = builder.find_packages(include)
assert pkg_dir is None
assert packages == ["my_package", "my_package.sub_pkg1", "my_package.sub_pkg2"]
assert packages == [
"my_package",
"my_package.sub_pkg1",
"my_package.sub_pkg2",
"my_package.sub_pkg3",
]
assert pkg_data == {
"": ["*"],
"my_package": ["data1/*"],
......@@ -408,6 +415,9 @@ def test_default_with_excluded_data(mocker):
assert "my-package-1.2.3/pyproject.toml" in names
assert "my-package-1.2.3/setup.py" in names
assert "my-package-1.2.3/PKG-INFO" in names
# all last modified times should be set to a valid timestamp
for tarinfo in tar.getmembers():
assert 0 < tarinfo.mtime
def test_src_excluded_nested_data():
......@@ -433,6 +443,7 @@ def test_src_excluded_nested_data():
assert "my-package-1.2.3/my_package/data/sub_data/data2.txt" not in names
assert "my-package-1.2.3/my_package/data/sub_data/data3.txt" not in names
assert "my-package-1.2.3/my_package/data/data1.txt" not in names
assert "my-package-1.2.3/my_package/data/data2.txt" in names
assert "my-package-1.2.3/my_package/puplic/publicdata.txt" in names
assert "my-package-1.2.3/my_package/public/item1/itemdata1.txt" not in names
assert (
......
......@@ -93,6 +93,7 @@ def test_wheel_excluded_nested_data():
assert "my_package/data/sub_data/data2.txt" not in z.namelist()
assert "my_package/data/sub_data/data3.txt" not in z.namelist()
assert "my_package/data/data1.txt" not in z.namelist()
assert "my_package/data/data2.txt" in z.namelist()
assert "my_package/puplic/publicdata.txt" in z.namelist()
assert "my_package/public/item1/itemdata1.txt" not in z.namelist()
assert "my_package/public/item1/subitem/subitemdata.txt" not in z.namelist()
......
......@@ -82,7 +82,7 @@ Metadata-Version: 2.1
Name: my-package
Version: 1.2.3
Summary: Some description.
Home-page: https://poetry.eustace.io/
Home-page: https://python-poetry.org/
License: MIT
Keywords: packaging,dependency,poetry
Author: Sébastien Eustace
......@@ -101,9 +101,9 @@ Provides-Extra: time
Requires-Dist: cachy[msgpack] (>=0.2.0,<0.3.0)
Requires-Dist: cleo (>=0.6,<0.7)
Requires-Dist: pendulum (>=1.4,<2.0); (python_version ~= "2.7" and sys_platform == "win32" or python_version in "3.4 3.5") and (extra == "time")
Project-URL: Documentation, https://poetry.eustace.io/docs
Project-URL: Issue Tracker, https://github.com/sdispater/poetry/issues
Project-URL: Repository, https://github.com/sdispater/poetry
Project-URL: Documentation, https://python-poetry.org/docs
Project-URL: Issue Tracker, https://github.com/python-poetry/poetry/issues
Project-URL: Repository, https://github.com/python-poetry/poetry
Description-Content-Type: text/x-rst
My Package
......
import pytest
from poetry.masonry.utils.package_include import PackageInclude
from poetry.utils._compat import Path
fixtures_dir = Path(__file__).parent / "fixtures"
with_includes = fixtures_dir / "with_includes"
def test_package_include_with_multiple_dirs():
pkg_include = PackageInclude(base=fixtures_dir, include="with_includes")
assert pkg_include.elements == [
with_includes / "__init__.py",
with_includes / "bar",
with_includes / "bar/baz.py",
with_includes / "extra_package",
with_includes / "extra_package/some_dir",
with_includes / "extra_package/some_dir/foo.py",
with_includes / "extra_package/some_dir/quux.py",
with_includes / "not_a_python_pkg",
with_includes / "not_a_python_pkg/baz.txt",
]
def test_package_include_with_simple_dir():
pkg_include = PackageInclude(base=with_includes, include="bar")
assert pkg_include.elements == [with_includes / "bar/baz.py"]
def test_package_include_with_nested_dir():
pkg_include = PackageInclude(base=with_includes, include="extra_package/**/*.py")
assert pkg_include.elements == [
with_includes / "extra_package/some_dir/foo.py",
with_includes / "extra_package/some_dir/quux.py",
]
def test_package_include_with_no_python_files_in_dir():
with pytest.raises(ValueError) as e:
PackageInclude(base=with_includes, include="not_a_python_pkg")
assert str(e.value) == "not_a_python_pkg is not a package."
......@@ -8,7 +8,9 @@ def test_dependency_does_not_match_root_python_constraint(root, provider, repo):
add_to_repo(repo, "foo", "1.0.0", python="<3.5")
error = """The current project must support the following Python versions: ^3.6
error = """The current project's Python requirement (^3.6) \
is not compatible with some of the required packages Python requirement:
- foo requires Python <3.5
Because no versions of foo match !=1.0.0
and foo (1.0.0) requires Python <3.5, foo is forbidden.
......
......@@ -8,12 +8,24 @@ from poetry.utils.env import EnvCommandError
from poetry.utils.env import MockEnv as BaseMockEnv
fixtures_dir = Path(__file__).parent.parent / "fixtures"
DIST_PATH = Path(__file__).parent.parent / "fixtures" / "git" / "github.com" / "demo"
class MockEnv(BaseMockEnv):
def run(self, bin, *args):
raise EnvCommandError(CalledProcessError(1, "python", output=""))
DIST_PATH = Path(__file__).parent.parent / "fixtures" / "git" / "github.com" / "demo"
def test_directory_dependency():
dependency = DirectoryDependency("simple_project", fixtures_dir / "simple_project")
assert dependency.pretty_name == "simple_project"
assert dependency.develop
assert dependency.path == fixtures_dir / "simple_project"
assert dependency.base_pep_508_name == "simple_project @ {}".format(
fixtures_dir / "simple_project"
)
def test_directory_dependency_must_exist():
......
......@@ -7,6 +7,16 @@ from poetry.utils._compat import Path
DIST_PATH = Path(__file__).parent.parent / "fixtures" / "distributions"
def test_file_dependency():
dependency = FileDependency("demo", DIST_PATH / "demo-0.1.0.tar.gz")
assert dependency.pretty_name == "demo"
assert dependency.path == DIST_PATH / "demo-0.1.0.tar.gz"
assert dependency.base_pep_508_name == "demo @ {}".format(
DIST_PATH / "demo-0.1.0.tar.gz"
)
def test_file_dependency_wrong_path():
with pytest.raises(ValueError):
FileDependency("demo", DIST_PATH / "demo-0.2.0.tar.gz")
......
......@@ -181,3 +181,68 @@ A = []
content = f.read()
assert expected == content
def test_reading_lock_file_should_raise_an_error_on_invalid_data(locker):
content = u"""[[package]]
category = "main"
description = ""
name = "A"
optional = false
python-versions = "*"
version = "1.0.0"
[package.extras]
foo = ["bar"]
[package.extras]
foo = ["bar"]
[metadata]
content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8"
python-versions = "*"
[metadata.files]
A = []
"""
with locker.lock.open("w", encoding="utf-8") as f:
f.write(content)
with pytest.raises(RuntimeError) as e:
_ = locker.lock_data
assert "Unable to read the lock file" in str(e.value)
def test_locking_legacy_repository_package_should_include_source_section(root, locker):
package_a = get_package("A", "1.0.0")
package_a.source_url = "https://foo.bar"
package_a.source_reference = "legacy"
packages = [package_a]
locker.set_lock_data(root, packages)
with locker.lock.open(encoding="utf-8") as f:
content = f.read()
expected = """[[package]]
category = "main"
description = ""
name = "A"
optional = false
python-versions = "*"
version = "1.0.0"
[package.source]
reference = "legacy"
url = "https://foo.bar"
[metadata]
content-hash = "115cf985d932e9bf5f540555bbdd75decbb62cac81e399375fc19f6277f8c1d8"
python-versions = "*"
[metadata.files]
A = []
"""
assert expected == content
......@@ -169,3 +169,25 @@ def test_dependency_from_pep_508_with_not_in_op_marker():
assert (
str(dep.marker) == 'python_version not in "3.0,3.1,3.2" and extra == "export"'
)
def test_dependency_from_pep_508_with_git_url():
name = "django-utils @ git+ssh://git@corp-gitlab.com/corp-utils.git@1.2"
dep = dependency_from_pep_508(name)
assert "django-utils" == dep.name
assert dep.is_vcs()
assert "git" == dep.vcs
assert "ssh://git@corp-gitlab.com/corp-utils.git" == dep.source
assert "1.2" == dep.reference
def test_dependency_from_pep_508_with_url():
name = "django-utils @ https://example.com/django-utils-1.0.0.tar.gz"
dep = dependency_from_pep_508(name)
assert "django-utils" == dep.name
assert dep.is_url()
assert "https://example.com/django-utils-1.0.0.tar.gz" == dep.url
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import pytest
from poetry.packages import Package
......@@ -14,3 +16,24 @@ def test_package_authors():
package.authors.insert(0, "John Doe")
assert package.author_name == "John Doe"
assert package.author_email is None
@pytest.mark.parametrize("category", ["main", "dev"])
def test_package_add_dependency_vcs_category(category):
package = Package("foo", "0.1.0")
dependency = package.add_dependency(
"poetry",
constraint={"git": "https://github.com/python-poetry/poetry.git"},
category=category,
)
assert dependency.category == category
def test_package_add_dependency_vcs_category_default_main():
package = Package("foo", "0.1.0")
dependency = package.add_dependency(
"poetry", constraint={"git": "https://github.com/python-poetry/poetry.git"}
)
assert dependency.category == "main"
import pytest
from poetry.packages.vcs_dependency import VCSDependency
def test_to_pep_508():
dependency = VCSDependency(
"poetry", "git", "https://github.com/sdispater/poetry.git"
"poetry", "git", "https://github.com/python-poetry/poetry.git"
)
expected = "poetry @ git+https://github.com/sdispater/poetry.git@master"
expected = "poetry @ git+https://github.com/python-poetry/poetry.git@master"
assert expected == dependency.to_pep_508()
def test_to_pep_508_ssh():
dependency = VCSDependency("poetry", "git", "git@github.com:sdispater/poetry.git")
expected = "poetry @ git+ssh://git@github.com/sdispater/poetry.git@master"
assert expected == dependency.to_pep_508()
def test_to_pep_508_with_extras():
dependency = VCSDependency(
"poetry", "git", "https://github.com/sdispater/poetry.git"
"poetry", "git", "https://github.com/python-poetry/poetry.git"
)
dependency.extras.append("foo")
expected = "poetry[foo] @ git+https://github.com/sdispater/poetry.git@master"
expected = "poetry[foo] @ git+https://github.com/python-poetry/poetry.git@master"
assert expected == dependency.to_pep_508()
def test_to_pep_508_in_extras():
dependency = VCSDependency(
"poetry", "git", "https://github.com/sdispater/poetry.git"
"poetry", "git", "https://github.com/python-poetry/poetry.git"
)
dependency.in_extras.append("foo")
expected = (
'poetry @ git+https://github.com/sdispater/poetry.git@master ; extra == "foo"'
)
expected = 'poetry @ git+https://github.com/python-poetry/poetry.git@master ; extra == "foo"'
assert expected == dependency.to_pep_508()
dependency = VCSDependency(
"poetry", "git", "https://github.com/sdispater/poetry.git"
"poetry", "git", "https://github.com/python-poetry/poetry.git"
)
dependency.in_extras.append("foo")
dependency.extras.append("bar")
expected = 'poetry[bar] @ git+https://github.com/sdispater/poetry.git@master ; extra == "foo"'
expected = 'poetry[bar] @ git+https://github.com/python-poetry/poetry.git@master ; extra == "foo"'
assert expected == dependency.to_pep_508()
dependency = VCSDependency(
"poetry", "git", "https://github.com/sdispater/poetry.git", "b;ar;"
"poetry", "git", "https://github.com/python-poetry/poetry.git", "b;ar;"
)
dependency.in_extras.append("foo;")
expected = (
'poetry @ git+https://github.com/sdispater/poetry.git@b;ar; ; extra == "foo;"'
)
expected = 'poetry @ git+https://github.com/python-poetry/poetry.git@b;ar; ; extra == "foo;"'
assert expected == dependency.to_pep_508()
@pytest.mark.parametrize("category", ["main", "dev"])
def test_category(category):
dependency = VCSDependency(
"poetry",
"git",
"https://github.com/python-poetry/poetry.git",
category=category,
)
assert category == dependency.category
......@@ -407,8 +407,10 @@ def test_solver_returns_extras_if_requested(solver, repo, package):
package_b = get_package("B", "1.0")
package_c = get_package("C", "1.0")
package_b.extras = {"foo": [get_dependency("C", "^1.0")]}
package_b.add_dependency("C", {"version": "^1.0", "optional": True})
dep = get_dependency("C", "^1.0", optional=True)
dep.marker = parse_marker("extra == 'foo'")
package_b.extras = {"foo": [dep]}
package_b.requires.append(dep)
repo.add_package(package_a)
repo.add_package(package_b)
......@@ -425,6 +427,9 @@ def test_solver_returns_extras_if_requested(solver, repo, package):
],
)
assert ops[-1].package.marker.is_any()
assert ops[0].package.marker.is_any()
def test_solver_returns_prereleases_if_requested(solver, repo, package):
package.add_dependency("A")
......@@ -1148,10 +1153,7 @@ def test_solver_does_not_trigger_new_resolution_on_duplicate_dependencies_if_onl
],
)
assert str(ops[0].package.marker) in [
'extra == "foo" or extra == "bar"',
'extra == "bar" or extra == "foo"',
]
assert str(ops[0].package.marker) == ""
assert str(ops[1].package.marker) == ""
......@@ -1684,7 +1686,6 @@ def test_solver_chooses_from_correct_repository_if_forced(
ops, [{"job": "install", "package": get_package("tomlkit", "0.5.2")}]
)
assert "legacy" == ops[0].package.source_type
assert "http://foo.bar" == ops[0].package.source_url
......@@ -1713,7 +1714,6 @@ def test_solver_chooses_from_correct_repository_if_forced_and_transitive_depende
],
)
assert "legacy" == ops[0].package.source_type
assert "http://foo.bar" == ops[0].package.source_url
assert "" == ops[1].package.source_type
......@@ -1743,11 +1743,9 @@ def test_solver_does_not_choose_from_secondary_repository_by_default(
],
)
assert "legacy" == ops[0].package.source_type
assert "http://foo.bar" == ops[0].package.source_url
assert "" == ops[1].package.source_type
assert "" == ops[1].package.source_url
assert "legacy" == ops[2].package.source_type
assert "http://foo.bar" == ops[2].package.source_url
......@@ -1772,7 +1770,6 @@ def test_solver_chooses_from_secondary_if_explicit(package, installed, locked, i
],
)
assert "legacy" == ops[0].package.source_type
assert "http://foo.bar" == ops[0].package.source_url
assert "" == ops[1].package.source_type
assert "" == ops[1].package.source_url
......@@ -1834,3 +1831,100 @@ def test_solver_does_not_raise_conflict_for_conditional_dev_dependencies(
{"job": "install", "package": package_a200},
],
)
def test_solver_does_not_loop_indefinitely_on_duplicate_constraints_with_extras(
solver, repo, package
):
package.python_versions = "~2.7 || ^3.5"
package.add_dependency("requests", {"version": "^2.22.0", "extras": ["security"]})
requests = get_package("requests", "2.22.0")
requests.add_dependency("idna", ">=2.5,<2.9")
requests.add_dependency(
"idna", {"version": ">=2.0.0", "markers": "extra == 'security'"}
)
requests.extras["security"] = [get_dependency("idna", ">=2.0.0")]
idna = get_package("idna", "2.8")
repo.add_package(requests)
repo.add_package(idna)
ops = solver.solve()
check_solver_result(
ops,
[{"job": "install", "package": idna}, {"job": "install", "package": requests}],
)
def test_solver_does_not_fail_with_locked_git_and_non_git_dependencies(
solver, repo, package, locked, pool, installed, io
):
package.add_dependency("demo", {"git": "https://github.com/demo/demo.git"})
package.add_dependency("a", "^1.2.3")
git_package = get_package("demo", "0.1.2")
git_package.source_type = "git"
git_package.source_url = "https://github.com/demo/demo.git"
git_package.source_reference = "commit"
installed.add_package(git_package)
locked.add_package(get_package("a", "1.2.3"))
locked.add_package(git_package)
repo.add_package(get_package("a", "1.2.3"))
solver = Solver(package, pool, installed, locked, io)
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": get_package("a", "1.2.3")},
{"job": "install", "package": git_package, "skipped": True},
],
)
def test_ignore_python_constraint_no_overlap_dependencies(solver, repo, package):
pytest = get_package("demo", "1.0.0")
pytest.add_dependency("configparser", {"version": "^1.2.3", "python": "<3.2"})
package.add_dependency("demo", {"version": "^1.0.0", "python": "^3.6"})
repo.add_package(pytest)
repo.add_package(get_package("configparser", "1.2.3"))
ops = solver.solve()
check_solver_result(
ops, [{"job": "install", "package": pytest}],
)
def test_solver_properly_propagates_markers(solver, repo, package):
package.python_versions = "~2.7 || ^3.4"
package.add_dependency(
"A",
{
"version": "^1.0",
"markers": "python_version >= '3.6' and implementation_name != 'pypy'",
},
)
package_a = get_package("A", "1.0.0")
package_a.python_versions = ">=3.6"
repo.add_package(package_a)
ops = solver.solve()
check_solver_result(ops, [{"job": "install", "package": package_a}])
assert (
str(ops[0].package.marker)
== 'python_version >= "3.6" and implementation_name != "pypy"'
)
Metadata-Version: 2.1
Name: cleo
Version: 0.7.6
Summary: Cleo allows you to create beautiful and testable command-line interfaces.
Home-page: https://github.com/sdispater/cleo
License: MIT
Keywords: cli,commands
Author: Sébastien Eustace
Author-email: sebastien@eustace.io
Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
Classifier: License :: OSI Approved :: MIT License
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
Requires-Dist: clikit (>=0.4.0,<0.5.0)
Description-Content-Type: text/x-rst
Cleo
####
.. image:: https://travis-ci.org/sdispater/cleo.png
:alt: Cleo Build status
:target: https://travis-ci.org/sdispater/cleo
Create beautiful and testable command-line interfaces.
Cleo is mostly a higher level wrapper for `CliKit <https://github.com/sdispater/clikit>`_, so
a lot of the components and utilities comes from it. Refer to its documentation for more
information.
Resources
=========
* `Documentation <http://cleo.readthedocs.io>`_
* `Issue Tracker <https://github.com/sdispater/cleo/issues>`_
Usage
=====
To make a command that greets you from the command line,
create ``greet_command.py`` and add the following to it:
.. code-block:: python
from cleo import Command
class GreetCommand(Command):
"""
Greets someone
greet
{name? : Who do you want to greet?}
{--y|yell : If set, the task will yell in uppercase letters}
"""
def handle(self):
name = self.argument('name')
if name:
text = 'Hello {}'.format(name)
else:
text = 'Hello'
if self.option('yell'):
text = text.upper()
self.line(text)
You also need to create the file to run at the command line which creates
an ``Application`` and adds commands to it:
.. code-block:: python
#!/usr/bin/env python
from greet_command import GreetCommand
from cleo import Application
application = Application()
application.add(GreetCommand())
if __name__ == '__main__':
application.run()
Test the new command by running the following
.. code-block:: bash
$ python application.py greet John
This will print the following to the command line:
.. code-block:: text
Hello John
You can also use the ``--yell`` option to make everything uppercase:
.. code-block:: bash
$ python application.py greet John --yell
This prints:
.. code-block:: text
HELLO JOHN
As you may have already seen, Cleo uses the command docstring to determine
the command definition.
The docstring must be in the following form :
.. code-block:: python
"""
Command description
Command signature
"""
The signature being in the following form:
.. code-block:: python
"""
command:name {argument : Argument description} {--option : Option description}
"""
The signature can span multiple lines.
.. code-block:: python
"""
command:name
{argument : Argument description}
{--option : Option description}
"""
Coloring the Output
-------------------
Whenever you output text, you can surround the text with tags to color its
output. For example:
.. code-block:: python
# green text
self.line('<info>foo</info>')
# yellow text
self.line('<comment>foo</comment>')
# black text on a cyan background
self.line('<question>foo</question>')
# white text on a red background
self.line('<error>foo</error>')
The closing tag can be replaced by ``</>``, which revokes all formatting options established by the last opened tag.
It is possible to define your own styles using the ``add_style()`` method:
.. code-block:: python
self.add_style('fire', fg='red', bg='yellow', options=['bold', 'blink'])
self.line('<fire>foo</fire>')
Available foreground and background colors are: ``black``, ``red``, ``green``,
``yellow``, ``blue``, ``magenta``, ``cyan`` and ``white``.
And available options are: ``bold``, ``underscore``, ``blink``, ``reverse`` and ``conceal``.
You can also set these colors and options inside the tag name:
.. code-block:: python
# green text
self.line('<fg=green>foo</>')
# black text on a cyan background
self.line('<fg=black;bg=cyan>foo</>')
# bold text on a yellow background
self.line('<bg=yellow;options=bold>foo</>')
Verbosity Levels
----------------
Cleo has four verbosity levels. These are defined in the ``Output`` class:
======================================= ================================== ======================
Mode Meaning Console option
======================================= ================================== ======================
``NA`` Do not output any messages ``-q`` or ``--quiet``
``clikit.VERBOSITY_NORMAL`` The default verbosity level (none)
``clikit.VERBOSITY_VERBOSE`` Increased verbosity of messages ``-v``
``clikit.VERBOSITY_VERY_VERBOSE`` Informative non essential messages ``-vv``
``clikit.VERBOSITY_DEBUG`` Debug messages ``-vvv``
======================================= ================================== ======================
It is possible to print a message in a command for only a specific verbosity
level. For example:
.. code-block:: python
if clikit.VERBOSITY_VERBOSE <= self.io.verbosity:
self.line(...)
There are also more semantic methods you can use to test for each of the
verbosity levels:
.. code-block:: python
if self.output.is_quiet():
# ...
if self.output.is_verbose():
# ...
You can also pass the verbosity flag directly to `line()`.
.. code-block:: python
self.line("", verbosity=clikit.VERBOSITY_VERBOSE)
When the quiet level is used, all output is suppressed.
Using Arguments
---------------
The most interesting part of the commands are the arguments and options that
you can make available. Arguments are the strings - separated by spaces - that
come after the command name itself. They are ordered, and can be optional
or required. For example, add an optional ``last_name`` argument to the command
and make the ``name`` argument required:
.. code-block:: python
class GreetCommand(Command):
"""
Greets someone
greet
{name : Who do you want to greet?}
{last_name? : Your last name?}
{--y|yell : If set, the task will yell in uppercase letters}
"""
You now have access to a ``last_name`` argument in your command:
.. code-block:: python
last_name = self.argument('last_name')
if last_name:
text += ' {}'.format(last_name)
The command can now be used in either of the following ways:
.. code-block:: bash
$ python application.py greet John
$ python application.py greet John Doe
It is also possible to let an argument take a list of values (imagine you want
to greet all your friends). For this it must be specified at the end of the
argument list:
.. code-block:: python
class GreetCommand(Command):
"""
Greets someone
greet
{names* : Who do you want to greet?}
{--y|yell : If set, the task will yell in uppercase letters}
"""
To use this, just specify as many names as you want:
.. code-block:: bash
$ python application.py demo:greet John Jane
You can access the ``names`` argument as a list:
.. code-block:: python
names = self.argument('names')
if names:
text += ' {}'.format(', '.join(names))
There are 3 argument variants you can use:
================================ ==================================== ===============================================================================================================
Mode Notation Value
================================ ==================================== ===============================================================================================================
``clikit.ARGUMENT_REQUIRED`` none (just write the argument name) The argument is required
``clikit.ARGUMENT_OPTIONAL`` ``argument?`` The argument is optional and therefore can be omitted
``clikit.ARGUMENT_MULTI_VALUED`` ``argument*`` The argument can contain an indefinite number of arguments and must be used at the end of the argument list
================================ ==================================== ===============================================================================================================
You can combine them like this:
.. code-block:: python
class GreetCommand(Command):
"""
Greets someone
greet
{names?* : Who do you want to greet?}
{--y|yell : If set, the task will yell in uppercase letters}
"""
If you want to set a default value, you can it like so:
.. code-block:: text
argument=default
The argument will then be considered optional.
Using Options
-------------
Unlike arguments, options are not ordered (meaning you can specify them in any
order) and are specified with two dashes (e.g. ``--yell`` - you can also
declare a one-letter shortcut that you can call with a single dash like
``-y``). Options are *always* optional, and can be setup to accept a value
(e.g. ``--dir=src``) or simply as a boolean flag without a value (e.g.
``--yell``).
.. tip::
It is also possible to make an option *optionally* accept a value (so that
``--yell`` or ``--yell=loud`` work). Options can also be configured to
accept a list of values.
For example, add a new option to the command that can be used to specify
how many times in a row the message should be printed:
.. code-block:: python
class GreetCommand(Command):
"""
Greets someone
greet
{name? : Who do you want to greet?}
{--y|yell : If set, the task will yell in uppercase letters}
{--iterations=1 : How many times should the message be printed?}
"""
Next, use this in the command to print the message multiple times:
.. code-block:: python
for _ in range(0, self.option('iterations')):
self.line(text)
Now, when you run the task, you can optionally specify a ``--iterations``
flag:
.. code-block:: bash
$ python application.py demo:greet John
$ python application.py demo:greet John --iterations=5
The first example will only print once, since ``iterations`` is empty and
defaults to ``1``. The second example will print five times.
Recall that options don't care about their order. So, either of the following
will work:
.. code-block:: bash
$ python application.py demo:greet John --iterations=5 --yell
$ python application.py demo:greet John --yell --iterations=5
There are 4 option variants you can use:
================================ =================================== ======================================================================================
Option Notation Value
================================ =================================== ======================================================================================
``clikit.OPTION_MULTI_VALUED`` ``--option=*`` This option accepts multiple values (e.g. ``--dir=/foo --dir=/bar``)
``clikit.OPTION_NO_VALUE`` ``--option`` Do not accept input for this option (e.g. ``--yell``)
``clikit.OPTION_REQUIRED_VALUE`` ``--option=`` This value is required (e.g. ``--iterations=5``), the option itself is still optional
``clikit.OPTION_OPTIONAL_VALUE`` ``--option=?`` This option may or may not have a value (e.g. ``--yell`` or ``--yell=loud``)
================================ =================================== ======================================================================================
You can combine them like this:
.. code-block:: python
class GreetCommand(Command):
"""
Greets someone
greet
{name? : Who do you want to greet?}
{--y|yell : If set, the task will yell in uppercase letters}
{--iterations=?*1 : How many times should the message be printed?}
"""
Testing Commands
----------------
Cleo provides several tools to help you test your commands. The most
useful one is the ``CommandTester`` class.
It uses a special IO class to ease testing without a real
console:
.. code-block:: python
import pytest
from cleo import Application
from cleo import CommandTester
def test_execute(self):
application = Application()
application.add(GreetCommand())
command = application.find('demo:greet')
command_tester = CommandTester(command)
command_tester.execute()
assert "..." == tester.io.fetch_output()
The ``CommandTester.io.fetch_output()`` method returns what would have been displayed
during a normal call from the console. ``CommandTester.io.fetch_error()`` is also available
to get what you have been written to the stderr.
You can test sending arguments and options to the command by passing them
as a string to the ``CommandTester.execute()`` method:
.. code-block:: python
import pytest
from cleo import Application
from cleo import CommandTester
def test_execute(self):
application = Application()
application.add(GreetCommand())
command = application.find('demo:greet')
command_tester = CommandTester(command)
command_tester.execute("John")
assert "John" in tester.io.fetch_output()
You can also test a whole console application by using the ``ApplicationTester`` class.
Calling an existing Command
---------------------------
If a command depends on another one being run before it, instead of asking the
user to remember the order of execution, you can call it directly yourself.
This is also useful if you want to create a "meta" command that just runs a
bunch of other commands.
Calling a command from another one is straightforward:
.. code-block:: python
def handle(self):
return_code = self.call('demo:greet', "John --yell")
# ...
If you want to suppress the output of the executed command,
you can use the ``call_silent()`` method instead.
Autocompletion
--------------
Cleo supports automatic (tab) completion in ``bash``, ``zsh`` and ``fish``.
To activate support for autocompletion, pass a ``complete`` keyword when initializing
your application:
.. code-block:: python
application = Application('My Application', '0.1', complete=True)
Now, register completion for your application by running one of the following in a terminal,
replacing ``[program]`` with the command you use to run your application:
.. code-block:: bash
# BASH - Ubuntu / Debian
[program] completions bash | sudo tee /etc/bash_completion.d/[program].bash-completion
# BASH - Mac OSX (with Homebrew "bash-completion")
[program] completions bash > $(brew --prefix)/etc/bash_completion.d/[program].bash-completion
# ZSH - Config file
mkdir ~/.zfunc
echo "fpath+=~/.zfunc" >> ~/.zshrc
[program] completions zsh > ~/.zfunc/_test
# FISH
[program] completions fish > ~/.config/fish/completions/[program].fish
Metadata-Version: 1.2
Name: pendulum
Version: 2.0.5
Summary: Python datetimes made easy
Home-page: https://pendulum.eustace.io
Author: Sébastien Eustace
Author-email: sebastien@eustace.io
License: UNKNOWN
Description: Pendulum
########
.. image:: https://img.shields.io/pypi/v/pendulum.svg
:target: https://pypi.python.org/pypi/pendulum
.. image:: https://img.shields.io/pypi/l/pendulum.svg
:target: https://pypi.python.org/pypi/pendulum
.. image:: https://img.shields.io/codecov/c/github/sdispater/pendulum/master.svg
:target: https://codecov.io/gh/sdispater/pendulum/branch/master
.. image:: https://travis-ci.org/sdispater/pendulum.svg
:alt: Pendulum Build status
:target: https://travis-ci.org/sdispater/pendulum
Python datetimes made easy.
Supports Python **2.7** and **3.4+**.
.. code-block:: python
>>> import pendulum
>>> now_in_paris = pendulum.now('Europe/Paris')
>>> now_in_paris
'2016-07-04T00:49:58.502116+02:00'
# Seamless timezone switching
>>> now_in_paris.in_timezone('UTC')
'2016-07-03T22:49:58.502116+00:00'
>>> tomorrow = pendulum.now().add(days=1)
>>> last_week = pendulum.now().subtract(weeks=1)
>>> past = pendulum.now().subtract(minutes=2)
>>> past.diff_for_humans()
>>> '2 minutes ago'
>>> delta = past - last_week
>>> delta.hours
23
>>> delta.in_words(locale='en')
'6 days 23 hours 58 minutes'
# Proper handling of datetime normalization
>>> pendulum.datetime(2013, 3, 31, 2, 30, tz='Europe/Paris')
'2013-03-31T03:30:00+02:00' # 2:30 does not exist (Skipped time)
# Proper handling of dst transitions
>>> just_before = pendulum.datetime(2013, 3, 31, 1, 59, 59, 999999, tz='Europe/Paris')
'2013-03-31T01:59:59.999999+01:00'
>>> just_before.add(microseconds=1)
'2013-03-31T03:00:00+02:00'
Why Pendulum?
=============
Native ``datetime`` instances are enough for basic cases but when you face more complex use-cases
they often show limitations and are not so intuitive to work with.
``Pendulum`` provides a cleaner and more easy to use API while still relying on the standard library.
So it's still ``datetime`` but better.
Unlike other datetime libraries for Python, Pendulum is a drop-in replacement
for the standard ``datetime`` class (it inherits from it), so, basically, you can replace all your ``datetime``
instances by ``DateTime`` instances in you code (exceptions exist for libraries that check
the type of the objects by using the ``type`` function like ``sqlite3`` or ``PyMySQL`` for instance).
It also removes the notion of naive datetimes: each ``Pendulum`` instance is timezone-aware
and by default in ``UTC`` for ease of use.
Pendulum also improves the standard ``timedelta`` class by providing more intuitive methods and properties.
Why not Arrow?
==============
Arrow is the most popular datetime library for Python right now, however its behavior
and API can be erratic and unpredictable. The ``get()`` method can receive pretty much anything
and it will try its best to return something while silently failing to handle some cases:
.. code-block:: python
arrow.get('2016-1-17')
# <Arrow [2016-01-01T00:00:00+00:00]>
pendulum.parse('2016-1-17')
# <Pendulum [2016-01-17T00:00:00+00:00]>
arrow.get('20160413')
# <Arrow [1970-08-22T08:06:53+00:00]>
pendulum.parse('20160413')
# <Pendulum [2016-04-13T00:00:00+00:00]>
arrow.get('2016-W07-5')
# <Arrow [2016-01-01T00:00:00+00:00]>
pendulum.parse('2016-W07-5')
# <Pendulum [2016-02-19T00:00:00+00:00]>
# Working with DST
just_before = arrow.Arrow(2013, 3, 31, 1, 59, 59, 999999, 'Europe/Paris')
just_after = just_before.replace(microseconds=1)
'2013-03-31T02:00:00+02:00'
# Should be 2013-03-31T03:00:00+02:00
(just_after.to('utc') - just_before.to('utc')).total_seconds()
-3599.999999
# Should be 1e-06
just_before = pendulum.datetime(2013, 3, 31, 1, 59, 59, 999999, 'Europe/Paris')
just_after = just_before.add(microseconds=1)
'2013-03-31T03:00:00+02:00'
(just_after.in_timezone('utc') - just_before.in_timezone('utc')).total_seconds()
1e-06
Those are a few examples showing that Arrow cannot always be trusted to have a consistent
behavior with the data you are passing to it.
Limitations
===========
Even though the ``DateTime`` class is a subclass of ``datetime`` there are some rare cases where
it can't replace the native class directly. Here is a list (non-exhaustive) of the reported cases with
a possible solution, if any:
* ``sqlite3`` will use the ``type()`` function to determine the type of the object by default. To work around it you can register a new adapter:
.. code-block:: python
from pendulum import DateTime
from sqlite3 import register_adapter
register_adapter(DateTime, lambda val: val.isoformat(' '))
* ``mysqlclient`` (former ``MySQLdb``) and ``PyMySQL`` will use the ``type()`` function to determine the type of the object by default. To work around it you can register a new adapter:
.. code-block:: python
import MySQLdb.converters
import pymysql.converters
from pendulum import DateTime
MySQLdb.converters.conversions[DateTime] = MySQLdb.converters.DateTime2literal
pymysql.converters.conversions[DateTime] = pymysql.converters.escape_datetime
* ``django`` will use the ``isoformat()`` method to store datetimes in the database. However since ``pendulum`` is always timezone aware the offset information will always be returned by ``isoformat()`` raising an error, at least for MySQL databases. To work around it you can either create your own ``DateTimeField`` or use the previous workaround for ``MySQLdb``:
.. code-block:: python
from django.db.models import DateTimeField as BaseDateTimeField
from pendulum import DateTime
class DateTimeField(BaseDateTimeField):
def value_to_string(self, obj):
val = self.value_from_object(obj)
if isinstance(value, DateTime):
return value.to_datetime_string()
return '' if val is None else val.isoformat()
Resources
=========
* `Official Website <https://pendulum.eustace.io>`_
* `Documentation <https://pendulum.eustace.io/docs/>`_
* `Issue Tracker <https://github.com/sdispater/pendulum/issues>`_
Contributing
============
Contributions are welcome, especially with localization.
Getting started
---------------
To work on the Pendulum codebase, you'll want to clone the project locally
and install the required depedendencies via `poetry <https://python-poetry.org>`_.
.. code-block:: bash
$ git clone git@github.com:sdispater/pendulum.git
$ poetry install
Localization
------------
If you want to help with localization, there are two different cases: the locale already exists
or not.
If the locale does not exist you will need to create it by using the ``clock`` utility:
.. code-block:: bash
./clock locale create <your-locale>
It will generate a directory in ``pendulum/locales`` named after your locale, with the following
structure:
.. code-block:: text
<your-locale>/
- custom.py
- locale.py
The ``locale.py`` file must not be modified. It contains the translations provided by
the CLDR database.
The ``custom.py`` file is the one you want to modify. It contains the data needed
by Pendulum that are not provided by the CLDR database. You can take the `en <https://github.com/sdispater/pendulum/tree/master/pendulum/locales/en/custom.py>`_
data as a reference to see which data is needed.
You should also add tests for the created or modified locale.
Platform: UNKNOWN
Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
python-dateutil<3.0,>=2.6
pytzdata>=2018.3
[:python_version < "3.5"]
typing<4.0,>=3.6
......@@ -24,7 +24,7 @@
"last_month": -1,
"last_week": -1
},
"home_page": "https://poetry.eustace.io/",
"home_page": "https://python-poetry.org/",
"keywords": "packaging,dependency,poetry",
"license": "MIT",
"maintainer": "Sébastien Eustace",
......@@ -34,9 +34,9 @@
"platform": "",
"project_url": "https://pypi.org/project/poetry/",
"project_urls": {
"Documentation": "https://poetry.eustace.io/docs",
"Homepage": "https://poetry.eustace.io/",
"Repository": "https://github.com/sdispater/poetry"
"Documentation": "https://python-poetry.org/docs",
"Homepage": "https://python-poetry.org/",
"Repository": "https://github.com/python-poetry/poetry"
},
"release_url": "https://pypi.org/project/poetry/0.12.4/",
"requires_dist": [
......
......@@ -24,7 +24,7 @@
"last_month": -1,
"last_week": -1
},
"home_page": "https://poetry.eustace.io/",
"home_page": "https://python-poetry.org/",
"keywords": "packaging,dependency,poetry",
"license": "MIT",
"maintainer": "Sébastien Eustace",
......@@ -34,9 +34,9 @@
"platform": "",
"project_url": "https://pypi.org/project/poetry/",
"project_urls": {
"Documentation": "https://poetry.eustace.io/docs",
"Homepage": "https://poetry.eustace.io/",
"Repository": "https://github.com/sdispater/poetry"
"Documentation": "https://python-poetry.org/docs",
"Homepage": "https://python-poetry.org/",
"Repository": "https://github.com/python-poetry/poetry"
},
"release_url": "https://pypi.org/project/poetry/0.12.4/",
"requires_dist": [
......
......@@ -8,9 +8,9 @@ from poetry.utils._compat import encode
def test_auth_with_request_on_the_same_host():
auth = Auth("https://poetry.eustace.io", "foo", "bar")
auth = Auth("https://python-poetry.org", "foo", "bar")
request = Request("GET", "https://poetry.eustace.io/docs/")
request = Request("GET", "https://python-poetry.org/docs/")
assert "Authorization" not in request.headers
request = auth(request)
......@@ -22,9 +22,9 @@ def test_auth_with_request_on_the_same_host():
def test_auth_with_request_with_same_authentication():
auth = Auth("https://poetry.eustace.io", "foo", "bar")
auth = Auth("https://python-poetry.org", "foo", "bar")
request = Request("GET", "https://foo:bar@poetry.eustace.io/docs/")
request = Request("GET", "https://foo:bar@python-poetry.org/docs/")
assert "Authorization" not in request.headers
request = auth(request)
......@@ -36,7 +36,7 @@ def test_auth_with_request_with_same_authentication():
def test_auth_with_request_on_different_hosts():
auth = Auth("https://poetry.eustace.io", "foo", "bar")
auth = Auth("https://python-poetry.org", "foo", "bar")
request = Request("GET", "https://pendulum.eustace.io/docs/")
assert "Authorization" not in request.headers
......
from poetry.repositories.installed_repository import InstalledRepository
from poetry.utils._compat import Path
from poetry.utils._compat import metadata
from poetry.utils._compat import zipp
from poetry.utils.env import MockEnv as BaseMockEnv
FREEZE_RESULTS = """cleo==0.6.8
-e git+https://github.com/sdispater/pendulum.git@bb058f6b78b2d28ef5d9a5e759cfa179a1a713d6#egg=pendulum
orator===0.9.8
"""
FIXTURES_DIR = Path(__file__).parent / "fixtures"
ENV_DIR = (FIXTURES_DIR / "installed").resolve()
SITE_PACKAGES = ENV_DIR / "lib" / "python3.7" / "site-packages"
SRC = ENV_DIR / "src"
INSTALLED_RESULTS = [
metadata.PathDistribution(SITE_PACKAGES / "cleo-0.7.6.dist-info"),
metadata.PathDistribution(SRC / "pendulum" / "pendulum.egg-info"),
metadata.PathDistribution(
zipp.Path(str(SITE_PACKAGES / "foo-0.1.0-py3.8.egg"), "EGG-INFO")
),
]
class MockEnv(BaseMockEnv):
def run(self, bin, *args):
if bin == "python" and args[:3] == ("-m", "pip", "freeze"):
return FREEZE_RESULTS
@property
def site_packages(self): # type: () -> Path
return SITE_PACKAGES
return super(MockEnv, self).run(bin, *args)
def test_load():
repository = InstalledRepository.load(MockEnv())
def test_load(mocker):
mocker.patch(
"poetry.utils._compat.metadata.Distribution.discover",
return_value=INSTALLED_RESULTS,
)
mocker.patch(
"poetry.vcs.git.Git.rev_parse",
return_value="bb058f6b78b2d28ef5d9a5e759cfa179a1a713d6",
)
mocker.patch(
"poetry.vcs.git.Git.remote_urls",
side_effect=[
{"remote.origin.url": "https://github.com/sdispater/pendulum.git"},
{"remote.origin.url": "git@github.com:sdispater/pendulum.git"},
],
)
repository = InstalledRepository.load(MockEnv(path=ENV_DIR))
assert len(repository.packages) == 3
cleo = repository.packages[0]
assert cleo.name == "cleo"
assert cleo.version.text == "0.6.8"
assert cleo.version.text == "0.7.6"
assert (
cleo.description
== "Cleo allows you to create beautiful and testable command-line interfaces."
)
foo = repository.packages[1]
assert foo.name == "foo"
assert foo.version.text == "0.1.0"
pendulum = repository.packages[1]
pendulum = repository.packages[2]
assert pendulum.name == "pendulum"
assert pendulum.version.text == "0.0.0"
assert pendulum.version.text == "2.0.5"
assert pendulum.description == "Python datetimes made easy"
assert pendulum.source_type == "git"
assert pendulum.source_url == "https://github.com/sdispater/pendulum.git"
assert pendulum.source_reference == "bb058f6b78b2d28ef5d9a5e759cfa179a1a713d6"
orator = repository.packages[2]
assert orator.name == "orator"
assert orator.version.text == "0.9.8"
import json
import shutil
from io import BytesIO
import pytest
from requests.exceptions import TooManyRedirects
from requests.models import Response
from poetry.packages import Dependency
from poetry.repositories.pypi_repository import PyPiRepository
from poetry.utils._compat import PY35
from poetry.utils._compat import Path
from poetry.utils._compat import encode
class MockRepository(PyPiRepository):
......@@ -186,3 +192,19 @@ def test_invalid_versions_ignored():
# and a correct one.
packages = repo.find_packages("pygame-music-grid")
assert len(packages) == 1
def test_get_should_invalid_cache_on_too_many_redirects_error(mocker):
delete_cache = mocker.patch("cachecontrol.caches.file_cache.FileCache.delete")
response = Response()
response.encoding = "utf-8"
response.raw = BytesIO(encode('{"foo": "bar"}'))
mocker.patch(
"cachecontrol.adapter.CacheControlAdapter.send",
side_effect=[TooManyRedirects(), response],
)
repository = PyPiRepository()
repository._get("https://pypi.org/pypi/async-timeout/json")
assert delete_cache.called
......@@ -68,6 +68,10 @@ def test_parse_constraint_wildcard(input, constraint):
("~3.5", VersionRange(Version(3, 5, 0), Version(3, 6, 0), True)),
("~=3.5", VersionRange(Version(3, 5, 0), Version(4, 0, 0), True)), # PEP 440
("~=3.5.3", VersionRange(Version(3, 5, 3), Version(3, 6, 0), True)), # PEP 440
(
"~=3.5.3rc1",
VersionRange(Version(3, 5, 3, pre="rc1"), Version(3, 6, 0), True),
), # PEP 440
],
)
def test_parse_constraint_tilde(input, constraint):
......
......@@ -27,8 +27,8 @@ def test_create_poetry():
package.readme.relative_to(fixtures_dir).as_posix()
== "sample_project/README.rst"
)
assert package.homepage == "https://poetry.eustace.io"
assert package.repository_url == "https://github.com/sdispater/poetry"
assert package.homepage == "https://python-poetry.org"
assert package.repository_url == "https://github.com/python-poetry/poetry"
assert package.keywords == ["packaging", "dependency", "poetry"]
assert package.python_versions == "~2.7 || ^3.6"
......
......@@ -9,9 +9,9 @@ license = "MIT"
readme = "README.md"
homepage = "https://poetry.eustace.io/"
repository = "https://github.com/sdispater/poetry"
documentation = "https://poetry.eustace.io/docs"
homepage = "https://python-poetry.org/"
repository = "https://github.com/python-poetry/poetry"
documentation = "https://python-poetry.org/docs"
keywords = ["packaging", "dependency", "poetry"]
......
import setuptools
setuptools.setup(
name="my_package",
version="0.1.2",
author="John Doe",
author_email="john@example.orh",
description="Just a description",
url="https://example.org",
packages=setuptools.find_packages(),
)
......@@ -9,6 +9,7 @@ from clikit.io import NullIO
from poetry.factory import Factory
from poetry.semver import Version
from poetry.utils._compat import WINDOWS
from poetry.utils._compat import Path
from poetry.utils.env import EnvCommandError
from poetry.utils.env import EnvManager
......@@ -30,6 +31,20 @@ print("nullpackage loaded"),
"""
class MockVirtualEnv(VirtualEnv):
def __init__(self, path, base=None, sys_path=None):
super(MockVirtualEnv, self).__init__(path, base=base)
self._sys_path = sys_path
@property
def sys_path(self):
if self._sys_path is not None:
return self._sys_path
return super(MockVirtualEnv, self).sys_path
@pytest.fixture()
def poetry(config):
poetry = Factory().create_poetry(
......@@ -661,3 +676,170 @@ def test_create_venv_does_not_try_to_find_compatible_versions_with_executable(
assert expected_message == str(e.value)
assert 0 == m.call_count
def test_create_venv_uses_patch_version_to_detect_compatibility(
manager, poetry, config, mocker
):
if "VIRTUAL_ENV" in os.environ:
del os.environ["VIRTUAL_ENV"]
version = Version.parse(".".join(str(c) for c in sys.version_info[:3]))
poetry.package.python_versions = "^{}".format(
".".join(str(c) for c in sys.version_info[:3])
)
venv_name = manager.generate_env_name("simple-project", str(poetry.file.parent))
mocker.patch("sys.version_info", (version.major, version.minor, version.patch + 1))
check_output = mocker.patch(
"poetry.utils._compat.subprocess.check_output",
side_effect=check_output_wrapper(Version.parse("3.6.9")),
)
m = mocker.patch(
"poetry.utils.env.EnvManager.build_venv", side_effect=lambda *args, **kwargs: ""
)
manager.create_venv(NullIO())
assert not check_output.called
m.assert_called_with(
str(
Path(
"/foo/virtualenvs/{}-py{}.{}".format(
venv_name, version.major, version.minor
)
)
),
executable=None,
)
def test_create_venv_uses_patch_version_to_detect_compatibility_with_executable(
manager, poetry, config, mocker
):
if "VIRTUAL_ENV" in os.environ:
del os.environ["VIRTUAL_ENV"]
version = Version.parse(".".join(str(c) for c in sys.version_info[:3]))
poetry.package.python_versions = "~{}".format(
".".join(str(c) for c in (version.major, version.minor - 1, 0))
)
venv_name = manager.generate_env_name("simple-project", str(poetry.file.parent))
check_output = mocker.patch(
"poetry.utils._compat.subprocess.check_output",
side_effect=check_output_wrapper(
Version.parse("{}.{}.0".format(version.major, version.minor - 1))
),
)
m = mocker.patch(
"poetry.utils.env.EnvManager.build_venv", side_effect=lambda *args, **kwargs: ""
)
manager.create_venv(
NullIO(), executable="python{}.{}".format(version.major, version.minor - 1)
)
assert check_output.called
m.assert_called_with(
str(
Path(
"/foo/virtualenvs/{}-py{}.{}".format(
venv_name, version.major, version.minor - 1
)
)
),
executable="python{}.{}".format(version.major, version.minor - 1),
)
def test_activate_with_in_project_setting_does_not_fail_if_no_venvs_dir(
manager, poetry, config, tmp_dir, mocker
):
if "VIRTUAL_ENV" in os.environ:
del os.environ["VIRTUAL_ENV"]
config.merge(
{
"virtualenvs": {
"path": str(Path(tmp_dir) / "virtualenvs"),
"in-project": True,
}
}
)
mocker.patch(
"poetry.utils._compat.subprocess.check_output",
side_effect=check_output_wrapper(),
)
mocker.patch(
"poetry.utils._compat.subprocess.Popen.communicate",
side_effect=[("/prefix", None), ("/prefix", None)],
)
m = mocker.patch("poetry.utils.env.EnvManager.build_venv")
manager.activate("python3.7", NullIO())
m.assert_called_with(
os.path.join(str(poetry.file.parent), ".venv"), executable="python3.7"
)
envs_file = TomlFile(Path(tmp_dir) / "virtualenvs" / "envs.toml")
assert not envs_file.exists()
def test_env_site_packages_should_find_the_site_packages_directory_if_standard(tmp_dir):
if WINDOWS:
site_packages = Path(tmp_dir).joinpath("Lib/site-packages")
else:
site_packages = Path(tmp_dir).joinpath(
"lib/python{}/site-packages".format(
".".join(str(v) for v in sys.version_info[:2])
)
)
site_packages.mkdir(parents=True)
env = MockVirtualEnv(Path(tmp_dir), Path(tmp_dir), sys_path=[str(site_packages)])
assert site_packages == env.site_packages
def test_env_site_packages_should_find_the_site_packages_directory_if_root(tmp_dir):
site_packages = Path(tmp_dir).joinpath("site-packages")
site_packages.mkdir(parents=True)
env = MockVirtualEnv(Path(tmp_dir), Path(tmp_dir), sys_path=[str(site_packages)])
assert site_packages == env.site_packages
def test_env_site_packages_should_find_the_dist_packages_directory_if_necessary(
tmp_dir,
):
site_packages = Path(tmp_dir).joinpath("dist-packages")
site_packages.mkdir(parents=True)
env = MockVirtualEnv(Path(tmp_dir), Path(tmp_dir), sys_path=[str(site_packages)])
assert site_packages == env.site_packages
def test_env_site_packages_should_prefer_site_packages_over_dist_packages(tmp_dir):
dist_packages = Path(tmp_dir).joinpath("dist-packages")
dist_packages.mkdir(parents=True)
site_packages = Path(tmp_dir).joinpath("site-packages")
site_packages.mkdir(parents=True)
env = MockVirtualEnv(
Path(tmp_dir), Path(tmp_dir), sys_path=[str(dist_packages), str(site_packages)]
)
assert site_packages == env.site_packages
def test_env_site_packages_should_raise_an_error_if_no_site_packages(tmp_dir):
env = MockVirtualEnv(Path(tmp_dir), Path(tmp_dir), sys_path=[])
with pytest.raises(RuntimeError):
env.site_packages
......@@ -603,7 +603,7 @@ def test_exporter_can_export_requirements_txt_with_file_packages(tmp_dir, poetry
content = f.read()
expected = """\
-e tests/fixtures/distributions/demo-0.1.0.tar.gz
tests/fixtures/distributions/demo-0.1.0.tar.gz
"""
assert expected == content
......@@ -644,7 +644,7 @@ def test_exporter_can_export_requirements_txt_with_file_packages_and_markers(
content = f.read()
expected = """\
-e tests/fixtures/distributions/demo-0.1.0.tar.gz; python_version < "3.7"
tests/fixtures/distributions/demo-0.1.0.tar.gz; python_version < "3.7"
"""
assert expected == content
......@@ -707,6 +707,86 @@ foo==1.2.3 \\
assert expected == content
def test_exporter_exports_requirements_txt_with_legacy_packages_and_duplicate_sources(
tmp_dir, poetry
):
poetry.pool.add_repository(
LegacyRepository(
"custom",
"https://example.com/simple",
auth=Auth("https://example.com/simple", "foo", "bar"),
)
)
poetry.pool.add_repository(LegacyRepository("custom", "https://foobaz.com/simple",))
poetry.locker.mock_lock_data(
{
"package": [
{
"name": "foo",
"version": "1.2.3",
"category": "main",
"optional": False,
"python-versions": "*",
"source": {
"type": "legacy",
"url": "https://example.com/simple",
"reference": "",
},
},
{
"name": "bar",
"version": "4.5.6",
"category": "dev",
"optional": False,
"python-versions": "*",
"source": {
"type": "legacy",
"url": "https://example.com/simple",
"reference": "",
},
},
{
"name": "baz",
"version": "7.8.9",
"category": "dev",
"optional": False,
"python-versions": "*",
"source": {
"type": "legacy",
"url": "https://foobaz.com/simple",
"reference": "",
},
},
],
"metadata": {
"python-versions": "*",
"content-hash": "123456789",
"hashes": {"foo": ["12345"], "bar": ["67890"], "baz": ["24680"]},
},
}
)
exporter = Exporter(poetry)
exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt", dev=True)
with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f:
content = f.read()
expected = """\
--extra-index-url https://example.com/simple
--extra-index-url https://foobaz.com/simple
bar==4.5.6 \\
--hash=sha256:67890
baz==7.8.9 \\
--hash=sha256:24680
foo==1.2.3 \\
--hash=sha256:12345
"""
assert expected == content
def test_exporter_exports_requirements_txt_with_legacy_packages_and_credentials(
tmp_dir, poetry, config
):
......
from poetry.utils._compat import Path
from poetry.utils.helpers import get_cert
from poetry.utils.helpers import get_client_cert
from poetry.utils.helpers import get_http_basic_auth
from poetry.utils.helpers import parse_requires
......@@ -54,22 +53,6 @@ zipfile36>=0.1.0.0,<0.2.0.0
assert result == expected
def test_get_http_basic_auth(config):
config.merge({"http-basic": {"foo": {"username": "foo", "password": "bar"}}})
assert get_http_basic_auth(config, "foo") == ("foo", "bar")
def test_get_http_basic_auth_without_password(config):
config.merge({"http-basic": {"foo": {"username": "foo"}}})
assert get_http_basic_auth(config, "foo") == ("foo", None)
def test_get_http_basic_auth_missing(config):
assert get_http_basic_auth(config, "foo") is None
def test_get_cert(config):
ca_cert = "path/to/ca.pem"
config.merge({"certificates": {"foo": {"cert": ca_cert}}})
......
from uuid import uuid4
import pytest
from keyring import get_keyring
from keyring import set_keyring
from keyring.backend import KeyringBackend
from keyring.errors import KeyringError
from poetry.utils.helpers import keyring_repository_password_del
from poetry.utils.helpers import keyring_repository_password_get
from poetry.utils.helpers import keyring_repository_password_set
from poetry.utils.helpers import keyring_service_name
class DictKeyring(KeyringBackend):
priority = 1
def __init__(self):
self._storage = dict()
def set_password(self, servicename, username, password):
if servicename not in self._storage:
self._storage[servicename] = dict()
self._storage[servicename][username] = password
def get_password(self, servicename, username):
if servicename in self._storage:
return self._storage[servicename].get(username)
def delete_password(self, servicename, username):
if servicename in self._storage:
if username in self._storage[servicename]:
del self._storage[servicename][username]
if not self._storage[servicename]:
del self._storage[servicename]
class BrokenKeyring(KeyringBackend):
priority = 1
def set_password(self, servicename, username, password):
raise KeyringError()
def get_password(self, servicename, username):
raise KeyringError()
def delete_password(self, servicename, username):
raise KeyringError()
@pytest.fixture
def keyring(): # type: () -> KeyringBackend
k = DictKeyring()
set_keyring(k)
return k
@pytest.fixture
def broken_keyring(): # type: () -> KeyringBackend
k = BrokenKeyring()
set_keyring(k)
return k
@pytest.fixture
def repository(): # type: () -> str
return "test"
@pytest.fixture
def username(): # type: () -> str
return "username"
@pytest.fixture
def password(): # type: () -> str
return str(uuid4())
def test_keyring_repository_password_get(keyring, repository, username, password):
keyring.set_password(keyring_service_name(repository), username, password)
assert keyring_repository_password_get(repository, username) == password
def test_keyring_repository_password_get_not_set(keyring, repository, username):
assert keyring.get_password(keyring_service_name(repository), username) is None
assert keyring_repository_password_get(repository, username) is None
def test_keyring_repository_password_get_broken(broken_keyring):
assert get_keyring() == broken_keyring
assert keyring_repository_password_get("repository", "username") is None
def test_keyring_repository_password_set(keyring, repository, username, password):
keyring_repository_password_set(repository, username, password)
assert keyring.get_password(keyring_service_name(repository), username) == password
def test_keyring_repository_password_set_broken(broken_keyring):
assert get_keyring() == broken_keyring
with pytest.raises(RuntimeError):
keyring_repository_password_set(repository, "username", "password")
def test_keyring_repository_password_del(
keyring, config, repository, username, password
):
keyring.set_password(keyring_service_name(repository), username, password)
config.merge({"http-basic": {repository: {"username": username}}})
keyring_repository_password_del(config, repository)
assert keyring.get_password(keyring_service_name(repository), username) is None
def test_keyring_repository_password_del_not_set(keyring, config, repository, username):
config.merge({"http-basic": {repository: {"username": username}}})
keyring_repository_password_del(config, repository)
assert keyring.get_password(keyring_service_name(repository), username) is None
def test_keyring_repository_password_del_broken(broken_keyring, config):
assert get_keyring() == broken_keyring
keyring_repository_password_del(config, "repository")
import pytest
from keyring.backend import KeyringBackend
from poetry.utils.password_manager import KeyRing
from poetry.utils.password_manager import KeyRingError
from poetry.utils.password_manager import PasswordManager
class DummyBackend(KeyringBackend):
def __init__(self):
self._passwords = {}
@classmethod
def priority(cls):
return 42
def set_password(self, service, username, password):
self._passwords[service] = {username: password}
def get_password(self, service, username):
return self._passwords.get(service, {}).get(username)
def delete_password(self, service, username):
if service in self._passwords and username in self._passwords[service]:
del self._passwords[service][username]
@pytest.fixture()
def backend():
return DummyBackend()
@pytest.fixture()
def mock_available_backend(backend):
import keyring
keyring.set_keyring(backend)
@pytest.fixture()
def mock_unavailable_backend():
import keyring
from keyring.backends.fail import Keyring
keyring.set_keyring(Keyring())
@pytest.fixture()
def mock_chainer_backend(mocker):
from keyring.backends.fail import Keyring
mocker.patch("keyring.backend.get_all_keyring", [Keyring()])
import keyring
from keyring.backends.chainer import ChainerBackend
keyring.set_keyring(ChainerBackend())
def test_set_http_password(config, mock_available_backend, backend):
manager = PasswordManager(config)
assert manager.keyring.is_available()
manager.set_http_password("foo", "bar", "baz")
assert "baz" == backend.get_password("poetry-repository-foo", "bar")
auth = config.get("http-basic.foo")
assert "bar" == auth["username"]
assert "password" not in auth
def test_get_http_auth(config, mock_available_backend, backend):
backend.set_password("poetry-repository-foo", "bar", "baz")
config.auth_config_source.add_property("http-basic.foo", {"username": "bar"})
manager = PasswordManager(config)
assert manager.keyring.is_available()
auth = manager.get_http_auth("foo")
assert "bar" == auth["username"]
assert "baz" == auth["password"]
def test_delete_http_password(config, mock_available_backend, backend):
backend.set_password("poetry-repository-foo", "bar", "baz")
config.auth_config_source.add_property("http-basic.foo", {"username": "bar"})
manager = PasswordManager(config)
assert manager.keyring.is_available()
manager.delete_http_password("foo")
assert backend.get_password("poetry-repository-foo", "bar") is None
assert config.get("http-basic.foo") is None
def test_set_pypi_token(config, mock_available_backend, backend):
manager = PasswordManager(config)
assert manager.keyring.is_available()
manager.set_pypi_token("foo", "baz")
assert config.get("pypi-token.foo") is None
assert "baz" == backend.get_password("poetry-repository-foo", "__token__")
def test_get_pypi_token(config, mock_available_backend, backend):
backend.set_password("poetry-repository-foo", "__token__", "baz")
manager = PasswordManager(config)
assert manager.keyring.is_available()
assert "baz" == manager.get_pypi_token("foo")
def test_delete_pypi_token(config, mock_available_backend, backend):
backend.set_password("poetry-repository-foo", "__token__", "baz")
manager = PasswordManager(config)
assert manager.keyring.is_available()
manager.delete_pypi_token("foo")
assert backend.get_password("poetry-repository-foo", "__token__") is None
def test_set_http_password_with_unavailable_backend(config, mock_unavailable_backend):
manager = PasswordManager(config)
assert not manager.keyring.is_available()
manager.set_http_password("foo", "bar", "baz")
auth = config.get("http-basic.foo")
assert "bar" == auth["username"]
assert "baz" == auth["password"]
def test_get_http_auth_with_unavailable_backend(config, mock_unavailable_backend):
config.auth_config_source.add_property(
"http-basic.foo", {"username": "bar", "password": "baz"}
)
manager = PasswordManager(config)
assert not manager.keyring.is_available()
auth = manager.get_http_auth("foo")
assert "bar" == auth["username"]
assert "baz" == auth["password"]
def test_delete_http_password_with_unavailable_backend(
config, mock_unavailable_backend
):
config.auth_config_source.add_property(
"http-basic.foo", {"username": "bar", "password": "baz"}
)
manager = PasswordManager(config)
assert not manager.keyring.is_available()
manager.delete_http_password("foo")
assert config.get("http-basic.foo") is None
def test_set_pypi_token_with_unavailable_backend(config, mock_unavailable_backend):
manager = PasswordManager(config)
assert not manager.keyring.is_available()
manager.set_pypi_token("foo", "baz")
assert "baz" == config.get("pypi-token.foo")
def test_get_pypi_token_with_unavailable_backend(config, mock_unavailable_backend):
config.auth_config_source.add_property("pypi-token.foo", "baz")
manager = PasswordManager(config)
assert not manager.keyring.is_available()
assert "baz" == manager.get_pypi_token("foo")
def test_delete_pypi_token_with_unavailable_backend(config, mock_unavailable_backend):
config.auth_config_source.add_property("pypi-token.foo", "baz")
manager = PasswordManager(config)
assert not manager.keyring.is_available()
manager.delete_pypi_token("foo")
assert config.get("pypi-token.foo") is None
def test_keyring_raises_errors_on_keyring_errors(mocker, mock_unavailable_backend):
mocker.patch("poetry.utils.password_manager.KeyRing._check")
key_ring = KeyRing("poetry")
with pytest.raises(KeyRingError):
key_ring.set_password("foo", "bar", "baz")
with pytest.raises(KeyRingError):
key_ring.get_password("foo", "bar")
with pytest.raises(KeyRingError):
key_ring.delete_password("foo", "bar")
def test_keyring_with_chainer_backend_and_not_compatible_only_should_be_unavailable(
mock_chainer_backend,
):
key_ring = KeyRing("poetry")
assert not key_ring.is_available()
......@@ -166,3 +166,14 @@ def test_setup_reader_read_extras_require_with_variables(setup):
assert expected_install_requires == result["install_requires"]
assert expected_extras_require == result["extras_require"]
assert expected_python_requires == result["python_requires"]
@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4")
def test_setup_reader_setuptools(setup):
result = SetupReader.read_from_directory(setup("setuptools_setup"))
expected_name = "my_package"
expected_version = "0.1.2"
assert expected_name == result["name"]
assert expected_version == result["version"]
import pytest
from poetry.vcs.git import Git
from poetry.vcs.git import GitUrl
@pytest.mark.parametrize(
"url, normalized",
[
(
"git+ssh://user@hostname:project.git#commit",
GitUrl("user@hostname:project.git", "commit"),
),
(
"git+http://user@hostname/project/blah.git@commit",
GitUrl("http://user@hostname/project/blah.git", "commit"),
),
(
"git+https://user@hostname/project/blah.git",
GitUrl("https://user@hostname/project/blah.git", None),
),
(
"git+https://user@hostname:project/blah.git",
GitUrl("https://user@hostname/project/blah.git", None),
),
(
"git+ssh://git@github.com:sdispater/poetry.git#v1.0.27",
GitUrl("git@github.com:sdispater/poetry.git", "v1.0.27"),
),
(
"git+ssh://git@github.com:/sdispater/poetry.git",
GitUrl("git@github.com:/sdispater/poetry.git", None),
),
("git+ssh://git@github.com:org/repo", GitUrl("git@github.com:org/repo", None),),
(
"git+ssh://git@github.com/org/repo",
GitUrl("ssh://git@github.com/org/repo", None),
),
("git+ssh://foo:22/some/path", GitUrl("ssh://foo:22/some/path", None)),
("git@github.com:org/repo", GitUrl("git@github.com:org/repo", None)),
(
"git+https://github.com/sdispater/pendulum",
GitUrl("https://github.com/sdispater/pendulum", None),
),
(
"git+https://github.com/sdispater/pendulum#7a018f2d075b03a73409e8356f9b29c9ad4ea2c5",
GitUrl(
"https://github.com/sdispater/pendulum",
"7a018f2d075b03a73409e8356f9b29c9ad4ea2c5",
),
),
(
"git+ssh://git@git.example.com:b/b.git#v1.0.0",
GitUrl("git@git.example.com:b/b.git", "v1.0.0"),
),
(
"git+ssh://git@github.com:sdispater/pendulum.git#foo/bar",
GitUrl("git@github.com:sdispater/pendulum.git", "foo/bar"),
),
("git+file:///foo/bar.git", GitUrl("file:///foo/bar.git", None)),
(
"git+file://C:\\Users\\hello\\testing.git#zkat/windows-files",
GitUrl("file://C:\\Users\\hello\\testing.git", "zkat/windows-files"),
),
(
"git+https://git.example.com/sdispater/project/my_repo.git",
GitUrl("https://git.example.com/sdispater/project/my_repo.git", None),
),
(
"git+ssh://git@git.example.com:sdispater/project/my_repo.git",
GitUrl("git@git.example.com:sdispater/project/my_repo.git", None),
),
],
)
def test_normalize_url(url, normalized):
assert normalized == Git.normalize_url(url)
......@@ -310,6 +310,24 @@ def test_marker_union_intersect_marker_union():
)
def test_marker_union_intersect_marker_union_drops_unnecessary_markers():
m = parse_marker(
'python_version >= "2.7" and python_version < "2.8" '
'or python_version >= "3.4" and python_version < "4.0"'
)
m2 = parse_marker(
'python_version >= "2.7" and python_version < "2.8" '
'or python_version >= "3.4" and python_version < "4.0"'
)
intersection = m.intersect(m2)
expected = (
'python_version >= "2.7" and python_version < "2.8" '
'or python_version >= "3.4" and python_version < "4.0"'
)
assert expected == str(intersection)
def test_marker_union_intersect_multi_marker():
m = parse_marker('sys_platform == "darwin" or python_version < "3.4"')
......@@ -354,6 +372,46 @@ def test_marker_union_union_duplicates():
)
def test_marker_union_all_any():
union = MarkerUnion(parse_marker(""), parse_marker(""))
assert union.is_any()
def test_marker_union_not_all_any():
union = MarkerUnion(parse_marker(""), parse_marker(""), parse_marker("<empty>"))
assert union.is_any()
def test_marker_union_all_empty():
union = MarkerUnion(parse_marker("<empty>"), parse_marker("<empty>"))
assert union.is_empty()
def test_marker_union_not_all_empty():
union = MarkerUnion(
parse_marker("<empty>"), parse_marker("<empty>"), parse_marker("")
)
assert not union.is_empty()
def test_marker_str_conversion_skips_empty_and_any():
union = MarkerUnion(
parse_marker("<empty>"),
parse_marker(
'sys_platform == "darwin" or python_version <= "3.6" or os_name == "Windows"'
),
parse_marker(""),
)
assert str(union) == (
'sys_platform == "darwin" or python_version <= "3.6" or os_name == "Windows"'
)
def test_intersect_compacts_constraints():
m = parse_marker('python_version < "4.0"')
......@@ -439,3 +497,110 @@ def test_parse_version_like_markers(marker, env):
m = parse_marker(marker)
assert m.validate(env)
@pytest.mark.parametrize(
"marker, expected",
[
('python_version >= "3.6"', 'python_version >= "3.6"'),
('python_version >= "3.6" and extra == "foo"', 'python_version >= "3.6"'),
(
'python_version >= "3.6" and (extra == "foo" or extra == "bar")',
'python_version >= "3.6"',
),
(
'python_version >= "3.6" and (extra == "foo" or extra == "bar") or implementation_name == "pypy"',
'python_version >= "3.6" or implementation_name == "pypy"',
),
(
'python_version >= "3.6" and extra == "foo" or implementation_name == "pypy" and extra == "bar"',
'python_version >= "3.6" or implementation_name == "pypy"',
),
(
'python_version >= "3.6" or extra == "foo" and implementation_name == "pypy" or extra == "bar"',
'python_version >= "3.6" or implementation_name == "pypy"',
),
],
)
def test_without_extras(marker, expected):
m = parse_marker(marker)
assert expected == str(m.without_extras())
@pytest.mark.parametrize(
"marker, excluded, expected",
[
('python_version >= "3.6"', "implementation_name", 'python_version >= "3.6"'),
('python_version >= "3.6"', "python_version", "*"),
(
'python_version >= "3.6" and extra == "foo"',
"extra",
'python_version >= "3.6"',
),
(
'python_version >= "3.6" and (extra == "foo" or extra == "bar")',
"python_version",
'(extra == "foo" or extra == "bar")',
),
(
'python_version >= "3.6" and (extra == "foo" or extra == "bar") or implementation_name == "pypy"',
"python_version",
'(extra == "foo" or extra == "bar") or implementation_name == "pypy"',
),
(
'python_version >= "3.6" and extra == "foo" or implementation_name == "pypy" and extra == "bar"',
"implementation_name",
'python_version >= "3.6" and extra == "foo" or extra == "bar"',
),
(
'python_version >= "3.6" or extra == "foo" and implementation_name == "pypy" or extra == "bar"',
"implementation_name",
'python_version >= "3.6" or extra == "foo" or extra == "bar"',
),
],
)
def test_exclude(marker, excluded, expected):
m = parse_marker(marker)
if expected == "*":
assert m.exclude(excluded).is_any()
else:
assert expected == str(m.exclude(excluded))
@pytest.mark.parametrize(
"marker, only, expected",
[
('python_version >= "3.6"', "python_version", 'python_version >= "3.6"'),
(
'python_version >= "3.6" and extra == "foo"',
"python_version",
'python_version >= "3.6"',
),
(
'python_version >= "3.6" and (extra == "foo" or extra == "bar")',
"extra",
'(extra == "foo" or extra == "bar")',
),
(
'python_version >= "3.6" and (extra == "foo" or extra == "bar") or implementation_name == "pypy"',
"implementation_name",
'implementation_name == "pypy"',
),
(
'python_version >= "3.6" and extra == "foo" or implementation_name == "pypy" and extra == "bar"',
"implementation_name",
'implementation_name == "pypy"',
),
(
'python_version >= "3.6" or extra == "foo" and implementation_name == "pypy" or extra == "bar"',
"implementation_name",
'implementation_name == "pypy"',
),
],
)
def test_only(marker, only, expected):
m = parse_marker(marker)
assert expected == str(m.only(only))
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment