Commit 329b334b by Sébastien Eustace

Merge branch 'master' into develop

parents 55668d76 f597d1fc
......@@ -25,7 +25,7 @@ pip-log.txt
/test.py
/test_*.*
setup.cfg
/setup.cfg
MANIFEST.in
/setup.py
/docs/site/*
......
......@@ -11,6 +11,55 @@
- Fixed transitive extra dependencies being removed when updating a specific dependency.
## [0.12.8] - 2018-11-13
### Fixed
- Fixed permission errors when adding/removing git dependencies on Windows.
- Fixed `Pool` not raising an exception when no package could be found.
- Fixed reading `bz2` source distribution.
- Fixed handling of arbitrary equals in `InstalledRepository`.
## [0.12.7] - 2018-11-08
### Fixed
- Fixed reading of some `setup.py` files.
- Fixed a `KeyError` when getting information for packages which require reading setup files.
- Fixed the building of wheels with C extensions and an `src` layout.
- Fixed extras being selected when resolving dependencies even when not required.
- Fixed performance issues when packaging projects if a lot of files were excluded.
- Fixed installation of files.
- Fixed extras not being retrieved for legacy repositories.
- Fixed invalid transitive constraints raising an error for legacy repositories.
## [0.12.6] - 2018-11-05
### Changed
- Poetry will now try to read, without executing, setup files (`setup.py` and/or `setup.cfg`) if the `egg_info` command fails when resolving dependencies.
### Fixed
- Fixed installation of directory dependencies.
- Fixed handling of dependencies with a `not in` marker operator.
- Fixed support for VCS dependencies.
- Fixed the `exclude` property not being respected if no VCS was available.
## [0.12.5] - 2018-10-26
### Fixed
- Fixed installation of Poetry git dependencies with a build system.
- Fixed possible errors when resolving dependencies for specific packages.
- Fixed handling of Python versions compatibility.
- Fixed the dependency resolver picking up unnecessary dependencies due to not using the `python_full_version` marker.
- Fixed the `Python-Requires` metadata being invalid for single Python versions.
## [0.12.4] - 2018-10-21
### Fixed
......@@ -548,7 +597,11 @@ Initial release
[Unreleased]: https://github.com/sdispater/poetry/compare/0.12.4...develop
[Unreleased]: https://github.com/sdispater/poetry/compare/0.12.8...master
[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
......
......@@ -153,7 +153,7 @@ There are some things we can notice here:
`poetry` will also detect if you are inside a virtualenv and install the packages accordingly.
So, `poetry` can be installed globally and used everywhere.
`poetry` also comes with a full fledged dependency resolution library, inspired by [Molinillo](https://github.com/CocoaPods/Molinillo).
`poetry` also comes with a full fledged dependency resolution library.
## Why?
......
......@@ -35,18 +35,26 @@ The `^` operator works very well with libraries following [semantic versioning](
## Is tox supported?
For now, you can use Poetry with [tox](https://tox.readthedocs.io/en/latest/) by using something similar to what is done in the [Pendulum](https://github.com/sdispater/pendulum/blob/master/tox.ini) package.
Yes. By using the [isolated builds](https://tox.readthedocs.io/en/latest/config.html#conf-isolated_build) `tox` provides,
you can use it in combination with the PEP 517 compliant build system provided by Poetry.
Minimal viable `tox.ini` configuration file looks like this:
So, in your `pyproject.toml` file add this section if does not already exists:
```toml
[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"
```
And use a `tox.ini` configuration file similar to this:
```INI
[tox]
skipsdist = True
isolated_build = true
envlist = py27, py36
[testenv]
whitelist_externals = poetry
skip_install = true
commands =
poetry install -v
poetry run pytest tests/
......
......@@ -412,7 +412,7 @@ class Installer:
def customize_install(self):
if not self._accept_all:
print("Before we start, please answer the following questions.")
print("You may simple press the Enter key to keave unchanged.")
print("You may simply press the Enter key to leave unchanged.")
modify_path = input("Modify PATH variable? ([y]/n) ") or "y"
if modify_path.lower() in {"n", "no"}:
......@@ -434,17 +434,6 @@ class Installer:
return True
def customize_install(self):
if not self._accept_all:
print("Before we start, please answer the following questions.")
print("You may simple press the Enter key to keave unchanged.")
modify_path = input("Modify PATH variable? ([y]/n) ") or "y"
if modify_path.lower() in {"n", "no"}:
self._modify_path = False
print("")
def ensure_home(self):
"""
Ensures that $POETRY_HOME exists or create it.
......@@ -701,7 +690,34 @@ class Installer:
self.set_windows_path_var(path)
def remove_from_unix_path(self):
pass
# Updating any profile we can on UNIX systems
export_string = self.get_export_string()
addition = "{}\n".format(export_string)
profiles = self.get_unix_profiles()
for profile in profiles:
if not os.path.exists(profile):
continue
with open(profile, "r") as f:
content = f.readlines()
if addition not in content:
continue
new_content = []
for line in content:
if line == addition:
if new_content and not new_content[-1].strip():
new_content = new_content[:-1]
continue
new_content.append(line)
with open(profile, "w") as f:
f.writelines(new_content)
def get_export_string(self):
path = POETRY_BIN.replace(os.getenv("HOME", ""), "$HOME")
......
......@@ -137,11 +137,20 @@ version = "0.4.0"
[[package]]
category = "dev"
description = "Backports and enhancements for the contextlib module"
marker = "python_version < \"3\""
name = "contextlib2"
optional = false
python-versions = "*"
version = "0.5.5"
[[package]]
category = "dev"
description = "Code coverage measurement for Python"
name = "coverage"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, <4"
version = "4.5.1"
version = "4.5.2"
[[package]]
category = "main"
......@@ -158,7 +167,7 @@ description = "A platform independent file lock."
name = "filelock"
optional = false
python-versions = "*"
version = "3.0.9"
version = "3.0.10"
[[package]]
category = "dev"
......@@ -208,6 +217,41 @@ version = "2.7"
[[package]]
category = "dev"
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.6"
[package.dependencies]
[package.dependencies.contextlib2]
python = "<3"
version = "*"
[package.dependencies.pathlib2]
python = "<3"
version = "*"
[[package]]
category = "dev"
description = "Read resources from Python packages"
marker = "python_version < \"3.7\""
name = "importlib-resources"
optional = false
python-versions = ">=2.7,!=3.0,!=3.1,!=3.2,!=3.3"
version = "1.0.2"
[package.dependencies]
[package.dependencies.pathlib2]
python = "<3"
version = "*"
[package.dependencies.typing]
python = "<3.5"
version = "*"
[[package]]
category = "dev"
description = "A small but fast and easy to use stand-alone template engine written in pure python."
marker = "python_version >= \"2.7.9\" and python_version < \"2.8.0\" or python_version >= \"3.4\" and python_version < \"4.0\""
name = "jinja2"
......@@ -267,12 +311,12 @@ version = "3.0.1"
[[package]]
category = "dev"
description = "Implements a XML/HTML/XHTML Markup safe string for Python"
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"
optional = false
python-versions = "*"
version = "1.0"
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
version = "1.1.0"
[[package]]
category = "dev"
......@@ -333,7 +377,19 @@ description = "Node.js virtual environment builder"
name = "nodeenv"
optional = false
python-versions = "*"
version = "1.3.2"
version = "1.3.3"
[[package]]
category = "dev"
description = "Core utilities for Python packages"
name = "packaging"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "18.0"
[package.dependencies]
pyparsing = ">=2.0.2"
six = "*"
[[package]]
category = "main"
......@@ -366,7 +422,7 @@ marker = "python_version < \"3.0\""
name = "pbr"
optional = false
python-versions = "*"
version = "5.0.0"
version = "5.1.1"
[[package]]
category = "main"
......@@ -390,19 +446,24 @@ description = "A framework for managing and maintaining multi-language pre-commi
name = "pre-commit"
optional = false
python-versions = "*"
version = "1.11.2"
version = "1.12.0"
[package.dependencies]
"aspy.yaml" = "*"
cached-property = "*"
cfgv = ">=1.0.0"
identify = ">=1.0.0"
importlib-metadata = "*"
nodeenv = ">=0.11.1"
pyyaml = "*"
six = "*"
toml = "*"
virtualenv = "*"
[package.dependencies.importlib-resources]
python = "<3.7"
version = "*"
[[package]]
category = "dev"
description = "library with cross-python path, ini-parsing, io, code, log facilities"
......@@ -455,7 +516,7 @@ description = "Python parsing module"
name = "pyparsing"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
version = "2.2.2"
version = "2.3.0"
[[package]]
category = "main"
......@@ -474,7 +535,7 @@ description = "pytest: simple powerful testing with Python"
name = "pytest"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "3.9.1"
version = "3.10.1"
[package.dependencies]
atomicwrites = ">=1.0"
......@@ -523,13 +584,14 @@ version = "*"
[[package]]
category = "dev"
description = "py.test is a plugin for py.test that changes the default look and feel of py.test (e.g. progressbar, show tests that fail instantly)."
description = "pytest-sugar is a plugin for pytest that changes the default look and feel of pytest (e.g. progressbar, show tests that fail instantly)."
name = "pytest-sugar"
optional = false
python-versions = "*"
version = "0.9.1"
version = "0.9.2"
[package.dependencies]
packaging = ">=14.1"
pytest = ">=2.9"
termcolor = ">=1.1.0"
......@@ -547,7 +609,7 @@ description = "Python HTTP for Humans."
name = "requests"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "2.20.0"
version = "2.20.1"
[package.dependencies]
certifi = ">=2017.4.17"
......@@ -613,7 +675,7 @@ description = "Style preserving TOML library"
name = "tomlkit"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "0.4.6"
version = "0.5.2"
[package.dependencies]
[package.dependencies.enum34]
......@@ -643,7 +705,7 @@ description = "virtualenv-based automation of test activities"
name = "tox"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "3.5.2"
version = "3.5.3"
[package.dependencies]
filelock = ">=3.0.0,<4"
......@@ -657,7 +719,7 @@ virtualenv = ">=1.11.2"
[[package]]
category = "main"
description = "Type Hints for Python"
marker = "python_version >= \"2.7\" and python_version < \"2.8\" or python_version >= \"3.4\" and python_version < \"3.5\""
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\""
name = "typing"
optional = false
python-versions = "*"
......@@ -669,15 +731,15 @@ 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.24"
version = "1.24.1"
[[package]]
category = "main"
description = "Virtual Python Environment builder"
name = "virtualenv"
optional = false
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*"
version = "16.0.0"
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
version = "16.1.0"
[[package]]
category = "main"
......@@ -688,7 +750,7 @@ python-versions = "*"
version = "0.5.1"
[metadata]
content-hash = "7d4198bfede7f32d4992ec73c05701c841eea21306a8d3acfdde6a1db49a34f2"
content-hash = "cc0af5d7cff3183d13e844efc25bfabc912de0a22eea948614a0895ab4be5ed1"
python-versions = "~2.7 || ^3.4"
[metadata.hashes]
......@@ -706,54 +768,58 @@ chardet = ["84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", "
cleo = ["85a63076b72ca376fb06668be1fc7758dc16740b394783d5cc65200c4b32f71b", "9b7f79f1aa470a025c0d28c76aa225ee9e65028d32f80032e871aa3500df61b8"]
click = ["2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", "5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"]
colorama = ["a3d89af5db9e9806a779a50296b5fdb466e281147c2c235e8225ecc6dbf7bbf3", "c9b54bebe91a6a803e0772c8561d53f2926bfeb17cd141fbabcb08424086595c"]
coverage = ["03481e81d558d30d230bc12999e3edffe392d244349a90f4ef9b88425fac74ba", "0b136648de27201056c1869a6c0d4e23f464750fd9a9ba9750b8336a244429ed", "104ab3934abaf5be871a583541e8829d6c19ce7bde2923b2751e0d3ca44db60a", "10a46017fef60e16694a30627319f38a2b9b52e90182dddb6e37dcdab0f4bf95", "15b111b6a0f46ee1a485414a52a7ad1d703bdf984e9ed3c288a4414d3871dcbd", "198626739a79b09fa0a2f06e083ffd12eb55449b5f8bfdbeed1df4910b2ca640", "1c383d2ef13ade2acc636556fd544dba6e14fa30755f26812f54300e401f98f2", "23d341cdd4a0371820eb2b0bd6b88f5003a7438bbedb33688cd33b8eae59affd", "28b2191e7283f4f3568962e373b47ef7f0392993bb6660d079c62bd50fe9d162", "2a5b73210bad5279ddb558d9a2bfedc7f4bf6ad7f3c988641d83c40293deaec1", "2eb564bbf7816a9d68dd3369a510be3327f1c618d2357fa6b1216994c2e3d508", "337ded681dd2ef9ca04ef5d93cfc87e52e09db2594c296b4a0a3662cb1b41249", "3a2184c6d797a125dca8367878d3b9a178b6fdd05fdc2d35d758c3006a1cd694", "3c79a6f7b95751cdebcd9037e4d06f8d5a9b60e4ed0cd231342aa8ad7124882a", "3d72c20bd105022d29b14a7d628462ebdc61de2f303322c0212a054352f3b287", "3eb42bf89a6be7deb64116dd1cc4b08171734d721e7a7e57ad64cc4ef29ed2f1", "4635a184d0bbe537aa185a34193898eee409332a8ccb27eea36f262566585000", "56e448f051a201c5ebbaa86a5efd0ca90d327204d8b059ab25ad0f35fbfd79f1", "5a13ea7911ff5e1796b6d5e4fbbf6952381a611209b736d48e675c2756f3f74e", "69bf008a06b76619d3c3f3b1983f5145c75a305a0fea513aca094cae5c40a8f5", "6bc583dc18d5979dc0f6cec26a8603129de0304d5ae1f17e57a12834e7235062", "701cd6093d63e6b8ad7009d8a92425428bc4d6e7ab8d75efbb665c806c1d79ba", "7608a3dd5d73cb06c531b8925e0ef8d3de31fed2544a7de6c63960a1e73ea4bc", "76ecd006d1d8f739430ec50cc872889af1f9c1b6b8f48e29941814b09b0fd3cc", "7aa36d2b844a3e4a4b356708d79fd2c260281a7390d678a10b91ca595ddc9e99", "7d3f553904b0c5c016d1dad058a7554c7ac4c91a789fca496e7d8347ad040653", "7e1fe19bd6dce69d9fd159d8e4a80a8f52101380d5d3a4d374b6d3eae0e5de9c", "8c3cb8c35ec4d9506979b4cf90ee9918bc2e49f84189d9bf5c36c0c1119c6558", "9d6dd10d49e01571bf6e147d3b505141ffc093a06756c60b053a859cb2128b1f", "9e112fcbe0148a6fa4f0a02e8d58e94470fc6cb82a5481618fea901699bf34c4", "ac4fef68da01116a5c117eba4dd46f2e06847a497de5ed1d64bb99a5fda1ef91", "b8815995e050764c8610dbc82641807d196927c3dbed207f0a079833ffcf588d", "be6cfcd8053d13f5f5eeb284aa8a814220c3da1b0078fa859011c7fffd86dab9", "c1bb572fab8208c400adaf06a8133ac0712179a334c09224fb11393e920abcdd", "de4418dadaa1c01d497e539210cb6baa015965526ff5afc078c57ca69160108d", "e05cb4d9aad6233d67e0541caa7e511fa4047ed7750ec2510d466e806e0255d6", "e4d96c07229f58cb686120f168276e434660e4358cc9cf3b0464210b04913e77", "f3f501f345f24383c0000395b26b726e46758b71393267aeae0bd36f8b3ade80", "f8a923a85cb099422ad5a2e345fe877bbc89a8a8b23235824a93488150e45f6e"]
contextlib2 = ["509f9419ee91cdd00ba34443217d5ca51f5a364a404e1dce9e8979cea969ca48", "f5260a6e679d2ff42ec91ec5252f4eeffdcf21053db9113bd0a8e4d953769c00"]
coverage = ["06123b58a1410873e22134ca2d88bd36680479fe354955b3579fb8ff150e4d27", "09e47c529ff77bf042ecfe858fb55c3e3eb97aac2c87f0349ab5a7efd6b3939f", "0a1f9b0eb3aa15c990c328535655847b3420231af299386cfe5efc98f9c250fe", "0cc941b37b8c2ececfed341444a456912e740ecf515d560de58b9a76562d966d", "0d34245f824cc3140150ab7848d08b7e2ba67ada959d77619c986f2062e1f0e8", "10e8af18d1315de936d67775d3a814cc81d0747a1a0312d84e27ae5610e313b0", "1b4276550b86caa60606bd3572b52769860a81a70754a54acc8ba789ce74d607", "1e8a2627c48266c7b813975335cfdea58c706fe36f607c97d9392e61502dc79d", "258b21c5cafb0c3768861a6df3ab0cfb4d8b495eee5ec660e16f928bf7385390", "2b224052bfd801beb7478b03e8a66f3f25ea56ea488922e98903914ac9ac930b", "3ad59c84c502cd134b0088ca9038d100e8fb5081bbd5ccca4863f3804d81f61d", "447c450a093766744ab53bf1e7063ec82866f27bcb4f4c907da25ad293bba7e3", "46101fc20c6f6568561cdd15a54018bb42980954b79aa46da8ae6f008066a30e", "4710dc676bb4b779c4361b54eb308bc84d64a2fa3d78e5f7228921eccce5d815", "510986f9a280cd05189b42eee2b69fecdf5bf9651d4cd315ea21d24a964a3c36", "5535dda5739257effef56e49a1c51c71f1d37a6e5607bb25a5eee507c59580d1", "5a7524042014642b39b1fcae85fb37556c200e64ec90824ae9ecf7b667ccfc14", "5f55028169ef85e1fa8e4b8b1b91c0b3b0fa3297c4fb22990d46ff01d22c2d6c", "6694d5573e7790a0e8d3d177d7a416ca5f5c150742ee703f3c18df76260de794", "6831e1ac20ac52634da606b658b0b2712d26984999c9d93f0c6e59fe62ca741b", "71afc1f5cd72ab97330126b566bbf4e8661aab7449f08895d21a5d08c6b051ff", "7349c27128334f787ae63ab49d90bf6d47c7288c63a0a5dfaa319d4b4541dd2c", "77f0d9fa5e10d03aa4528436e33423bfa3718b86c646615f04616294c935f840", "828ad813c7cdc2e71dcf141912c685bfe4b548c0e6d9540db6418b807c345ddd", "859714036274a75e6e57c7bab0c47a4602d2a8cfaaa33bbdb68c8359b2ed4f5c", "85a06c61598b14b015d4df233d249cd5abfa61084ef5b9f64a48e997fd829a82", "869ef4a19f6e4c6987e18b315721b8b971f7048e6eaea29c066854242b4e98d9", "8cb4febad0f0b26c6f62e1628f2053954ad2c555d67660f28dfb1b0496711952", "977e2d9a646773cc7428cdd9a34b069d6ee254fadfb4d09b3f430e95472f3cf3", "99bd767c49c775b79fdcd2eabff405f1063d9d959039c0bdd720527a7738748a", "a5c58664b23b248b16b96253880b2868fb34358911400a7ba39d7f6399935389", "aaa0f296e503cda4bc07566f592cd7a28779d433f3a23c48082af425d6d5a78f", "ab235d9fe64833f12d1334d29b558aacedfbca2356dfb9691f2d0d38a8a7bfb4", "b3b0c8f660fae65eac74fbf003f3103769b90012ae7a460863010539bb7a80da", "bab8e6d510d2ea0f1d14f12642e3f35cefa47a9b2e4c7cea1852b52bc9c49647", "c45297bbdbc8bb79b02cf41417d63352b70bcb76f1bbb1ee7d47b3e89e42f95d", "d19bca47c8a01b92640c614a9147b081a1974f69168ecd494687c827109e8f42", "d64b4340a0c488a9e79b66ec9f9d77d02b99b772c8b8afd46c1294c1d39ca478", "da969da069a82bbb5300b59161d8d7c8d423bc4ccd3b410a9b4d8932aeefc14b", "ed02c7539705696ecb7dc9d476d861f3904a8d2b7e894bd418994920935d36bb", "ee5b8abc35b549012e03a7b1e86c09491457dba6c94112a2482b18589cc2bdb9"]
enum34 = ["2d81cbbe0e73112bdfe6ef8576f2238f2ba27dd0d55752a776c41d38b7da2850", "644837f692e5f550741432dd3f223bbb9852018674981b1664e5dc339387588a", "6bd0f6ad48ec2aa117d3d141940d484deccda84d4fcd884f5c3d93c23ecd8c79", "8ad8c4783bf61ded74527bffb48ed9b54166685e4230386a9ed9b1279e2df5b1"]
filelock = ["86fe6af56ae08ebc9c66d54ba3398c35b98916d0862d782b276a65816ff39392", "97694f181bdf58f213cca0a7cb556dc7bf90e2f8eb9aa3151260adac56701afb"]
filelock = ["b8d5ca5ca1c815e1574aee746650ea7301de63d87935b3463d26368b76e31633", "d610c1bb404daf85976d7a82eb2ada120f04671007266b708606565dd03b5be6"]
funcsigs = ["330cc27ccbf7f1e992e69fef78261dc7c6569012cf397db8d3de0234e6c937ca", "a7bb0f2cf3a3fd1ab2732cb49eba4252c2af4240442415b4abce3b87022a8f50"]
functools32 = ["89d824aa6c358c421a234d7f9ee0bd75933a67c29588ce50aaa3acdf4d403fa0", "f6253dfbe0538ad2e387bd8fdfd9293c925d63553f5813c4e587745416501e6d"]
html5lib = ["20b159aa3badc9d5ee8f5c647e5efd02ed2a66ab8d354930bd9ff139fc1dc0a3", "66cb0dcfdbbc4f9c3ba1a63fdb511ffdbd4f513b2b6d81b80cd26ce6b3fb3736"]
identify = ["5e956558a9a1e3b3891d7c6609fc9709657a11878af288ace484d1a46a93922b", "623086059219cc7b86c77a3891f3700cb175d4ce02b8fb8802b047301d71e783"]
idna = ["156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e", "684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16"]
importlib-metadata = ["36b02c84f9001adf65209fefdf951be8e9014a95eab9938c0779ad5670359b1c", "60b6481a72908c93ccb707abeb926fb5a15319b9e6f0b76639a718837ee12de0"]
importlib-resources = ["6e2783b2538bd5a14678284a3962b0660c715e5a0f10243fd5e00a4b5974f50b", "d3279fd0f6f847cced9f7acc19bd3e5df54d34f93a2e7bb5f238f81545787078"]
jinja2 = ["74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd", "f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"]
jsonschema = ["3ae8afd6f4ca6417f14bf43ef61341311598f14234cdb4174fe43d42b236a3c8", "dfd8426040892c8d0ef6da574085f282569f189cb24b70091a66c21c12d6705e"]
livereload = ["583179dc8d49b040a9da79bd33de59e160d2a8802b939e304eb359a4419f6498", "dd4469a8f5a6833576e9f5433f1439c306de15dbbfeceabd32479b1123380fa5"]
lockfile = ["6aed02de03cba24efabcd600b30540140634fc06cfa603822d508d5361e9f799", "6c3cb24f344923d30b2785d5ad75182c8ea7ac1b6171b08657258ec7429d50fa"]
markdown = ["c00429bd503a47ec88d5e30a751e147dcb4c6889663cd3e2ba0afe858e009baa", "d02e0f9b04c500cde6637c11ad7c72671f359b87b9fe924b2383649d8841db7c"]
markupsafe = ["a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665"]
markupsafe = ["048ef924c1623740e70204aa7143ec592504045ae4429b59c30054cb31e3c432", "130f844e7f5bdd8e9f3f42e7102ef1d49b2e6fdf0d7526df3f87281a532d8c8b", "19f637c2ac5ae9da8bfd98cef74d64b7e1bb8a63038a3505cd182c3fac5eb4d9", "1b8a7a87ad1b92bd887568ce54b23565f3fd7018c4180136e1cf412b405a47af", "1c25694ca680b6919de53a4bb3bdd0602beafc63ff001fea2f2fc16ec3a11834", "1f19ef5d3908110e1e891deefb5586aae1b49a7440db952454b4e281b41620cd", "1fa6058938190ebe8290e5cae6c351e14e7bb44505c4a7624555ce57fbbeba0d", "31cbb1359e8c25f9f48e156e59e2eaad51cd5242c05ed18a8de6dbe85184e4b7", "3e835d8841ae7863f64e40e19477f7eb398674da6a47f09871673742531e6f4b", "4e97332c9ce444b0c2c38dd22ddc61c743eb208d916e4265a2a3b575bdccb1d3", "525396ee324ee2da82919f2ee9c9e73b012f23e7640131dd1b53a90206a0f09c", "52b07fbc32032c21ad4ab060fec137b76eb804c4b9a1c7c7dc562549306afad2", "52ccb45e77a1085ec5461cde794e1aa037df79f473cbc69b974e73940655c8d7", "5c3fbebd7de20ce93103cb3183b47671f2885307df4a17a0ad56a1dd51273d36", "5e5851969aea17660e55f6a3be00037a25b96a9b44d2083651812c99d53b14d1", "5edfa27b2d3eefa2210fb2f5d539fbed81722b49f083b2c6566455eb7422fd7e", "7d263e5770efddf465a9e31b78362d84d015cc894ca2c131901a4445eaa61ee1", "83381342bfc22b3c8c06f2dd93a505413888694302de25add756254beee8449c", "857eebb2c1dc60e4219ec8e98dfa19553dae33608237e107db9c6078b1167856", "98e439297f78fca3a6169fd330fbe88d78b3bb72f967ad9961bcac0d7fdd1550", "bf54103892a83c64db58125b3f2a43df6d2cb2d28889f14c78519394feb41492", "d9ac82be533394d341b41d78aca7ed0e0f4ba5a2231602e2f05aa87f25c51672", "e982fe07ede9fada6ff6705af70514a52beb1b2c3d25d4e873e82114cf3c5401", "edce2ea7f3dfc981c4ddc97add8a61381d9642dc3273737e756517cc03e84dd6", "efdc45ef1afc238db84cb4963aa689c0408912a0239b0721cb172b4016eb31d6", "f137c02498f8b935892d5c0172560d7ab54bc45039de8805075e19079c639a9c", "f82e347a72f955b7017a39708a3667f106e6ad4d10b25f237396a7115d8ed5fd", "fb7c206e01ad85ce57feeaaa0bf784b97fa3cad0d4a5737bc5295785f5c613a1"]
mkdocs = ["17d34329aad75d5de604b9ed4e31df3a4d235afefdc46ce7b1964fddb2e1e939", "8cc8b38325456b9e942c981a209eaeb1e9f3f77b493ad755bfef889b9c8d356a"]
mock = ["5ce3c71c5545b472da17b72268978914d0252980348636840bd34a00b5cc96c1", "b158b6df76edd239b8208d481dc46b6afd45a846b7812ff0ce58971cf5bc8bba"]
more-itertools = ["c187a73da93e7a8acc0001572aebc7e3c69daf7bf6881a2cea10650bd4420092", "c476b5d3a34e12d40130bc2f935028b5f636df8f372dc2c1c01dc19681b2039e", "fcbfeaea0be121980e15bc97b3817b5202ca73d0eae185b4550cbfce2a3ebb3d"]
msgpack = ["0b3b1773d2693c70598585a34ca2715873ba899565f0a7c9a1545baef7e7fbdc", "0bae5d1538c5c6a75642f75a1781f3ac2275d744a92af1a453c150da3446138b", "0ee8c8c85aa651be3aa0cd005b5931769eaa658c948ce79428766f1bd46ae2c3", "1369f9edba9500c7a6489b70fdfac773e925342f4531f1e3d4c20ac3173b1ae0", "22d9c929d1d539f37da3d1b0e16270fa9d46107beab8c0d4d2bddffffe895cee", "2ff43e3247a1e11d544017bb26f580a68306cec7a6257d8818893c1fda665f42", "31a98047355d34d047fcdb55b09cb19f633cf214c705a765bd745456c142130c", "8767eb0032732c3a0da92cbec5ac186ef89a3258c6edca09161472ca0206c45f", "8acc8910218555044e23826980b950e96685dc48124a290c86f6f41a296ea172", "ab189a6365be1860a5ecf8159c248f12d33f79ea799ae9695fa6a29896dcf1d4", "cfd6535feb0f1cf1c7cdb25773e965cc9f92928244a8c3ef6f8f8a8e1f7ae5c4", "e274cd4480d8c76ec467a85a9c6635bbf2258f0649040560382ab58cabb44bcf", "f86642d60dca13e93260187d56c2bef2487aa4d574a669e8ceefcf9f4c26fd00", "f8a57cbda46a94ed0db55b73e6ab0c15e78b4ede8690fa491a0e55128d552bb0", "fcea97a352416afcbccd7af9625159d80704a25c519c251c734527329bb20d0e"]
nodeenv = ["aa040ab5189bae17d272175609010be6c5b589ec4b8dbd832cc50c9e9cb7496f"]
nodeenv = ["ad8259494cf1c9034539f6cced78a1da4840a4b157e23640bc4a0c0546b0cb7a"]
packaging = ["0886227f54515e592aaa2e5a553332c73962917f2831f1b0f9b9f4380a4b9807", "f95a1e147590f204328170981833854229bb2912ac3d5f89e2a8ccd2834800c9"]
pastel = ["3108af417ec0fa6d0a620e676ec4f02c839ca13e10611586e5d2174b46aa0bc3", "d1fee8079534f99f1805a044fef946d23eee6d6a7cd34292c30e6c16be9a80b9"]
pathlib2 = ["8eb170f8d0d61825e09a95b38be068299ddeda82f35e96c3301a8a5e7604cb83", "d1aa2a11ba7b8f7b21ab852b1fb5afb277e1bb99d5dfc663380b5015c0d80c5a"]
pbr = ["ab94783019179bf48f5784edc63f5bc8328ec5ff93f33591567f266d21ac7323", "bfcff1a3878eebf559392c2130a17f612a03f96a0d44c3559d9c1e62a4235a2d"]
pbr = ["f59d71442f9ece3dffc17bc36575768e1ee9967756e6b6535f0ee1f0054c3d68", "f6d5b23f226a2ba58e14e49aa3b1bfaf814d0199144b95d78458212444de1387"]
pkginfo = ["5878d542a4b3f237e359926384f1dde4e099c9f5525d236b1840cf704fa8d474", "a39076cb3eb34c333a0dd390b568e9e1e881c7bf2cc0aee12120636816f55aee"]
pluggy = ["447ba94990e8014ee25ec853339faf7b0fc8050cdc3289d4d71f7f410fb90095", "bde19360a8ec4dfd8a20dcb811780a30998101f078fc7ded6162f0076f50508f"]
pre-commit = ["98ec318db1f255e3d5089ae416959ef3c775215a3509620027d446eeabbba740", "b68f9219c56b50ebafb57f378c5325f9876d06fb29e9cd05daf5cbd6676ce8f2"]
pre-commit = ["7542bd8ae1c58745175ea0a9295964ee82a10f7e18c4344f5e4c02bd85d02561", "87f687da6a2651d5067cfec95b854b004e95b70143cbf2369604bb3acbce25ec"]
py = ["bf92637198836372b520efcba9e020c330123be8ce527e535d185ed4b6f45694", "e76826342cefe3c3d5f7e8ee4316b80d1dd8a300781612ddbc765c17ba25a6c6"]
pygments = ["78f3f434bcc5d6ee09020f92ba487f95ba50f1e3ef83ae96b9d5ffa1bab25c5d", "dbae1046def0efb574852fab9e90209b23f556367b5a320c0bcb871c77c3e8cc"]
pygments-github-lexers = ["0f9e9fb607d351c127a1e55e82a6eb491ed1fc11b2d6a0444ba217dc6d1f82c1", "aaca57e77cd6fcfce8d6ee97a998962eebf7fbb810519a8ebde427c62823e133"]
pylev = ["063910098161199b81e453025653ec53556c1be7165a9b7c50be2f4d57eae1c3", "1d29a87beb45ebe1e821e7a3b10da2b6b2f4c79b43f482c2df1a1f748a6e114e"]
pymdown-extensions = ["25b0a7967fa697b5035e23340a48594e3e93acb10b06d74574218ace3347d1df", "6cf0cf36b5a03b291ace22dc2f320f4789ce56fbdb6635a3be5fadbf5d7694dd"]
pyparsing = ["bc6c7146b91af3f567cf6daeaec360bc07d45ffec4cf5353f4d7a208ce7ca30a", "d29593d8ebe7b57d6967b62494f8c72b03ac0262b1eed63826c6f788b3606401"]
pyparsing = ["40856e74d4987de5d01761a22d1621ae1c7f8774585acae358aa5c5936c6c90b", "f353aab21fd474459d97b709e527b5571314ee5f067441dc9f88e33eecd96592"]
pyrsistent = ["f64dd1b706c31f7aa24495a7da58c0407c072981289b675331e2a16364355102"]
pytest = ["10e59f84267370ab20cec9305bafe7505ba4d6b93ecbf66a1cce86193ed511d5", "8c827e7d4816dfe13e9329c8226aef8e6e75d65b939bc74fda894143b6d1df59"]
pytest = ["3f193df1cfe1d1609d4c583838bea3d532b18d6160fd3f55c9447fdca30848ec", "e246cf173c01169b9617fc07264b7b1316e78d7a650055235d6d897bc80d9660"]
pytest-cov = ["513c425e931a0344944f84ea47f3956be0e416d95acbd897a44970c8d926d5d7", "e360f048b7dae3f2f2a9a4d067b2dd6b6a015d384d1577c994a43f3f7cbad762"]
pytest-mock = ["53801e621223d34724926a5c98bd90e8e417ce35264365d39d6c896388dcc928", "d89a8209d722b8307b5e351496830d5cc5e192336003a485443ae9adeb7dd4c0"]
pytest-sugar = ["ab8cc42faf121344a4e9b13f39a51257f26f410e416c52ea11078cdd00d98a2c"]
pytest-sugar = ["26cf8289fe10880cbbc130bd77398c4e6a8b936d8393b116a5c16121d95ab283", "fcd87a74b2bce5386d244b49ad60549bfbc4602527797fac167da147983f58ab"]
pyyaml = ["3d7da3009c0f3e783b2c873687652d83b1bbfd5c88e9813fb7e5b03c0dd3108b", "3ef3092145e9b70e3ddd2c7ad59bdd0252a94dfe3949721633e41344de00a6bf", "40c71b8e076d0550b2e6380bada1f1cd1017b882f7e16f09a65be98e017f211a", "558dd60b890ba8fd982e05941927a3911dc409a63dcb8b634feaa0cda69330d3", "a7c28b45d9f99102fa092bb213aa12e0aaf9a6a1f5e395d36166639c1f96c3a1", "aa7dd4a6a427aed7df6fb7f08a580d68d9b118d90310374716ae90b710280af1", "bc558586e6045763782014934bfaf39d48b8ae85a2713117d16c39864085c613", "d46d7982b62e0729ad0175a9bc7e10a566fc07b224d2c79fafb5e032727eaa04", "d5eef459e30b09f5a098b9cea68bebfeb268697f78d647bd255a085371ac7f3f", "e01d3203230e1786cd91ccfdc8f8454c8069c91bee3962ad93b87a4b2860f537", "e170a9e6fcfd19021dd29845af83bb79236068bf5fd4df3327c1be18182b2531"]
requests = ["99dcfdaaeb17caf6e526f32b6a7b780461512ab3f1d992187801694cba42770c", "a84b8c9ab6239b578f22d1c21d51b696dcfe004032bb80ea832398d6909d7279"]
requests = ["65b3a120e4329e33c9889db89c80976c5272f56ea92d3e74da8a463992e3ff54", "ea881206e59f41dbd0bd445437d792e43906703fff75ca8ff43ccdb11f33f263"]
requests-toolbelt = ["42c9c170abc2cacb78b8ab23ac957945c7716249206f90874651971a4acff237", "f6a531936c6fa4c6cfce1b9c10d5c4f498d16528d2a54a22ca00011205a187b5"]
scandir = ["04b8adb105f2ed313a7c2ef0f1cf7aff4871aa7a1883fa4d8c44b5551ab052d6", "1444134990356c81d12f30e4b311379acfbbcd03e0bab591de2696a3b126d58e", "1b5c314e39f596875e5a95dd81af03730b338c277c54a454226978d5ba95dbb6", "346619f72eb0ddc4cf355ceffd225fa52506c92a2ff05318cfabd02a144e7c4e", "44975e209c4827fc18a3486f257154d34ec6eaec0f90fef0cca1caa482db7064", "61859fd7e40b8c71e609c202db5b0c1dbec0d5c7f1449dec2245575bdc866792", "a5e232a0bf188362fa00123cc0bb842d363a292de7126126df5527b6a369586a", "c14701409f311e7a9b7ec8e337f0815baf7ac95776cc78b419a1e6d49889a383", "c7708f29d843fc2764310732e41f0ce27feadde453261859ec0fca7865dfc41b", "c9009c527929f6e25604aec39b0a43c3f831d2947d89d6caaab22f057b7055c8", "f5c71e29b4e2af7ccdc03a020c626ede51da471173b4a6ad1e904f2b2e04b4bd"]
shellingham = ["c9fd71508d4363e8a3dadf405e681021461dca9ca9a2b48c9461fdfbfceaebff", "f56b5547ed84296318c21162ce345d83dd5e4755a0e4f57daee1948479f47119"]
six = ["70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", "832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"]
termcolor = ["1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b"]
toml = ["229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", "235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e", "f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3"]
tomlkit = ["27ddd2796855428a0316057884ec081a1c967c8d29c3d489fcfccd1bb2976ede", "8f857398aefa2c6a488c824f1e7f757e73a4f68246f1874f9df5eb53903231de"]
tomlkit = ["82a8fbb8d8c6af72e96ba00b9db3e20ef61be6c79082552c9363f4559702258b", "a43e0195edc9b3c198cd4b5f0f3d427a395d47c4a76ceba7cc875ed030756c39"]
tornado = ["0662d28b1ca9f67108c7e3b77afabfb9c7e87bde174fbda78186ecedc2499a9d", "4e5158d97583502a7e2739951553cbd88a72076f152b4b11b64b9a10c4c49409", "732e836008c708de2e89a31cb2fa6c0e5a70cb60492bee6f1ea1047500feaf7f", "8154ec22c450df4e06b35f131adc4f2f3a12ec85981a203301d310abf580500f", "8e9d728c4579682e837c92fdd98036bd5cdefa1da2aaf6acf26947e6dd0c01c5", "d4b3e5329f572f055b587efc57d29bd051589fb5a43ec8898c77a47ec2fa2bbb", "e5f2585afccbff22390cddac29849df463b252b711aa2ce7c5f3f342a5b3b444"]
tox = ["217fb84aecf9792a98f93f07cfcaf014205a76c64e52bd7c2b4135458e6ad2a1", "4baeb3d8ebdcd9f43afce38aa67d06f1165a87d221d5bb21e8b39a0d4880c134"]
tox = ["513e32fdf2f9e2d583c2f248f47ba9886428c949f068ac54a0469cac55df5862", "75fa30e8329b41b664585f5fb837e23ce1d7e6fa1f7811f2be571c990f9d911b"]
typing = ["4027c5f6127a6267a435201981ba156de91ad0d1d98e9ddc2aa173453453492d", "57dcf675a99b74d64dacf6fba08fb17cf7e3d5fdff53d4a30ea2a5e7e52543d4", "a4c8473ce11a65999c8f59cb093e70686b6c84c98df58c1dae9b3b196089858a"]
urllib3 = ["41c3db2fc01e5b907288010dec72f9d0a74e37d6994e6eb56849f59fea2265ae", "8819bba37a02d143296a4d032373c4dd4aca11f6d4c9973335ca75f9c8475f59"]
virtualenv = ["2ce32cd126117ce2c539f0134eb89de91a8413a29baac49cbab3eb50e2026669", "ca07b4c0b54e14a91af9f34d0919790b016923d157afda5efdde55c96718f752"]
urllib3 = ["61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39", "de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22"]
virtualenv = ["686176c23a538ecc56d27ed9d5217abd34644823d6391cbeb232f42bf722baad", "f899fafcd92e1150f40c8215328be38ff24b519cd95357fa6e78e006c7638208"]
webencodings = ["a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", "b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"]
__version__ = "0.12.4"
__version__ = "0.12.8"
......@@ -31,19 +31,22 @@ class DebugInfoCommand(Command):
env_python_version = ".".join(str(s) for s in env.version_info[:3])
self.output.title("Virtualenv")
self.output.listing(
[
"<info>Python</info>: <comment>{}</>".format(
env_python_version
),
"<info>Implementation</info>: <comment>{}</>".format(
env.python_implementation
),
"<info>Path</info>: <comment>{}</>".format(
env.path if env.is_venv() else "NA"
),
]
)
listing = [
"<info>Python</info>: <comment>{}</>".format(env_python_version),
"<info>Implementation</info>: <comment>{}</>".format(
env.python_implementation
),
"<info>Path</info>: <comment>{}</>".format(
env.path if env.is_venv() else "NA"
),
]
if env.is_venv():
listing.append(
"<info>Valid</info>: <{tag}>{is_valid}</{tag}>".format(
tag="comment" if env.is_sane() else "error", is_valid=env.is_sane()
)
)
self.output.listing(listing)
self.line("")
......
......@@ -8,10 +8,27 @@ class EnvCommand(Command):
super(EnvCommand, self).__init__()
def initialize(self, i, o):
from poetry.semver import parse_constraint
from poetry.utils.env import Env
super(EnvCommand, self).initialize(i, o)
# Checking compatibility of the current environment with
# the python dependency specified in pyproject.toml
current_env = Env.get()
supported_python = self.poetry.package.python_constraint
current_python = parse_constraint(
".".join(str(v) for v in current_env.version_info[:3])
)
if not supported_python.allows(current_python):
raise RuntimeError(
"The current Python version ({}) is not supported by the project ({})\n"
"Please activate a compatible Python version.".format(
current_python, self.poetry.package.python_versions
)
)
self._env = Env.create_venv(
o, self.poetry.package.name, cwd=self.poetry.file.parent
)
......
......@@ -37,6 +37,7 @@ The <info>init</info> command creates a basic <comment>pyproject.toml</> file in
def handle(self):
from poetry.layouts import layout
from poetry.utils._compat import Path
from poetry.utils.env import Env
from poetry.vcs.git import GitConfig
if (Path.cwd() / "pyproject.toml").exists():
......@@ -101,7 +102,16 @@ The <info>init</info> command creates a basic <comment>pyproject.toml</> file in
question.validator = self._validate_license
license = self.ask(question)
question = self.create_question("Compatible Python versions [*]: ", default="*")
current_env = Env.get()
default_python = "^{}".format(
".".join(str(v) for v in current_env.version_info[:2])
)
question = self.create_question(
"Compatible Python versions [<comment>{}</comment>]: ".format(
default_python
),
default=default_python,
)
python = self.ask(question)
self.line("")
......
......@@ -14,6 +14,7 @@ class NewCommand(Command):
def handle(self):
from poetry.layouts import layout
from poetry.utils._compat import Path
from poetry.utils.env import Env
from poetry.vcs.git import GitConfig
if self.option("src"):
......@@ -44,7 +45,17 @@ class NewCommand(Command):
if author_email:
author += " <{}>".format(author_email)
layout_ = layout_(name, "0.1.0", author=author, readme_format=readme_format)
current_env = Env.get()
default_python = "^{}".format(
".".join(str(v) for v in current_env.version_info[:2])
)
layout_ = layout_(
name,
"0.1.0",
author=author,
readme_format=readme_format,
python=default_python,
)
layout_.create(path)
self.line(
......
......@@ -24,12 +24,8 @@ lists all packages available."""
colors = ["green", "yellow", "cyan", "magenta", "blue"]
def handle(self):
from poetry.packages.constraints import (
parse_constraint as parse_generic_constraint,
)
from poetry.repositories.installed_repository import InstalledRepository
from poetry.semver import Version
from poetry.semver import parse_constraint
package = self.argument("package")
......@@ -172,7 +168,7 @@ lists all packages available."""
color = "yellow"
line += " <fg={}>{:{}}</>".format(
color, latest.pretty_version, latest_length
color, latest.full_pretty_version, latest_length
)
if self.option("outdated") and update_status == "up-to-date":
continue
......@@ -291,11 +287,17 @@ lists all packages available."""
self.set_style(color, color)
def find_latest_package(self, package):
from poetry.io import NullIO
from poetry.puzzle.provider import Provider
from poetry.version.version_selector import VersionSelector
# find the latest version allowed in this pool
if package.source_type == "git":
return
for dep in self.poetry.package.requires:
if dep.name == package.name and dep.is_vcs():
return Provider(
self.poetry.package, self.poetry.pool, NullIO()
).search_for_vcs(dep)[0]
name = package.name
selector = VersionSelector(self.poetry.pool)
......
import os
import shutil
import tempfile
from subprocess import CalledProcessError
from poetry.config import Config
from poetry.utils.helpers import get_http_basic_auth
from poetry.utils.helpers import safe_rmtree
try:
......@@ -25,7 +27,7 @@ class PipInstaller(BaseInstaller):
def install(self, package, update=False):
if package.source_type == "directory":
self.install_directory(package, update=update)
self.install_directory(package)
return
......@@ -92,6 +94,12 @@ class PipInstaller(BaseInstaller):
self.install(target, update=True)
def remove(self, package):
# If we have a VCS package, remove its source directory
if package.source_type == "git":
src_dir = self._env.path / "src" / package.name
if src_dir.exists():
safe_rmtree(str(src_dir))
try:
self.run("uninstall", package.name, "-y")
except CalledProcessError as e:
......@@ -119,7 +127,7 @@ class PipInstaller(BaseInstaller):
else:
req = os.path.realpath(package.source_url)
if package.develop:
if package.develop and package.source_type == "directory":
req = ["-e", req]
return req
......@@ -143,7 +151,7 @@ class PipInstaller(BaseInstaller):
return name
def install_directory(self, package, update=False):
def install_directory(self, package):
from poetry.io import NullIO
from poetry.masonry.builder import SdistBuilder
from poetry.poetry import Poetry
......@@ -161,12 +169,16 @@ class PipInstaller(BaseInstaller):
pyproject = TomlFile(os.path.join(req, "pyproject.toml"))
has_poetry = False
has_build_system = False
if pyproject.exists():
pyproject_content = pyproject.read()
has_poetry = (
"tool" in pyproject_content and "poetry" in pyproject_content["tool"]
)
has_build_system = "build-system" in pyproject_content
# Even if there is a build system specified
# pip as of right now does not support it fully
# TODO: Check for pip version when proper PEP-517 support lands
# has_build_system = ("build-system" in pyproject_content)
setup = os.path.join(req, "setup.py")
has_setup = os.path.exists(setup)
......@@ -193,21 +205,22 @@ class PipInstaller(BaseInstaller):
def install_git(self, package):
from poetry.packages import Package
from poetry.utils._compat import Path
from poetry.utils.helpers import temporary_directory
from poetry.vcs import Git
with temporary_directory() as tmp_dir:
tmp_dir = Path(tmp_dir)
src_dir = self._env.path / "src" / package.name
if src_dir.exists():
safe_rmtree(str(src_dir))
src_dir.parent.mkdir(exist_ok=True)
git = Git()
git.clone(package.source_url, tmp_dir)
git.checkout(package.source_reference, tmp_dir)
git = Git()
git.clone(package.source_url, src_dir)
git.checkout(package.source_reference, src_dir)
# Now we just need to install from the temporary directory
pkg = Package(package.name, package.version)
pkg.source_type = "directory"
pkg.source_url = str(tmp_dir)
pkg.develop = False
# Now we just need to install from the source directory
pkg = Package(package.name, package.version)
pkg.source_type = "directory"
pkg.source_url = str(src_dir)
pkg.develop = True
self.install_directory(pkg)
self.install_directory(pkg)
# -*- coding: utf-8 -*-
import os
import re
import shutil
import tempfile
from collections import defaultdict
from contextlib import contextmanager
from typing import Set
from typing import Union
from poetry.utils._compat import Path
from poetry.utils._compat import basestring
from poetry.utils._compat import lru_cache
from poetry.vcs import get_vcs
from ..metadata import Metadata
......@@ -39,35 +42,40 @@ class Builder(object):
def build(self):
raise NotImplementedError()
def find_excluded_files(self): # type: () -> list
@lru_cache(maxsize=None)
def find_excluded_files(self): # type: () -> Set[str]
# Checking VCS
vcs = get_vcs(self._path)
if not vcs:
return []
vcs_ignored_files = set()
else:
vcs_ignored_files = set(vcs.get_ignored_files())
explicitely_excluded = []
explicitely_excluded = set()
for excluded_glob in self._package.exclude:
for excluded in self._path.glob(excluded_glob):
explicitely_excluded.append(excluded)
explicitely_excluded.add(excluded.relative_to(self._path).as_posix())
ignored = vcs.get_ignored_files() + explicitely_excluded
result = []
ignored = vcs_ignored_files | explicitely_excluded
result = set()
for file in ignored:
try:
file = Path(file).absolute().relative_to(self._path)
except ValueError:
# Should only happen in tests
continue
result.append(file)
result.add(file)
# The list of excluded files might be big and we will do a lot
# containment check (x in excluded).
# Returning a set make those tests much much faster.
return result
def find_files_to_add(self, exclude_build=True): # type: () -> list
def is_excluded(self, filepath): # type: (Union[str, Path]) -> bool
if not isinstance(filepath, basestring):
filepath = filepath.as_posix()
return filepath in self.find_excluded_files()
def find_files_to_add(self, exclude_build=True): # type: (bool) -> list
"""
Finds all files to add to the tarball
"""
excluded = self.find_excluded_files()
to_add = []
for include in self._module.includes:
......@@ -80,7 +88,7 @@ class Builder(object):
file = file.relative_to(self._path)
if file in excluded and isinstance(include, PackageInclude):
if self.is_excluded(file) and isinstance(include, PackageInclude):
continue
if file.suffix == ".pyc":
......
......@@ -278,7 +278,7 @@ class SdistBuilder(Builder):
if not f.is_dir()
]
data = [e for e in data_elements if e not in excluded_files]
data = [e for e in data_elements if not self.is_excluded(e)]
if not data:
continue
......
......@@ -11,10 +11,10 @@ import zipfile
from base64 import urlsafe_b64encode
from io import StringIO
from typing import Set
from poetry.__version__ import __version__
from poetry.semver import parse_constraint
from poetry.utils._compat import Path
from ..utils.helpers import normalize_file_permissions
from ..utils.package_include import PackageInclude
......@@ -67,8 +67,8 @@ class WheelBuilder(Builder):
with zipfile.ZipFile(
os.fdopen(fd, "w+b"), mode="w", compression=zipfile.ZIP_DEFLATED
) as zip_file:
self._build()
self._copy_module(zip_file)
self._build(zip_file)
self._write_metadata(zip_file)
self._write_record(zip_file)
......@@ -79,7 +79,7 @@ class WheelBuilder(Builder):
self._io.writeln(" - Built <fg=cyan>{}</>".format(self.wheel_filename))
def _build(self):
def _build(self, wheel):
if self._package.build:
setup = self._path / "setup.py"
......@@ -103,9 +103,22 @@ class WheelBuilder(Builder):
return
lib = lib[0]
for pkg in lib.glob("*"):
shutil.rmtree(str(self._path / pkg.name))
shutil.copytree(str(pkg), str(self._path / pkg.name))
excluded = self.find_excluded_files()
for pkg in lib.glob("**/*"):
if pkg.is_dir() or pkg in excluded:
continue
rel_path = str(pkg.relative_to(lib))
if rel_path in wheel.namelist():
continue
self._io.writeln(
" - Adding: <comment>{}</comment>".format(rel_path),
verbosity=self._io.VERBOSITY_VERY_VERBOSE,
)
self._add_file(wheel, pkg, rel_path)
def _copy_module(self, wheel):
excluded = self.find_excluded_files()
......@@ -173,9 +186,9 @@ class WheelBuilder(Builder):
# RECORD itself is recorded with no hash or size
f.write(self.dist_info + "/RECORD,,\n")
def find_excluded_files(self): # type: () -> list
def find_excluded_files(self): # type: () -> Set
# Checking VCS
return []
return set()
@property
def dist_info(self): # type: () -> str
......
......@@ -39,7 +39,7 @@ class _Writer:
if required_python_version is not None:
buffer.append(
"The current supported Python versions are {}".format(
"The current project must support the following Python versions: {}".format(
required_python_version
)
)
......
......@@ -105,20 +105,21 @@ def dependency_from_pep_508(name):
op = ""
elif op == "!=":
version += ".*"
elif op == "in":
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 = ""
op_ = "" if op == "in" else "!="
else:
op = "=="
op_ = "==" if op == "in" else "!="
versions.append(op + ".".join(split))
versions.append(op_ + ".".join(split))
glue = " || " if op == "in" else ", "
if versions:
ands.append(" || ".join(versions))
ands.append(glue.join(versions))
continue
......
import os
import pkginfo
from pkginfo.distribution import HEADER_ATTRS
from pkginfo.distribution import HEADER_ATTRS_2_0
from poetry.io import NullIO
from poetry.utils._compat import Path
from poetry.utils._compat import decode
from poetry.utils.helpers import parse_requires
from poetry.utils.toml_file import TomlFile
from poetry.utils.env import Env
from .dependency import Dependency
......@@ -22,15 +15,13 @@ HEADER_ATTRS.update(
class DirectoryDependency(Dependency):
def __init__(
self,
name,
path, # type: Path
category="main", # type: str
optional=False, # type: bool
base=None, # type: Path
develop=True, # type: bool
):
from . import dependency_from_pep_508
from .package import Package
self._path = path
self._base = base
self._full_path = path
......@@ -46,7 +37,7 @@ class DirectoryDependency(Dependency):
if self._full_path.is_file():
raise ValueError("{} is a file, expected a directory".format(self._path))
# Checking content to dertermine actions
# Checking content to determine actions
setup = self._full_path / "setup.py"
pyproject = TomlFile(self._full_path / "pyproject.toml")
if pyproject.exists():
......@@ -62,67 +53,8 @@ class DirectoryDependency(Dependency):
)
)
if self._supports_poetry:
from poetry.poetry import Poetry
poetry = Poetry.create(self._full_path)
package = poetry.package
self._package = Package(package.pretty_name, package.version)
self._package.requires += package.requires
self._package.dev_requires += package.dev_requires
self._package.extras = package.extras
self._package.python_versions = package.python_versions
else:
# Execute egg_info
current_dir = os.getcwd()
os.chdir(str(self._full_path))
try:
cwd = base
venv = Env.create_venv(NullIO(), cwd=cwd)
venv.run("python", "setup.py", "egg_info")
finally:
os.chdir(current_dir)
egg_info = list(self._full_path.glob("*.egg-info"))[0]
meta = pkginfo.UnpackedSDist(str(egg_info))
if meta.requires_dist:
reqs = list(meta.requires_dist)
else:
reqs = []
requires = egg_info / "requires.txt"
if requires.exists():
with requires.open() as f:
reqs = parse_requires(f.read())
package = Package(meta.name, meta.version)
package.description = meta.summary
for req in reqs:
package.requires.append(dependency_from_pep_508(req))
if meta.requires_python:
package.python_versions = meta.requires_python
if meta.platforms:
platforms = [p for p in meta.platforms if p.lower() != "unknown"]
if platforms:
package.platform = " || ".join(platforms)
self._package = package
self._package.source_type = "directory"
self._package.source_url = self._path.as_posix()
super(DirectoryDependency, self).__init__(
self._package.name,
self._package.version,
category=category,
optional=optional,
allows_prereleases=True,
name, "*", category=category, optional=optional, allows_prereleases=True
)
@property
......@@ -134,8 +66,8 @@ class DirectoryDependency(Dependency):
return self._full_path.resolve()
@property
def package(self):
return self._package
def base(self):
return self._base
@property
def develop(self):
......
import hashlib
import io
import pkginfo
from pkginfo.distribution import HEADER_ATTRS
from pkginfo.distribution import HEADER_ATTRS_2_0
......@@ -19,6 +17,7 @@ HEADER_ATTRS.update(
class FileDependency(Dependency):
def __init__(
self,
name,
path, # type: Path
category="main", # type: str
optional=False, # type: bool
......@@ -37,18 +36,8 @@ class FileDependency(Dependency):
if self._full_path.is_dir():
raise ValueError("{} is a directory, expected a file".format(self._path))
if self._path.suffix == ".whl":
self._meta = pkginfo.Wheel(str(self._full_path))
else:
# Assume sdist
self._meta = pkginfo.SDist(str(self._full_path))
super(FileDependency, self).__init__(
self._meta.name,
self._meta.version,
category=category,
optional=optional,
allows_prereleases=True,
name, "*", category=category, optional=optional, allows_prereleases=True
)
@property
......@@ -59,10 +48,6 @@ class FileDependency(Dependency):
def full_path(self):
return self._full_path.resolve()
@property
def metadata(self):
return self._meta
def is_file(self):
return True
......
......@@ -73,6 +73,7 @@ class Package(object):
self._python_constraint = parse_constraint("*")
self._python_marker = AnyMarker()
self.platform = None
self.marker = AnyMarker()
self.root_dir = None
......@@ -168,19 +169,6 @@ class Package(object):
return self._python_marker
@property
def platform(self): # type: () -> str
return self._platform
@platform.setter
def platform(self, value): # type: (str) -> None
self._platform = value
self._platform_constraint = parse_generic_constraint(value)
@property
def platform_constraint(self):
return self._platform_constraint
@property
def license(self):
return self._license
......@@ -273,7 +261,7 @@ class Package(object):
file_path = Path(constraint["file"])
dependency = FileDependency(
file_path, category=category, base=self.root_dir
name, file_path, category=category, base=self.root_dir
)
elif "path" in constraint:
path = Path(constraint["path"])
......@@ -285,10 +273,15 @@ class Package(object):
if is_file:
dependency = FileDependency(
path, category=category, optional=optional, base=self.root_dir
name,
path,
category=category,
optional=optional,
base=self.root_dir,
)
else:
dependency = DirectoryDependency(
name,
path,
category=category,
optional=optional,
......
......@@ -146,6 +146,12 @@ def convert_markers(marker):
else:
variable, op, value = group
group_name = str(variable)
# python_full_version is equivalent to python_version
# for Poetry so we merge them
if group_name == "python_full_version":
group_name = "python_version"
if group_name not in requirements:
requirements[group_name] = []
......
......@@ -11,6 +11,8 @@ from contextlib import contextmanager
from tempfile import mkdtemp
from typing import List
from poetry.io import NullIO
from poetry.packages import Dependency
from poetry.packages import DependencyPackage
from poetry.packages import DirectoryDependency
......@@ -30,8 +32,10 @@ from poetry.repositories import Pool
from poetry.utils._compat import PY35
from poetry.utils._compat import Path
from poetry.utils.helpers import parse_requires
from poetry.utils.toml_file import TomlFile
from poetry.utils.helpers import safe_rmtree
from poetry.utils.env import Env
from poetry.utils.env import EnvCommandError
from poetry.utils.setup_reader import SetupReader
from poetry.vcs.git import Git
......@@ -175,89 +179,16 @@ class Provider:
if dependency.tag or dependency.rev:
revision = dependency.reference
pyproject = TomlFile(tmp_dir / "pyproject.toml")
pyproject_content = None
has_poetry = False
if pyproject.exists():
pyproject_content = pyproject.read()
has_poetry = (
"tool" in pyproject_content
and "poetry" in pyproject_content["tool"]
)
if pyproject_content and has_poetry:
# If a pyproject.toml file exists
# We use it to get the information we need
info = pyproject_content["tool"]["poetry"]
name = info["name"]
version = info["version"]
package = Package(name, version, version)
package.source_type = dependency.vcs
package.source_url = dependency.source
package.source_reference = dependency.reference
for req_name, req_constraint in info["dependencies"].items():
if req_name == "python":
package.python_versions = req_constraint
continue
package.add_dependency(req_name, req_constraint)
else:
# We need to use setup.py here
# to figure the information we need
# We need to place ourselves in the proper
# folder for it to work
venv = Env.get(self._io)
current_dir = os.getcwd()
os.chdir(tmp_dir.as_posix())
try:
venv.run("python", "setup.py", "egg_info")
# Sometimes pathlib will fail on recursive
# symbolic links, so we need to workaround it
# and use the glob module instead.
# Note that this does not happen with pathlib2
# so it's safe to use it for Python < 3.4.
if PY35:
egg_info = next(
Path(p)
for p in glob.glob(
os.path.join(str(tmp_dir), "**", "*.egg-info"),
recursive=True,
)
)
else:
egg_info = next(tmp_dir.glob("**/*.egg-info"))
meta = pkginfo.UnpackedSDist(str(egg_info))
if meta.requires_dist:
reqs = list(meta.requires_dist)
else:
reqs = []
requires = egg_info / "requires.txt"
if requires.exists():
with requires.open() as f:
reqs = parse_requires(f.read())
package = Package(meta.name, meta.version)
for req in reqs:
dep = dependency_from_pep_508(req)
if dep.in_extras:
for extra in dep.in_extras:
if extra not in package.extras:
package.extras[extra] = []
package.extras[extra].append(dep)
directory_dependency = DirectoryDependency(
dependency.name,
tmp_dir,
category=dependency.category,
optional=dependency.is_optional(),
)
for extra in dependency.extras:
directory_dependency.extras.append(extra)
package.requires.append(dep)
except Exception:
raise
finally:
os.chdir(current_dir)
package = self.search_for_directory(directory_dependency)[0]
package.source_type = "git"
package.source_url = dependency.source
......@@ -265,52 +196,198 @@ class Provider:
except Exception:
raise
finally:
shutil.rmtree(tmp_dir.as_posix())
safe_rmtree(str(tmp_dir))
if dependency.name != package.name:
return [package]
def search_for_file(self, dependency): # type: (FileDependency) -> List[Package]
if dependency.path.suffix == ".whl":
meta = pkginfo.Wheel(str(dependency.full_path))
else:
# Assume sdist
meta = pkginfo.SDist(str(dependency.full_path))
if dependency.name != meta.name:
# For now, the dependency's name must match the actual package's name
raise RuntimeError(
"The dependency name for {} does not match the actual package's name: {}".format(
dependency.name, package.name
dependency.name, meta.name
)
)
if dependency.extras:
for extra in dependency.extras:
if extra in package.extras:
for dep in package.extras[extra]:
dep.activate()
return [package]
def search_for_file(self, dependency): # type: (FileDependency) -> List[Package]
package = Package(dependency.name, dependency.pretty_constraint)
package = Package(meta.name, meta.version)
package.source_type = "file"
package.source_url = dependency.path.as_posix()
package.description = dependency.metadata.summary
for req in dependency.metadata.requires_dist:
package.requires.append(dependency_from_pep_508(req))
package.description = meta.summary
for req in meta.requires_dist:
dep = dependency_from_pep_508(req)
for extra in dep.in_extras:
if extra not in package.extras:
package.extras[extra] = []
package.extras[extra].append(dep)
if dependency.metadata.requires_python:
package.python_versions = dependency.metadata.requires_python
if not dep.is_optional():
package.requires.append(dep)
if dependency.metadata.platforms:
package.platform = " || ".join(dependency.metadata.platforms)
if meta.requires_python:
package.python_versions = meta.requires_python
package.hashes = [dependency.hash()]
for extra in dependency.extras:
if extra in package.extras:
for dep in package.extras[extra]:
dep.activate()
package.requires += package.extras[extra]
return [package]
def search_for_directory(
self, dependency
): # type: (DirectoryDependency) -> List[Package]
package = dependency.package
if dependency.extras:
for extra in dependency.extras:
if extra in package.extras:
for dep in package.extras[extra]:
dep.activate()
if dependency.supports_poetry():
from poetry.poetry import Poetry
poetry = Poetry.create(dependency.full_path)
pkg = poetry.package
package = Package(pkg.name, pkg.version)
for dep in pkg.requires:
if not dep.is_optional():
package.requires.append(dep)
for extra, deps in pkg.extras.items():
if extra not in package.extras:
package.extras[extra] = []
for dep in deps:
package.extras[extra].append(dep)
package.python_versions = pkg.python_versions
else:
# Execute egg_info
current_dir = os.getcwd()
os.chdir(str(dependency.full_path))
try:
cwd = dependency.full_path
venv = Env.get(NullIO(), cwd=cwd)
venv.run("python", "setup.py", "egg_info")
except EnvCommandError:
result = SetupReader.read_from_directory(dependency.full_path)
if not result["name"]:
# The name could not be determined
# We use the dependency name
result["name"] = dependency.name
if not result["version"]:
# The version could not be determined
# so we raise an error since it is mandatory
raise RuntimeError(
"Unable to retrieve the package version for {}".format(
dependency.path
)
)
package_name = result["name"]
package_version = result["version"]
python_requires = result["python_requires"]
if python_requires is None:
python_requires = "*"
package_summary = ""
requires = ""
for dep in result["install_requires"]:
requires += dep + "\n"
if result["extras_require"]:
requires += "\n"
for extra_name, deps in result["extras_require"].items():
requires += "[{}]\n".format(extra_name)
for dep in deps:
requires += dep + "\n"
requires += "\n"
reqs = parse_requires(requires)
else:
os.chdir(current_dir)
# Sometimes pathlib will fail on recursive
# symbolic links, so we need to workaround it
# and use the glob module instead.
# Note that this does not happen with pathlib2
# so it's safe to use it for Python < 3.4.
if PY35:
egg_info = next(
Path(p)
for p in glob.glob(
os.path.join(str(dependency.full_path), "**", "*.egg-info"),
recursive=True,
)
)
else:
egg_info = next(dependency.full_path.glob("**/*.egg-info"))
meta = pkginfo.UnpackedSDist(str(egg_info))
package_name = meta.name
package_version = meta.version
package_summary = meta.summary
python_requires = meta.requires_python
if meta.requires_dist:
reqs = list(meta.requires_dist)
else:
reqs = []
requires = egg_info / "requires.txt"
if requires.exists():
with requires.open() as f:
reqs = parse_requires(f.read())
finally:
os.chdir(current_dir)
package = Package(package_name, package_version)
if dependency.name != package.name:
# For now, the dependency's name must match the actual package's name
raise RuntimeError(
"The dependency name for {} does not match the actual package's name: {}".format(
dependency.name, package.name
)
)
package.description = package_summary
for req in reqs:
dep = dependency_from_pep_508(req)
if dep.in_extras:
for extra in dep.in_extras:
if extra not in package.extras:
package.extras[extra] = []
package.extras[extra].append(dep)
if not dep.is_optional():
package.requires.append(dep)
if python_requires:
package.python_versions = python_requires
package.source_type = "directory"
package.source_url = dependency.path.as_posix()
for extra in dependency.extras:
if extra in package.extras:
for dep in package.extras[extra]:
dep.activate()
package.requires += package.extras[extra]
return [package]
......@@ -534,14 +611,15 @@ class Provider:
)
if package.dependency.is_directory() and dep.is_directory():
if dep.package.source_url.startswith(package.source_url):
relative = (
Path(package.source_url) / dep.package.source_url
).relative_to(package.source_url)
if dep.path.as_posix().startswith(package.source_url):
relative = (Path(package.source_url) / dep.path).relative_to(
package.source_url
)
else:
relative = Path(package.source_url) / dep.package.source_url
relative = Path(package.source_url) / dep.path
dep.package.source_url = relative.as_posix()
# TODO: Improve the way we set the correct relative path for dependencies
dep._path = relative
package.requires = dependencies
......
......@@ -7,6 +7,7 @@ from typing import List
from poetry.mixology import resolve_version
from poetry.mixology.failure import SolveFailure
from poetry.packages import DependencyPackage
from poetry.packages import Package
from poetry.semver import parse_constraint
from poetry.version.markers import AnyMarker
......@@ -55,8 +56,33 @@ class Solver:
for pkg in self._installed.packages:
if package.name == pkg.name:
installed = True
# Checking version
if package.version != pkg.version:
if pkg.source_type == "git" and package.source_type == "git":
# Trying to find the currently installed version
for locked in self._locked.packages:
if (
locked.name == pkg.name
and locked.source_type == pkg.source_type
and locked.source_url == pkg.source_url
and locked.source_reference == pkg.source_reference
):
pkg = Package(pkg.name, locked.version)
pkg.source_type = "git"
pkg.source_url = locked.source_url
pkg.source_reference = locked.source_reference
break
if (
pkg.source_url != package.source_url
or pkg.source_reference != package.source_reference
):
operations.append(Update(pkg, package))
else:
operations.append(
Install(package).skip("Already installed")
)
elif package.version != pkg.version:
# Checking version
operations.append(Update(pkg, package))
else:
operations.append(Install(package).skip("Already installed"))
......
class RepositoryError(Exception):
pass
class PackageNotFound(Exception):
pass
import re
from poetry.packages import Package
from poetry.utils.env import Env
......@@ -17,7 +19,26 @@ class InstalledRepository(Repository):
freeze_output = env.run("pip", "freeze")
for line in freeze_output.split("\n"):
if "==" in line:
name, version = line.split("==")
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)
return repo
......@@ -41,6 +41,7 @@ from poetry.utils._compat import Path
from poetry.utils.helpers import canonicalize_name, get_http_basic_auth
from poetry.version.markers import InvalidMarker
from .exceptions import PackageNotFound
from .pypi_repository import PyPiRepository
......@@ -265,9 +266,17 @@ class LegacyRepository(PyPiRepository):
req = req.split(";")[0]
dependency = dependency_from_pep_508(req)
except ValueError:
# Likely unable to parse constraint so we skip it
self._log(
"Invalid constraint ({}) found in {}-{} dependencies, "
"skipping".format(req, package.name, package.version),
level="debug",
)
continue
if dependency.extras:
for extra in dependency.extras:
if dependency.in_extras:
for extra in dependency.in_extras:
if extra not in package.extras:
package.extras[extra] = []
......@@ -297,7 +306,7 @@ class LegacyRepository(PyPiRepository):
def _get_release_info(self, name, version): # type: (str, str) -> dict
page = self._get("/{}/".format(canonicalize_name(name).replace(".", "-")))
if page is None:
raise ValueError('No package named "{}"'.format(name))
raise PackageNotFound('No package named "{}"'.format(name))
data = {
"name": name,
......@@ -310,7 +319,7 @@ class LegacyRepository(PyPiRepository):
links = list(page.links_for_version(Version.parse(version)))
if not links:
raise ValueError(
raise PackageNotFound(
'No valid distribution links found for package: "{}" version: "{}"'.format(
name, version
)
......
from typing import List
from typing import Union
import poetry.packages
from .base_repository import BaseRepository
from .exceptions import PackageNotFound
from .repository import Repository
......@@ -65,7 +65,7 @@ class Pool(BaseRepository):
for repository in self._repositories:
try:
package = repository.package(name, version, extras=extras)
except ValueError:
except PackageNotFound:
continue
if package:
......@@ -73,7 +73,7 @@ class Pool(BaseRepository):
return package
raise PackageNotFound("Package [{}] not found.".format(name))
raise PackageNotFound("Package {} ({}) not found.".format(name, version))
def find_packages(
self, name, constraint=None, extras=None, allow_prereleases=False
......
......@@ -39,8 +39,10 @@ from poetry.utils._compat import to_str
from poetry.utils.helpers import parse_requires
from poetry.utils.helpers import temporary_directory
from poetry.utils.env import Env
from poetry.utils.setup_reader import SetupReader
from poetry.version.markers import InvalidMarker
from .exceptions import PackageNotFound
from .repository import Repository
......@@ -225,7 +227,7 @@ class PyPiRepository(Repository):
def _get_package_info(self, name): # type: (str) -> dict
data = self._get("pypi/{}/json".format(name))
if data is None:
raise ValueError("Package [{}] not found.".format(name))
raise PackageNotFound("Package [{}] not found.".format(name))
return data
......@@ -261,7 +263,7 @@ class PyPiRepository(Repository):
json_data = self._get("pypi/{}/{}/json".format(name, version))
if json_data is None:
raise ValueError("Package [{}] not found.".format(name))
raise PackageNotFound("Package [{}] not found.".format(name))
info = json_data["info"]
data = {
......@@ -460,6 +462,9 @@ class PyPiRepository(Repository):
else:
if suffix == ".bz2":
gz = BZ2File(str(filepath))
suffixes = filepath.suffixes
if len(suffixes) > 1 and suffixes[-2] == ".tar":
suffix = ".tar.bz2"
else:
gz = GzipFile(str(filepath))
suffix = ".tar.gz"
......@@ -501,45 +506,42 @@ class PyPiRepository(Repository):
return info
# Still nothing, assume no dependencies
# We could probably get them by executing
# python setup.py egg-info but I don't feel
# confortable executing a file just for the sake
# of getting dependencies.
return info
# Still nothing, try reading (without executing it)
# the setup.py file.
try:
info.update(self._inspect_sdist_with_setup(sdist_dir))
return info
except Exception as e:
self._log(
"An error occurred when reading setup.py or setup.cfg: {}".format(
str(e)
),
"warning",
)
return info
def _inspect_sdist_with_setup(self, sdist_dir):
info = {"requires_python": None, "requires_dist": None}
setup = sdist_dir / "setup.py"
if not setup.exists():
return info
result = SetupReader.read_from_directory(sdist_dir)
requires = ""
for dep in result["install_requires"]:
requires += dep + "\n"
venv = Env.create_venv(NullIO())
if result["extras_require"]:
requires += "\n"
current_dir = os.getcwd()
os.chdir(sdist_dir.as_posix())
for extra_name, deps in result["extras_require"].items():
requires += "[{}]\n".format(extra_name)
try:
venv.run("python", "setup.py", "egg_info")
for dep in deps:
requires += dep + "\n"
egg_info = list(sdist_dir.glob("**/*.egg-info"))[0]
meta = pkginfo.UnpackedSDist(str(egg_info))
if meta.requires_python:
info["requires_python"] = meta.requires_python
if meta.requires_dist:
info["requires_dist"] = list(meta.requires_dist)
else:
requires = egg_info / "requires.txt"
if requires.exists():
with requires.open() as f:
info["requires_dist"] = parse_requires(f.read())
except Exception:
pass
requires += "\n"
os.chdir(current_dir)
info["requires_dist"] = parse_requires(requires)
info["requires_python"] = result["python_requires"]
return info
......
import sys
try:
import pathlib2
from pathlib2 import Path
from functools32 import lru_cache
except ImportError:
from pathlib import Path
from functools import lru_cache
try: # Python 2
long = long
......@@ -21,6 +20,12 @@ PY35 = sys.version_info >= (3, 5)
PY36 = sys.version_info >= (3, 6)
if PY35:
from pathlib import Path
else:
from pathlib2 import Path
def decode(string, encodings=None):
if not PY2 and not isinstance(string, bytes):
return string
......
......@@ -302,8 +302,6 @@ class Env(object):
Return path to the given executable.
"""
bin_path = (self._bin_dir / bin).with_suffix(".exe" if self._is_windows else "")
if not bin_path.exists():
return bin
return str(bin_path)
......@@ -516,3 +514,19 @@ class NullEnv(SystemEnv):
def _bin(self, bin):
return bin
class MockEnv(NullEnv):
def __init__(self, version_info=(3, 7, 0), python_implementation="cpython"):
super(MockEnv, self).__init__()
self._version_info = version_info
self._python_implementation = python_implementation
@property
def version_info(self): # type: () -> Tuple[int]
return self._version_info
@property
def python_implementation(self): # type: () -> str
return self._python_implementation
import os
import re
import shutil
import stat
import tempfile
from contextlib import contextmanager
......@@ -89,3 +91,12 @@ def get_http_basic_auth(
return repo_auth["username"], repo_auth.get("password")
return None
def _on_rm_error(func, path, exc_info):
os.chmod(path, stat.S_IWRITE)
func(path)
def safe_rmtree(path):
shutil.rmtree(path, onerror=_on_rm_error)
import ast
try:
from configparser import ConfigParser
except ImportError:
from ConfigParser import ConfigParser
from typing import Any
from typing import Dict
from typing import Iterable
from typing import List
from typing import Optional
from typing import Tuple
from typing import Union
from ._compat import PY35
from ._compat import basestring
from ._compat import Path
class SetupReader(object):
"""
Class that reads a setup.py file without executing it.
"""
DEFAULT = {
"name": None,
"version": None,
"install_requires": [],
"extras_require": {},
"python_requires": None,
}
FILES = ["setup.py", "setup.cfg"]
@classmethod
def read_from_directory(
cls, directory
): # type: (Union[basestring, Path]) -> Dict[str, Union[List, Dict]]
if isinstance(directory, basestring):
directory = Path(directory)
result = cls.DEFAULT.copy()
for filename in cls.FILES:
filepath = directory / filename
if not filepath.exists():
continue
new_result = getattr(cls(), "read_{}".format(filename.replace(".", "_")))(
filepath
)
for key in result.keys():
if new_result[key]:
result[key] = new_result[key]
return result
@classmethod
def _is_empty_result(cls, result): # type: (Dict[str, Any]) -> bool
return (
not result["install_requires"]
and not result["extras_require"]
and not result["python_requires"]
)
def read_setup_py(
self, filepath
): # type: (Union[basestring, Path]) -> Dict[str, Union[List, Dict]]
if not PY35:
return self.DEFAULT
if isinstance(filepath, basestring):
filepath = Path(filepath)
with filepath.open(encoding="utf-8") as f:
content = f.read()
result = {}
body = ast.parse(content).body
setup_call, body = self._find_setup_call(body)
if not setup_call:
return self.DEFAULT
# Inspecting keyword arguments
result["name"] = self._find_single_string(setup_call, body, "name")
result["version"] = self._find_single_string(setup_call, body, "version")
result["install_requires"] = self._find_install_requires(setup_call, body)
result["extras_require"] = self._find_extras_require(setup_call, body)
result["python_requires"] = self._find_single_string(
setup_call, body, "python_requires"
)
return result
def read_setup_cfg(
self, filepath
): # type: (Union[basestring, Path]) -> Dict[str, Union[List, Dict]]
parser = ConfigParser()
parser.read(str(filepath))
name = None
version = None
if parser.has_option("metadata", "name"):
name = parser.get("metadata", "name")
if parser.has_option("metadata", "version"):
version = parser.get("metadata", "version")
install_requires = []
extras_require = {}
python_requires = None
if parser.has_section("options"):
if parser.has_option("options", "install_requires"):
for dep in parser.get("options", "install_requires").split("\n"):
dep = dep.strip()
if not dep:
continue
install_requires.append(dep)
if parser.has_option("options", "python_requires"):
python_requires = parser.get("options", "python_requires")
if parser.has_section("options.extras_require"):
for group in parser.options("options.extras_require"):
extras_require[group] = []
deps = parser.get("options.extras_require", group)
for dep in deps.split("\n"):
dep = dep.strip()
if not dep:
continue
extras_require[group].append(dep)
return {
"name": name,
"version": version,
"install_requires": install_requires,
"extras_require": extras_require,
"python_requires": python_requires,
}
def _find_setup_call(
self, elements
): # type: (List[Any]) -> Tuple[Optional[ast.Call], Optional[List[Any]]]
funcdefs = []
for i, element in enumerate(elements):
if isinstance(element, ast.If) and i == len(elements) - 1:
# Checking if the last element is an if statement
# and if it is 'if __name__ == "__main__"' which
# could contain the call to setup()
test = element.test
if not isinstance(test, ast.Compare):
continue
left = test.left
if not isinstance(left, ast.Name):
continue
if left.id != "__name__":
continue
setup_call, body = self._find_sub_setup_call([element])
if not setup_call:
continue
return setup_call, body + elements
if not isinstance(element, ast.Expr):
if isinstance(element, ast.FunctionDef):
funcdefs.append(element)
continue
value = element.value
if not isinstance(value, ast.Call):
continue
func = value.func
if not isinstance(func, ast.Name):
continue
if func.id != "setup":
continue
return value, elements
# Nothing, we inspect the function definitions
return self._find_sub_setup_call(funcdefs)
def _find_sub_setup_call(
self, elements
): # type: (List[Any]) -> Tuple[Optional[ast.Call], Optional[List[Any]]]
for element in elements:
if not isinstance(element, (ast.FunctionDef, ast.If)):
continue
setup_call = self._find_setup_call(element.body)
if setup_call != (None, None):
setup_call, body = setup_call
body = elements + body
return setup_call, body
return None, None
def _find_install_requires(
self, call, body
): # type: (ast.Call, Iterable[Any]) -> List[str]
install_requires = []
value = self._find_in_call(call, "install_requires")
if value is None:
# Trying to find in kwargs
kwargs = self._find_call_kwargs(call)
if kwargs is None or not isinstance(kwargs, ast.Name):
return install_requires
variable = self._find_variable_in_body(body, kwargs.id)
if not isinstance(variable, (ast.Dict, ast.Call)):
return install_requires
if isinstance(variable, ast.Call):
if not isinstance(variable.func, ast.Name):
return install_requires
if variable.func.id != "dict":
return install_requires
value = self._find_in_call(variable, "install_requires")
else:
value = self._find_in_dict(variable, "install_requires")
if value is None:
return install_requires
if isinstance(value, ast.List):
for el in value.elts:
install_requires.append(el.s)
elif isinstance(value, ast.Name):
variable = self._find_variable_in_body(body, value.id)
if variable is not None and isinstance(variable, ast.List):
for el in variable.elts:
install_requires.append(el.s)
return install_requires
def _find_extras_require(
self, call, body
): # type: (ast.Call, Iterable[Any]) -> Dict[str, List]
extras_require = {}
value = self._find_in_call(call, "extras_require")
if value is None:
# Trying to find in kwargs
kwargs = self._find_call_kwargs(call)
if kwargs is None or not isinstance(kwargs, ast.Name):
return extras_require
variable = self._find_variable_in_body(body, kwargs.id)
if not isinstance(variable, (ast.Dict, ast.Call)):
return extras_require
if isinstance(variable, ast.Call):
if not isinstance(variable.func, ast.Name):
return extras_require
if variable.func.id != "dict":
return extras_require
value = self._find_in_call(variable, "extras_require")
else:
value = self._find_in_dict(variable, "extras_require")
if value is None:
return extras_require
if isinstance(value, ast.Dict):
for key, val in zip(value.keys, value.values):
extras_require[key.s] = [e.s for e in val.elts]
elif isinstance(value, ast.Name):
variable = self._find_variable_in_body(body, value.id)
if variable is None or not isinstance(variable, ast.Dict):
return extras_require
for key, val in zip(variable.keys, variable.values):
extras_require[key.s] = [e.s for e in val.elts]
return extras_require
def _find_single_string(
self, call, body, name
): # type: (ast.Call, List[Any], str) -> Optional[str]
value = self._find_in_call(call, name)
if value is None:
# Trying to find in kwargs
kwargs = self._find_call_kwargs(call)
if kwargs is None or not isinstance(kwargs, ast.Name):
return
variable = self._find_variable_in_body(body, kwargs.id)
if not isinstance(variable, (ast.Dict, ast.Call)):
return
if isinstance(variable, ast.Call):
if not isinstance(variable.func, ast.Name):
return
if variable.func.id != "dict":
return
value = self._find_in_call(variable, name)
else:
value = self._find_in_dict(variable, name)
if value is None:
return
if isinstance(value, ast.Str):
return value.s
elif isinstance(value, ast.Name):
variable = self._find_variable_in_body(body, value.id)
if variable is not None and isinstance(variable, ast.Str):
return variable.s
def _find_in_call(self, call, name): # type: (ast.Call, str) -> Optional[Any]
for keyword in call.keywords:
if keyword.arg == name:
return keyword.value
def _find_call_kwargs(self, call): # type: (ast.Call) -> Optional[Any]
kwargs = None
for keyword in call.keywords:
if keyword.arg is None:
kwargs = keyword.value
return kwargs
def _find_variable_in_body(
self, body, name
): # type: (Iterable[Any], str) -> Optional[Any]
found = None
for elem in body:
if found:
break
if not isinstance(elem, ast.Assign):
continue
for target in elem.targets:
if not isinstance(target, ast.Name):
continue
if target.id == name:
return elem.value
def _find_in_dict(self, dict_, name): # type: (ast.Call, str) -> Optional[Any]
for key, val in zip(dict_.keys, dict_.values):
if isinstance(key, ast.Str) and key.s == name:
return val
......@@ -21,7 +21,10 @@ def format_python_constraint(constraint):
This helper will help in transforming
disjunctive constraint into proper constraint.
"""
if isinstance(constraint, Version) and constraint.precision < 3:
if isinstance(constraint, Version):
if constraint.precision >= 3:
return "=={}".format(str(constraint))
# Transform 3.6 or 3
if constraint.precision == 2:
# 3.6
......
[tool.poetry]
name = "poetry"
version = "0.12.4"
version = "0.12.8"
description = "Python dependency management and packaging made easy."
authors = [
"Sébastien Eustace <sebastien@eustace.io>"
......@@ -34,7 +34,7 @@ cachecontrol = { version = "^0.12.4", extras = ["filecache"] }
pkginfo = "^1.4"
html5lib = "^1.0"
shellingham = "^1.1"
tomlkit = "^0.4.4"
tomlkit = "^0.5.1"
# The typing module is not in the stdlib in Python 2.7 and 3.4
typing = { version = "^3.6", python = "~2.7 || ~3.4" }
......@@ -43,6 +43,8 @@ typing = { version = "^3.6", python = "~2.7 || ~3.4" }
pathlib2 = { version = "^2.3", python = "~2.7 || ~3.4" }
# Use virtualenv for Python 2.7 since venv does not exist
virtualenv = { version = "^16.0", python = "~2.7" }
# functools32 is needed for Python 2.7
functools32 = { version = "^3.2.3", python = "~2.7" }
[tool.poetry.dev-dependencies]
pytest = "^3.4"
......@@ -55,7 +57,7 @@ pygments-github-lexers = "^0.0.5"
black = { version = "^18.3-alpha.0", python = "^3.6" }
pre-commit = "^1.10"
tox = "^3.0"
pytest-sugar = "^0.9.1"
pytest-sugar = "^0.9.2"
[tool.poetry.scripts]
......
......@@ -51,6 +51,7 @@ class MakeReleaseCommand(Command):
from poetry.repositories.repository import Repository
from poetry.utils._compat import Path
from poetry.utils.helpers import temporary_directory
from poetry.vcs import get_vcs
project = Poetry.create(Path.cwd())
package = project.package
......@@ -60,11 +61,21 @@ class MakeReleaseCommand(Command):
pool = Pool()
pool.add_repository(project.locker.locked_repository(with_dev_reqs=True))
vcs = get_vcs(Path(__file__).parent)
if vcs:
vcs_excluded = [str(f) for f in vcs.get_ignored_files()]
else:
vcs_excluded = []
with temporary_directory() as tmp_dir:
# Copy poetry to tmp dir
poetry_dir = os.path.join(tmp_dir, "poetry")
shutil.copytree(
os.path.join(os.path.dirname(__file__), "poetry"), poetry_dir
os.path.join(os.path.dirname(__file__), "poetry"),
poetry_dir,
ignore=lambda dir_, names: set(vcs_excluded).intersection(
set([os.path.join(dir_, name) for name in names])
),
)
for version, python in sorted(pythons.items()):
self.line(
......@@ -102,9 +113,14 @@ class MakeReleaseCommand(Command):
continue
path = os.path.join(os.path.realpath(root), f)
relpath = os.path.relpath(
path, os.path.realpath(tmp_dir)
)
if relpath in vcs_excluded:
continue
tar_info = tar.gettarinfo(str(path), arcname=relpath)
if tar_info.isreg():
......
import pytest
import shutil
import tempfile
try:
import urllib.parse as urlparse
except ImportError:
import urlparse
from poetry.config import Config
from poetry.utils._compat import Path
from poetry.utils.toml_file import TomlFile
......@@ -11,3 +18,28 @@ def config(): # type: () -> Config
f.close()
return Config(TomlFile(f.name))
def mock_clone(_, 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))
@pytest.fixture(autouse=True)
def git_mock(mocker):
# Patch git module to not actually clone projects
mocker.patch("poetry.vcs.git.Git.clone", new=mock_clone)
mocker.patch("poetry.vcs.git.Git.checkout", new=lambda *_: None)
p = mocker.patch("poetry.vcs.git.Git.rev_parse")
p.return_value = "9cf87a285a2d3fbb0b9fa621997b3acc3631ed24"
import sys
from cleo.testers import CommandTester
from tests.helpers import get_dependency
from tests.helpers import get_package
......@@ -78,7 +75,6 @@ Resolving dependencies...
Resolution results:
- pendulum (2.0.3)
- cleo (0.6.5)
- demo (0.1.2)
"""
......
......@@ -148,7 +148,7 @@ description = ""
authors = ["Your Name <you@example.com>"]
[tool.poetry.dependencies]
python = "*"
python = "^3.7"
[tool.poetry.dev-dependencies]
"""
......
......@@ -17,6 +17,8 @@ from poetry.packages import Locker as BaseLocker
from poetry.repositories import Pool
from poetry.repositories import Repository
from poetry.utils._compat import Path
from poetry.utils.env import Env
from poetry.utils.env import MockEnv
from poetry.utils.toml_file import TomlFile
......@@ -48,6 +50,8 @@ def installed():
@pytest.fixture(autouse=True)
def setup(mocker, installer, installed):
Env._env = MockEnv()
# Set Installer's installer
p = mocker.patch("poetry.installation.installer.Installer._get_installer")
p.return_value = installer
......@@ -74,6 +78,7 @@ def setup(mocker, installer, installed):
os.environ.clear()
os.environ.update(environ)
Env._env = None
class Application(BaseApplication):
......
# -*- coding: utf-8 -*-
from setuptools import setup
......@@ -13,7 +12,7 @@ kwargs = dict(
url="https://github.com/demo/demo",
packages=["demo"],
install_requires=["pendulum>=1.4.4"],
extras_require={"foo": ["cleo"]},
extras_require={"foo": ["cleo"], "bar": ["tomlkit"]},
)
......
# -*- coding: utf-8 -*-
import ast
import os
from setuptools import setup
def read_version():
with open(os.path.join(os.path.dirname(__file__), "demo", "__init__.py")) as f:
for line in f:
if line.startswith("__version__ = "):
return ast.literal_eval(line[len("__version__ = ") :].strip())
kwargs = dict(
name="demo",
license="MIT",
version=read_version(),
description="Demo project.",
author="Sébastien Eustace",
author_email="sebastien@eustace.io",
url="https://github.com/demo/demo",
packages=["demo"],
install_requires=["pendulum>=1.4.4"],
extras_require={"foo": ["cleo"]},
)
setup(**kwargs)
[[package]]
description = ""
category = "dev"
name = "cachy"
optional = true
python-versions = "*"
version = "0.2.0"
[[package]]
description = ""
category = "dev"
name = "pendulum"
optional = true
python-versions = "*"
version = "1.4.4"
[[package]]
category = "main"
description = ""
name = "project-with-extras"
......@@ -40,7 +24,7 @@ python-versions = "*"
version = "1.2.3"
[package.dependencies]
project-with-extras = "1.2.3"
project-with-extras = "*"
[package.source]
reference = ""
......@@ -52,7 +36,5 @@ content-hash = "123456789"
python-versions = "*"
[metadata.hashes]
cachy = []
project-with-extras = []
project-with-transitive-directory-dependencies = []
pendulum = []
[[package]]
description = ""
category = "main"
name = "cachy"
optional = true
python-versions = "*"
version = "0.2.0"
[[package]]
description = ""
category = "main"
name = "pendulum"
optional = false
python-versions = "*"
......@@ -39,6 +31,5 @@ content-hash = "123456789"
python-versions = "*"
[metadata.hashes]
cachy = []
project-with-extras = []
pendulum = []
[[package]]
name = "demo"
version = "0.1.0"
description = "Description"
description = ""
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
......@@ -12,7 +12,7 @@ reference = ""
url = "tests/fixtures/distributions/demo-0.1.0-py2.py3-none-any.whl"
[package.dependencies]
pendulum = ">=1.4.0.0,<2.0.0.0"
pendulum = ">=1.4.4"
[[package]]
name = "pendulum"
......@@ -27,5 +27,5 @@ python-versions = "*"
content-hash = "123456789"
[metadata.hashes]
demo = ["373a7e4bb99653541f35e52738f1b8a8e889c12e5f8b93a88c757d948b0cbe89"]
demo = ["70e704135718fffbcbf61ed1fc45933cfd86951a744b681000eaaa75da31f17a"]
pendulum = []
......@@ -77,21 +77,12 @@ class Locker(BaseLocker):
self._written_data = data
@pytest.fixture(autouse=True)
def setup():
# Mock python version and platform to get reliable tests
original_platform = sys.platform
sys.platform = "darwin"
yield
sys.platform = original_platform
@pytest.fixture()
def package():
return ProjectPackage("root", "1.0")
p = ProjectPackage("root", "1.0")
p.root_dir = Path.cwd()
return p
@pytest.fixture()
......@@ -472,8 +463,10 @@ def test_run_with_optional_and_python_restricted_dependencies(
def test_run_with_optional_and_platform_restricted_dependencies(
installer, locker, repo, package
installer, locker, repo, package, mocker
):
mocker.patch("sys.platform", "darwin")
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.1")
package_c12 = get_package("C", "1.2")
......@@ -678,11 +671,12 @@ def test_run_installs_with_local_file(installer, locker, repo, package):
def test_run_installs_with_local_poetry_directory_and_extras(
installer, locker, repo, package, tmpdir
):
file_path = Path("tests/fixtures/project_with_extras/")
package.add_dependency("demo", {"path": str(file_path), "extras": ["extras_a"]})
file_path = Path("tests/fixtures/project_with_extras")
package.add_dependency(
"project-with-extras", {"path": str(file_path), "extras": ["extras_a"]}
)
repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cachy", "0.2.0"))
installer.run()
......@@ -699,7 +693,9 @@ def test_run_installs_with_local_poetry_directory_transitive(
file_path = Path(
"tests/fixtures/directory/project_with_transitive_directory_dependencies/"
)
package.add_dependency("demo", {"path": str(file_path)})
package.add_dependency(
"project-with-transitive-directory-dependencies", {"path": str(file_path)}
)
repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cachy", "0.2.0"))
......@@ -717,7 +713,7 @@ def test_run_installs_with_local_setuptools_directory(
installer, locker, repo, package, tmpdir
):
file_path = Path("tests/fixtures/project_with_setup/")
package.add_dependency("demo", {"path": str(file_path)})
package.add_dependency("my-package", {"path": str(file_path)})
repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cachy", "0.2.0"))
......
......@@ -20,6 +20,10 @@ classifiers = [
"Topic :: Software Development :: Libraries :: Python Modules"
]
exclude = [
"**/*.xml"
]
# Requirements
[tool.poetry.dependencies]
python = "^3.6"
......
[tool.poetry]
name = "single-python"
version = "0.1"
description = "Some description."
authors = [
"Sébastien Eustace <sebastien@eustace.io>"
]
license = "MIT"
readme = "README.rst"
homepage = "https://poetry.eustace.io/"
[tool.poetry.dependencies]
python = "2.7.15"
from distutils.core import Extension
extensions = [Extension("extended.extended", ["src/extended/extended.c"])]
def build(setup_kwargs):
setup_kwargs.update({"ext_modules": extensions})
[tool.poetry]
name = "extended"
version = "0.1"
description = "Some description."
authors = [
"Sébastien Eustace <sebastien@eustace.io>"
]
license = "MIT"
readme = "README.rst"
homepage = "https://poetry.eustace.io/"
build = "build.py"
#include <Python.h>
static PyObject *hello(PyObject *self) {
return PyUnicode_FromString("Hello");
}
static PyMethodDef module_methods[] = {
{
"hello",
(PyCFunction) hello,
NULL,
PyDoc_STR("Say hello.")
},
{NULL}
};
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"extended",
NULL,
-1,
module_methods,
NULL,
NULL,
NULL,
NULL,
};
#endif
PyMODINIT_FUNC
#if PY_MAJOR_VERSION >= 3
PyInit_extended(void)
#else
init_extended(void)
#endif
{
PyObject *module;
#if PY_MAJOR_VERSION >= 3
module = PyModule_Create(&moduledef);
#else
module = Py_InitModule3("extended", module_methods, NULL);
#endif
if (module == NULL)
#if PY_MAJOR_VERSION >= 3
return NULL;
#else
return;
#endif
#if PY_MAJOR_VERSION >= 3
return module;
#endif
}
......@@ -7,6 +7,7 @@ import shutil
import sys
import tarfile
import zipfile
import tempfile
from poetry import __version__
from poetry.io import NullIO
......@@ -83,6 +84,67 @@ $""".format(
)
is not None
)
records = decode(zip.read("extended-0.1.dist-info/RECORD"))
assert re.search(r"\s+extended/extended.*\.(so|pyd)", records) is not None
finally:
zip.close()
@pytest.mark.skipif(
sys.platform == "win32" and sys.version_info <= (3, 4),
reason="Disable test on Windows for Python <=3.4",
)
def test_wheel_c_extension_src_layout():
module_path = fixtures_dir / "src_extended"
builder = CompleteBuilder(
Poetry.create(module_path), NullEnv(execute=True), NullIO()
)
builder.build()
sdist = fixtures_dir / "src_extended" / "dist" / "extended-0.1.tar.gz"
assert sdist.exists()
with tarfile.open(str(sdist), "r") as tar:
assert "extended-0.1/build.py" in tar.getnames()
assert "extended-0.1/src/extended/extended.c" in tar.getnames()
whl = list((module_path / "dist").glob("extended-0.1-cp*-cp*-*.whl"))[0]
assert whl.exists()
zip = zipfile.ZipFile(str(whl))
has_compiled_extension = False
for name in zip.namelist():
if name.startswith("extended/extended") and name.endswith((".so", ".pyd")):
has_compiled_extension = True
assert has_compiled_extension
try:
wheel_data = decode(zip.read("extended-0.1.dist-info/WHEEL"))
assert (
re.match(
"""(?m)^\
Wheel-Version: 1.0
Generator: poetry {}
Root-Is-Purelib: false
Tag: cp[23]\\d-cp[23]\\dmu?-.+
$""".format(
__version__
),
wheel_data,
)
is not None
)
records = decode(zip.read("extended-0.1.dist-info/RECORD"))
assert re.search(r"\s+extended/extended.*\.(so|pyd)", records) is not None
finally:
zip.close()
......@@ -164,6 +226,105 @@ My Package
zip.close()
def test_complete_no_vcs():
# Copy the complete fixtures dir to a temporary directory
module_path = fixtures_dir / "complete"
temporary_dir = Path(tempfile.mkdtemp()) / "complete"
shutil.copytree(module_path.as_posix(), temporary_dir.as_posix())
builder = CompleteBuilder(
Poetry.create(temporary_dir), NullEnv(execute=True), NullIO()
)
builder.build()
whl = temporary_dir / "dist" / "my_package-1.2.3-py3-none-any.whl"
assert whl.exists()
zip = zipfile.ZipFile(str(whl))
# Check the zipped file to be sure that included and excluded files are
# correctly taken account of without vcs
expected_name_list = [
"my_package/__init__.py",
"my_package/data1/test.json",
"my_package/sub_pkg1/__init__.py",
"my_package/sub_pkg2/__init__.py",
"my_package/sub_pkg2/data2/data.json",
"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",
"my_package-1.2.3.dist-info/METADATA",
"my_package-1.2.3.dist-info/RECORD",
]
assert sorted(zip.namelist()) == sorted(expected_name_list)
try:
entry_points = zip.read("my_package-1.2.3.dist-info/entry_points.txt")
assert (
decode(entry_points.decode())
== """\
[console_scripts]
extra-script=my_package.extra:main[time]
my-2nd-script=my_package:main2
my-script=my_package:main
"""
)
wheel_data = decode(zip.read("my_package-1.2.3.dist-info/WHEEL"))
assert (
wheel_data
== """\
Wheel-Version: 1.0
Generator: poetry {}
Root-Is-Purelib: true
Tag: py3-none-any
""".format(
__version__
)
)
wheel_data = decode(zip.read("my_package-1.2.3.dist-info/METADATA"))
assert (
wheel_data
== """\
Metadata-Version: 2.1
Name: my-package
Version: 1.2.3
Summary: Some description.
Home-page: https://poetry.eustace.io/
License: MIT
Keywords: packaging,dependency,poetry
Author: Sébastien Eustace
Author-email: sebastien@eustace.io
Requires-Python: >=3.6,<4.0
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Topic :: Software Development :: Build Tools
Classifier: Topic :: Software Development :: Libraries :: Python Modules
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); extra == "time"
Project-URL: Documentation, https://poetry.eustace.io/docs
Project-URL: Repository, https://github.com/sdispater/poetry
Description-Content-Type: text/x-rst
My Package
==========
"""
)
finally:
zip.close()
def test_module_src():
module_path = fixtures_dir / "source_file"
builder = CompleteBuilder(
......
......@@ -309,6 +309,21 @@ def test_with_c_extensions():
assert "extended-0.1/extended/extended.c" in tar.getnames()
def test_with_c_extensions_src_layout():
poetry = Poetry.create(project("src_extended"))
builder = SdistBuilder(poetry, NullEnv(), NullIO())
builder.build()
sdist = fixtures_dir / "src_extended" / "dist" / "extended-0.1.tar.gz"
assert sdist.exists()
with tarfile.open(str(sdist), "r") as tar:
assert "extended-0.1/build.py" in tar.getnames()
assert "extended-0.1/src/extended/extended.c" in tar.getnames()
def test_with_src_module_file():
poetry = Poetry.create(project("source_file"))
......@@ -423,14 +438,18 @@ def test_default_with_excluded_data(mocker):
# Patch git module to return specific excluded files
p = mocker.patch("poetry.vcs.git.Git.get_ignored_files")
p.return_value = [
str(
Path(__file__).parent
/ "fixtures"
/ "default_with_excluded_data"
/ "my_package"
/ "data"
/ "sub_data"
/ "data2.txt"
(
(
Path(__file__).parent
/ "fixtures"
/ "default_with_excluded_data"
/ "my_package"
/ "data"
/ "sub_data"
/ "data2.txt"
)
.relative_to(project("default_with_excluded_data"))
.as_posix()
)
]
poetry = Poetry.create(project("default_with_excluded_data"))
......@@ -471,7 +490,7 @@ def test_default_with_excluded_data(mocker):
assert "my-package-1.2.3/PKG-INFO" in names
def test_proper_python_requires_if_single_version_specified():
def test_proper_python_requires_if_two_digits_precision_version_specified():
poetry = Poetry.create(project("simple_version"))
builder = SdistBuilder(poetry, NullEnv(), NullIO())
......@@ -480,3 +499,14 @@ def test_proper_python_requires_if_single_version_specified():
parsed = p.parsestr(to_str(pkg_info))
assert parsed["Requires-Python"] == ">=3.6,<3.7"
def test_proper_python_requires_if_three_digits_precision_version_specified():
poetry = Poetry.create(project("single_python"))
builder = SdistBuilder(poetry, NullEnv(), NullIO())
pkg_info = builder.build_pkg_info()
p = Parser()
parsed = p.parsestr(to_str(pkg_info))
assert parsed["Requires-Python"] == "==2.7.15"
......@@ -8,7 +8,7 @@ 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 supported Python versions are ^3.6
error = """The current project must support the following Python versions: ^3.6
Because no versions of foo match !=1.0.0
and foo (1.0.0) requires Python <3.5, foo is forbidden.
......
import pytest
from poetry.packages.directory_dependency import DirectoryDependency
from poetry.utils._compat import Path
from poetry.utils.env import EnvCommandError
from poetry.utils.env import MockEnv as BaseMockEnv
from subprocess import CalledProcessError
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_must_exist():
with pytest.raises(ValueError):
DirectoryDependency("demo", DIST_PATH / "invalid")
......@@ -6,35 +6,11 @@ from poetry.utils._compat import Path
DIST_PATH = Path(__file__).parent.parent / "fixtures" / "distributions"
def test_file_dependency_wheel():
dependency = FileDependency(DIST_PATH / "demo-0.1.0-py2.py3-none-any.whl")
assert dependency.is_file()
assert dependency.name == "demo"
assert dependency.pretty_constraint == "0.1.0"
assert dependency.python_versions == "*"
meta = dependency.metadata
assert meta.requires_dist == ["pendulum (>=1.4.0.0,<2.0.0.0)"]
def test_file_dependency_sdist():
dependency = FileDependency(DIST_PATH / "demo-0.1.0.tar.gz")
assert dependency.is_file()
assert dependency.name == "demo"
assert dependency.pretty_constraint == "0.1.0"
assert dependency.python_versions == "*"
meta = dependency.metadata
assert meta.requires_dist == ["pendulum (>=1.4.0.0,<2.0.0.0)"]
def test_file_dependency_wrong_path():
with pytest.raises(ValueError):
FileDependency(DIST_PATH / "demo-0.2.0.tar.gz")
FileDependency("demo", DIST_PATH / "demo-0.2.0.tar.gz")
def test_file_dependency_dir():
with pytest.raises(ValueError):
FileDependency(DIST_PATH)
FileDependency("demo", DIST_PATH)
......@@ -152,3 +152,20 @@ def test_dependency_from_pep_508_with_python_version_union_of_multi():
'python_version >= "2.7" and python_version < "2.8" '
'or python_version >= "3.4" and python_version < "3.5"'
)
def test_dependency_from_pep_508_with_not_in_op_marker():
name = (
"jinja2 (>=2.7,<2.8)"
'; python_version not in "3.0,3.1,3.2" and extra == "export"'
)
dep = dependency_from_pep_508(name)
assert dep.name == "jinja2"
assert str(dep.constraint) == ">=2.7,<2.8"
assert dep.in_extras == ["export"]
assert dep.python_versions == "!=3.0.*, !=3.1.*, !=3.2.*"
assert (
str(dep.marker) == 'python_version not in "3.0,3.1,3.2" and extra == "export"'
)
import pytest
from poetry.io import NullIO
from poetry.packages import ProjectPackage
from poetry.packages.directory_dependency import DirectoryDependency
from poetry.packages.file_dependency import FileDependency
from poetry.packages.vcs_dependency import VCSDependency
from poetry.puzzle.provider import Provider
from poetry.repositories.pool import Pool
from poetry.repositories.repository import Repository
from poetry.utils._compat import PY35
from poetry.utils._compat import Path
from poetry.utils.env import EnvCommandError
from poetry.utils.env import MockEnv as BaseMockEnv
from tests.helpers import get_dependency
from subprocess import CalledProcessError
class MockEnv(BaseMockEnv):
def run(self, bin, *args):
raise EnvCommandError(CalledProcessError(1, "python", output=""))
@pytest.fixture
def root():
return ProjectPackage("root", "1.2.3")
@pytest.fixture
def repository():
return Repository()
@pytest.fixture
def pool(repository):
pool = Pool()
pool.add_repository(repository)
return pool
@pytest.fixture
def provider(root, pool):
return Provider(root, pool, NullIO())
def test_search_for_vcs_setup_egg_info(provider):
dependency = VCSDependency("demo", "git", "https://github.com/demo/demo.git")
package = provider.search_for_vcs(dependency)[0]
assert package.name == "demo"
assert package.version.text == "0.1.2"
assert package.requires == [get_dependency("pendulum", ">=1.4.4")]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
def test_search_for_vcs_setup_egg_info_with_extras(provider):
dependency = VCSDependency("demo", "git", "https://github.com/demo/demo.git")
dependency.extras.append("foo")
package = provider.search_for_vcs(dependency)[0]
assert package.name == "demo"
assert package.version.text == "0.1.2"
assert package.requires == [
get_dependency("pendulum", ">=1.4.4"),
get_dependency("cleo", optional=True),
]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4")
def test_search_for_vcs_read_setup(provider, mocker):
mocker.patch("poetry.utils.env.Env.get", return_value=MockEnv())
dependency = VCSDependency("demo", "git", "https://github.com/demo/demo.git")
package = provider.search_for_vcs(dependency)[0]
assert package.name == "demo"
assert package.version.text == "0.1.2"
assert package.requires == [get_dependency("pendulum", ">=1.4.4")]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4")
def test_search_for_vcs_read_setup_with_extras(provider, mocker):
mocker.patch("poetry.utils.env.Env.get", return_value=MockEnv())
dependency = VCSDependency("demo", "git", "https://github.com/demo/demo.git")
dependency.extras.append("foo")
package = provider.search_for_vcs(dependency)[0]
assert package.name == "demo"
assert package.version.text == "0.1.2"
assert package.requires == [
get_dependency("pendulum", ">=1.4.4"),
get_dependency("cleo", optional=True),
]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
def test_search_for_vcs_read_setup_raises_error_if_no_version(provider, mocker):
mocker.patch("poetry.utils.env.Env.get", return_value=MockEnv())
dependency = VCSDependency("demo", "git", "https://github.com/demo/no-version.git")
with pytest.raises(RuntimeError):
provider.search_for_vcs(dependency)
def test_search_for_directory_setup_egg_info(provider):
dependency = DirectoryDependency(
"demo",
Path(__file__).parent.parent
/ "fixtures"
/ "git"
/ "github.com"
/ "demo"
/ "demo",
)
package = provider.search_for_directory(dependency)[0]
assert package.name == "demo"
assert package.version.text == "0.1.2"
assert package.requires == [get_dependency("pendulum", ">=1.4.4")]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
def test_search_for_directory_setup_egg_info_with_extras(provider):
dependency = DirectoryDependency(
"demo",
Path(__file__).parent.parent
/ "fixtures"
/ "git"
/ "github.com"
/ "demo"
/ "demo",
)
dependency.extras.append("foo")
package = provider.search_for_directory(dependency)[0]
assert package.name == "demo"
assert package.version.text == "0.1.2"
assert package.requires == [
get_dependency("pendulum", ">=1.4.4"),
get_dependency("cleo", optional=True),
]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4")
def test_search_for_directory_setup_read_setup(provider, mocker):
mocker.patch("poetry.utils.env.Env.get", return_value=MockEnv())
dependency = DirectoryDependency(
"demo",
Path(__file__).parent.parent
/ "fixtures"
/ "git"
/ "github.com"
/ "demo"
/ "demo",
)
package = provider.search_for_directory(dependency)[0]
assert package.name == "demo"
assert package.version.text == "0.1.2"
assert package.requires == [get_dependency("pendulum", ">=1.4.4")]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4")
def test_search_for_directory_setup_read_setup_with_extras(provider, mocker):
mocker.patch("poetry.utils.env.Env.get", return_value=MockEnv())
dependency = DirectoryDependency(
"demo",
Path(__file__).parent.parent
/ "fixtures"
/ "git"
/ "github.com"
/ "demo"
/ "demo",
)
dependency.extras.append("foo")
package = provider.search_for_directory(dependency)[0]
assert package.name == "demo"
assert package.version.text == "0.1.2"
assert package.requires == [
get_dependency("pendulum", ">=1.4.4"),
get_dependency("cleo", optional=True),
]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
def test_search_for_directory_poetry(provider):
dependency = DirectoryDependency(
"demo", Path(__file__).parent.parent / "fixtures" / "project_with_extras"
)
package = provider.search_for_directory(dependency)[0]
assert package.name == "project-with-extras"
assert package.version.text == "1.2.3"
assert package.requires == []
assert package.extras == {
"extras_a": [get_dependency("pendulum", ">=1.4.4")],
"extras_b": [get_dependency("cachy", ">=0.2.0")],
}
def test_search_for_directory_poetry_with_extras(provider):
dependency = DirectoryDependency(
"demo", Path(__file__).parent.parent / "fixtures" / "project_with_extras"
)
dependency.extras.append("extras_a")
package = provider.search_for_directory(dependency)[0]
assert package.name == "project-with-extras"
assert package.version.text == "1.2.3"
assert package.requires == [get_dependency("pendulum", ">=1.4.4")]
assert package.extras == {
"extras_a": [get_dependency("pendulum", ">=1.4.4")],
"extras_b": [get_dependency("cachy", ">=0.2.0")],
}
def test_search_for_file_sdist(provider):
dependency = FileDependency(
"demo",
Path(__file__).parent.parent
/ "fixtures"
/ "distributions"
/ "demo-0.1.0.tar.gz",
)
package = provider.search_for_file(dependency)[0]
assert package.name == "demo"
assert package.version.text == "0.1.0"
assert package.requires == [get_dependency("pendulum", ">=1.4.4")]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
def test_search_for_file_sdist_with_extras(provider):
dependency = FileDependency(
"demo",
Path(__file__).parent.parent
/ "fixtures"
/ "distributions"
/ "demo-0.1.0.tar.gz",
)
dependency.extras.append("foo")
package = provider.search_for_file(dependency)[0]
assert package.name == "demo"
assert package.version.text == "0.1.0"
assert package.requires == [
get_dependency("pendulum", ">=1.4.4"),
get_dependency("cleo", optional=True),
]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
def test_search_for_file_wheel(provider):
dependency = FileDependency(
"demo",
Path(__file__).parent.parent
/ "fixtures"
/ "distributions"
/ "demo-0.1.0-py2.py3-none-any.whl",
)
package = provider.search_for_file(dependency)[0]
assert package.name == "demo"
assert package.version.text == "0.1.0"
assert package.requires == [get_dependency("pendulum", ">=1.4.4")]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
def test_search_for_file_wheel_with_extras(provider):
dependency = FileDependency(
"demo",
Path(__file__).parent.parent
/ "fixtures"
/ "distributions"
/ "demo-0.1.0-py2.py3-none-any.whl",
)
dependency.extras.append("foo")
package = provider.search_for_file(dependency)[0]
assert package.name == "demo"
assert package.version.text == "0.1.0"
assert package.requires == [
get_dependency("pendulum", ">=1.4.4"),
get_dependency("cleo", optional=True),
]
assert package.extras == {
"foo": [get_dependency("cleo")],
"bar": [get_dependency("tomlkit")],
}
......@@ -10,6 +10,7 @@ from poetry.repositories.pool import Pool
from poetry.repositories.repository import Repository
from poetry.puzzle import Solver
from poetry.puzzle.exceptions import SolverProblemError
from poetry.utils._compat import Path
from poetry.version.markers import parse_marker
from tests.helpers import get_dependency
......@@ -913,7 +914,6 @@ def test_solver_can_resolve_git_dependencies(solver, repo, package):
ops,
[
{"job": "install", "package": pendulum},
{"job": "install", "package": cleo},
{"job": "install", "package": get_package("demo", "0.1.2")},
],
)
......@@ -1192,3 +1192,299 @@ def test_solver_should_not_resolve_prerelease_version_if_not_requested(
with pytest.raises(SolverProblemError):
solver.solve()
def test_solver_ignores_dependencies_with_incompatible_python_full_version_marker(
solver, repo, package
):
package.python_versions = "^3.6"
package.add_dependency("A", "^1.0")
package.add_dependency("B", "^2.0")
package_a = get_package("A", "1.0.0")
package_a.requires.append(
dependency_from_pep_508(
'B (<2.0); platform_python_implementation == "PyPy" and python_full_version < "2.7.9"'
)
)
package_b200 = get_package("B", "2.0.0")
package_b100 = get_package("B", "1.0.0")
repo.add_package(package_a)
repo.add_package(package_b100)
repo.add_package(package_b200)
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": package_a},
{"job": "install", "package": package_b200},
],
)
def test_solver_git_dependencies_update(solver, repo, package, installed):
pendulum = get_package("pendulum", "2.0.3")
cleo = get_package("cleo", "1.0.0")
repo.add_package(pendulum)
repo.add_package(cleo)
demo = get_package("demo", "0.1.2")
demo.source_type = "git"
demo.source_url = "https://github.com/demo/demo.git"
demo.source_reference = "123456"
installed.add_package(demo)
package.add_dependency("demo", {"git": "https://github.com/demo/demo.git"})
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": pendulum},
{
"job": "update",
"from": get_package("demo", "0.1.2"),
"to": get_package("demo", "0.1.2"),
},
],
)
op = ops[1]
assert op.job_type == "update"
assert op.package.source_type == "git"
assert op.package.source_reference.startswith("9cf87a2")
assert op.initial_package.source_reference == "123456"
def test_solver_git_dependencies_update_skipped(solver, repo, package, installed):
pendulum = get_package("pendulum", "2.0.3")
cleo = get_package("cleo", "1.0.0")
repo.add_package(pendulum)
repo.add_package(cleo)
demo = get_package("demo", "0.1.2")
demo.source_type = "git"
demo.source_url = "https://github.com/demo/demo.git"
demo.source_reference = "9cf87a285a2d3fbb0b9fa621997b3acc3631ed24"
installed.add_package(demo)
package.add_dependency("demo", {"git": "https://github.com/demo/demo.git"})
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": pendulum},
{
"job": "install",
"package": get_package("demo", "0.1.2"),
"skipped": True,
},
],
)
def test_solver_can_resolve_directory_dependencies(solver, repo, package):
pendulum = get_package("pendulum", "2.0.3")
repo.add_package(pendulum)
path = (
Path(__file__).parent.parent
/ "fixtures"
/ "git"
/ "github.com"
/ "demo"
/ "demo"
).as_posix()
package.add_dependency("demo", {"path": path})
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": pendulum},
{"job": "install", "package": get_package("demo", "0.1.2")},
],
)
op = ops[1]
assert op.package.name == "demo"
assert op.package.version.text == "0.1.2"
assert op.package.source_type == "directory"
assert op.package.source_url == path
def test_solver_can_resolve_directory_dependencies_with_extras(solver, repo, package):
pendulum = get_package("pendulum", "2.0.3")
cleo = get_package("cleo", "1.0.0")
repo.add_package(pendulum)
repo.add_package(cleo)
path = (
Path(__file__).parent.parent
/ "fixtures"
/ "git"
/ "github.com"
/ "demo"
/ "demo"
).as_posix()
package.add_dependency("demo", {"path": path, "extras": ["foo"]})
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": cleo},
{"job": "install", "package": pendulum},
{"job": "install", "package": get_package("demo", "0.1.2")},
],
)
op = ops[2]
assert op.package.name == "demo"
assert op.package.version.text == "0.1.2"
assert op.package.source_type == "directory"
assert op.package.source_url == path
def test_solver_can_resolve_sdist_dependencies(solver, repo, package):
pendulum = get_package("pendulum", "2.0.3")
repo.add_package(pendulum)
path = (
Path(__file__).parent.parent
/ "fixtures"
/ "distributions"
/ "demo-0.1.0.tar.gz"
).as_posix()
package.add_dependency("demo", {"path": path})
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": pendulum},
{"job": "install", "package": get_package("demo", "0.1.0")},
],
)
op = ops[1]
assert op.package.name == "demo"
assert op.package.version.text == "0.1.0"
assert op.package.source_type == "file"
assert op.package.source_url == path
def test_solver_can_resolve_sdist_dependencies_with_extras(solver, repo, package):
pendulum = get_package("pendulum", "2.0.3")
cleo = get_package("cleo", "1.0.0")
repo.add_package(pendulum)
repo.add_package(cleo)
path = (
Path(__file__).parent.parent
/ "fixtures"
/ "distributions"
/ "demo-0.1.0.tar.gz"
).as_posix()
package.add_dependency("demo", {"path": path, "extras": ["foo"]})
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": cleo},
{"job": "install", "package": pendulum},
{"job": "install", "package": get_package("demo", "0.1.0")},
],
)
op = ops[2]
assert op.package.name == "demo"
assert op.package.version.text == "0.1.0"
assert op.package.source_type == "file"
assert op.package.source_url == path
def test_solver_can_resolve_wheel_dependencies(solver, repo, package):
pendulum = get_package("pendulum", "2.0.3")
repo.add_package(pendulum)
path = (
Path(__file__).parent.parent
/ "fixtures"
/ "distributions"
/ "demo-0.1.0-py2.py3-none-any.whl"
).as_posix()
package.add_dependency("demo", {"path": path})
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": pendulum},
{"job": "install", "package": get_package("demo", "0.1.0")},
],
)
op = ops[1]
assert op.package.name == "demo"
assert op.package.version.text == "0.1.0"
assert op.package.source_type == "file"
assert op.package.source_url == path
def test_solver_can_resolve_wheel_dependencies_with_extras(solver, repo, package):
pendulum = get_package("pendulum", "2.0.3")
cleo = get_package("cleo", "1.0.0")
repo.add_package(pendulum)
repo.add_package(cleo)
path = (
Path(__file__).parent.parent
/ "fixtures"
/ "distributions"
/ "demo-0.1.0-py2.py3-none-any.whl"
).as_posix()
package.add_dependency("demo", {"path": path, "extras": ["foo"]})
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": cleo},
{"job": "install", "package": pendulum},
{"job": "install", "package": get_package("demo", "0.1.0")},
],
)
op = ops[2]
assert op.package.name == "demo"
assert op.package.version.text == "0.1.0"
assert op.package.source_type == "file"
assert op.package.source_url == path
<!DOCTYPE html>
<html>
<head>
<title>Links for jupyter</title>
</head>
<body>
<h1>Links for jupyter</h1>
<a href="https://files.pythonhosted.org/packages/c9/a9/371d0b8fe37dd231cf4b2cff0a9f0f25e98f3a73c3771742444be27f2944/jupyter-1.0.0.tar.gz#sha256=d9dc4b3318f310e34c82951ea5d6683f67bed7def4b259fafbfe4f1beb1d8e5f">jupyter-1.0.0.tar.gz</a><br/>
</body>
</html>
<!--SERIAL 1673841-->
<!DOCTYPE html>
<html>
<head>
<title>Links for python-language-server</title>
</head>
<body>
<h1>Links for python-language-server</h1>
<a href="https://files.pythonhosted.org/packages/9f/1d/2817b5dc2dd77f897410a11c1c9e2a6d96b3273c53d4219dd9edab7882af/python-language-server-0.21.2.tar.gz#sha256=fa9162acb1402b807132d7288b7f521db2bd666d63505d8a4d9464d4b8488c52">python-language-server-0.21.2.tar.gz</a><br/>
</body>
</html>
<!--SERIAL 4245719-->
{
"info": {
"author": "Mike Bayer",
"author_email": "mike_mp@zzzcomputing.com",
"bugtrack_url": null,
"classifiers": [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
"Topic :: Database :: Front-Ends"
],
"description": "",
"description_content_type": "",
"docs_url": null,
"download_url": "",
"downloads": {
"last_day": -1,
"last_month": -1,
"last_week": -1
},
"home_page": "http://www.sqlalchemy.org",
"keywords": "",
"license": "MIT License",
"maintainer": "",
"maintainer_email": "",
"name": "SQLAlchemy",
"package_url": "https://pypi.org/project/SQLAlchemy/",
"platform": "",
"project_url": "https://pypi.org/project/SQLAlchemy/",
"project_urls": {
"Homepage": "http://www.sqlalchemy.org"
},
"release_url": "https://pypi.org/project/SQLAlchemy/1.2.12/",
"requires_dist": null,
"requires_python": "",
"summary": "Database Abstraction Library",
"version": "1.2.12"
},
"last_serial": 4289618,
"releases": {
"1.2.12": [
{
"comment_text": "",
"digests": {
"md5": "3baca105a1e49798d6bc99eb2738cb3b",
"sha256": "c5951d9ef1d5404ed04bae5a16b60a0779087378928f997a294d1229c6ca4d3e"
},
"downloads": -1,
"filename": "SQLAlchemy-1.2.12.tar.gz",
"has_sig": true,
"md5_digest": "3baca105a1e49798d6bc99eb2738cb3b",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 5634807,
"upload_time": "2018-09-19T18:14:55",
"url": "https://files.pythonhosted.org/packages/25/c9/b0552098cee325425a61efdf380c51b5c721e459081c85bbb860f501c091/SQLAlchemy-1.2.12.tar.gz"
}
]
},
"urls": [
{
"comment_text": "",
"digests": {
"md5": "3baca105a1e49798d6bc99eb2738cb3b",
"sha256": "c5951d9ef1d5404ed04bae5a16b60a0779087378928f997a294d1229c6ca4d3e"
},
"downloads": -1,
"filename": "SQLAlchemy-1.2.12.tar.gz",
"has_sig": true,
"md5_digest": "3baca105a1e49798d6bc99eb2738cb3b",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 5634807,
"upload_time": "2018-09-19T18:14:55",
"url": "https://files.pythonhosted.org/packages/25/c9/b0552098cee325425a61efdf380c51b5c721e459081c85bbb860f501c091/SQLAlchemy-1.2.12.tar.gz"
}
]
}
{
"info": {
"author": "Twisted Matrix Laboratories",
"author_email": "twisted-python@twistedmatrix.com",
"bugtrack_url": null,
"classifiers": [
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7"
],
"description": "description",
"description_content_type": "",
"docs_url": null,
"download_url": "",
"downloads": {
"last_day": -1,
"last_month": -1,
"last_week": -1
},
"home_page": "http://twistedmatrix.com/",
"keywords": "",
"license": "MIT",
"maintainer": "Glyph Lefkowitz",
"maintainer_email": "glyph@twistedmatrix.com",
"name": "Twisted",
"package_url": "https://pypi.org/project/Twisted/",
"platform": "",
"project_url": "https://pypi.org/project/Twisted/",
"project_urls": {
"Homepage": "http://twistedmatrix.com/"
},
"release_url": "https://pypi.org/project/Twisted/18.9.0/",
"requires_dist": null,
"requires_python": "",
"summary": "An asynchronous networking framework written in Python",
"version": "18.9.0"
},
"last_serial": 4376865,
"releases": {
"18.9.0": [
{
"comment_text": "",
"digests": {
"md5": "20fe2ec156e6e45b0b0d2ff06d9e828f",
"sha256": "294be2c6bf84ae776df2fc98e7af7d6537e1c5e60a46d33c3ce2a197677da395"
},
"downloads": -1,
"filename": "Twisted-18.9.0.tar.bz2",
"has_sig": false,
"md5_digest": "20fe2ec156e6e45b0b0d2ff06d9e828f",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 3088398,
"upload_time": "2018-10-15T09:11:22",
"url": "https://files.pythonhosted.org/packages/5d/0e/a72d85a55761c2c3ff1cb968143a2fd5f360220779ed90e0fadf4106d4f2/Twisted-18.9.0.tar.bz2"
}
]
},
"urls": [
{
"comment_text": "",
"digests": {
"md5": "20fe2ec156e6e45b0b0d2ff06d9e828f",
"sha256": "294be2c6bf84ae776df2fc98e7af7d6537e1c5e60a46d33c3ce2a197677da395"
},
"downloads": -1,
"filename": "Twisted-18.9.0.tar.bz2",
"has_sig": false,
"md5_digest": "20fe2ec156e6e45b0b0d2ff06d9e828f",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 3088398,
"upload_time": "2018-10-15T09:11:22",
"url": "https://files.pythonhosted.org/packages/5d/0e/a72d85a55761c2c3ff1cb968143a2fd5f360220779ed90e0fadf4106d4f2/Twisted-18.9.0.tar.bz2"
}
]
}
from poetry.repositories.installed_repository import InstalledRepository
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
"""
class MockEnv(BaseMockEnv):
def run(self, bin, *args):
if bin == "pip" and args[0] == "freeze":
return FREEZE_RESULTS
super(MockEnv, self).run(bin, *args)
def test_load():
repository = InstalledRepository.load(MockEnv())
assert len(repository.packages) == 3
cleo = repository.packages[0]
assert cleo.name == "cleo"
assert cleo.version.text == "0.6.8"
pendulum = repository.packages[1]
assert pendulum.name == "pendulum"
assert pendulum.version.text == "0.0.0"
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 pytest
import shutil
try:
import urllib.parse as urlparse
except ImportError:
import urlparse
from poetry.packages import Dependency
from poetry.repositories.exceptions import PackageNotFound
from poetry.repositories.legacy_repository import LegacyRepository
from poetry.repositories.legacy_repository import Page
from poetry.utils._compat import PY35
from poetry.utils._compat import Path
......@@ -23,6 +32,12 @@ class MockRepository(LegacyRepository):
with fixture.open() as f:
return Page(self._url + endpoint, f.read(), {})
def _download(self, url, dest):
filename = urlparse.urlparse(url).path.rsplit("/")[-1]
filepath = self.FIXTURES.parent / "pypi.org" / "dists" / filename
shutil.copyfile(str(filepath), dest)
def test_page_relative_links_path_are_correct():
repo = MockRepository()
......@@ -52,8 +67,64 @@ def test_sdist_format_support():
assert bz2_links[0].filename == "poetry-0.1.1.tar.bz2"
def test_missing_version(mocker):
def test_missing_version():
repo = MockRepository()
with pytest.raises(ValueError):
with pytest.raises(PackageNotFound):
repo._get_release_info("missing_version", "1.1.0")
def test_get_package_information_fallback_read_setup():
repo = MockRepository()
package = repo.package("jupyter", "1.0.0")
assert package.name == "jupyter"
assert package.version.text == "1.0.0"
assert (
package.description
== "Jupyter metapackage. Install all the Jupyter components in one go."
)
if PY35:
assert package.requires == [
Dependency("notebook", "*"),
Dependency("qtconsole", "*"),
Dependency("jupyter-console", "*"),
Dependency("nbconvert", "*"),
Dependency("ipykernel", "*"),
Dependency("ipywidgets", "*"),
]
def test_get_package_information_skips_dependencies_with_invalid_constraints():
repo = MockRepository()
package = repo.package("python-language-server", "0.21.2")
assert package.name == "python-language-server"
assert package.version.text == "0.21.2"
assert (
package.description == "Python Language Server for the Language Server Protocol"
)
assert sorted(package.requires, key=lambda r: r.name) == [
Dependency("configparser", "*"),
Dependency("future", ">=0.14.0"),
Dependency("futures", "*"),
Dependency("jedi", ">=0.12"),
Dependency("pluggy", "*"),
Dependency("python-jsonrpc-server", "*"),
]
all_extra = package.extras["all"]
# rope>-0.10.5 should be discarded
assert sorted(all_extra, key=lambda r: r.name) == [
Dependency("autopep8", "*"),
Dependency("mccabe", "*"),
Dependency("pycodestyle", "*"),
Dependency("pydocstyle", ">=2.0.0"),
Dependency("pyflakes", ">=1.6.0"),
Dependency("yapf", "*"),
]
import pytest
from poetry.repositories import Pool
from poetry.repositories import Repository
from poetry.repositories.exceptions import PackageNotFound
def test_pool_raises_package_not_found_when_no_package_is_found():
pool = Pool()
pool.add_repository(Repository())
with pytest.raises(PackageNotFound):
pool.package("foo", "1.0.0")
import json
import pytest
import shutil
from poetry.packages import Dependency
from poetry.repositories.pypi_repository import PyPiRepository
from poetry.utils._compat import PY35
from poetry.utils._compat import Path
......@@ -104,3 +107,62 @@ def test_fallback_inspects_sdist_first_if_no_matching_wheels_can_be_found():
dep = package.requires[0]
assert dep.name == "futures"
assert dep.python_versions == "~2.7"
@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4")
def test_fallback_can_read_setup_to_get_dependencies():
repo = MockRepository(fallback=True)
package = repo.package("sqlalchemy", "1.2.12")
assert package.name == "sqlalchemy"
assert len(package.requires) == 0
assert package.extras == {
"mssql_pymssql": [Dependency("pymssql", "*")],
"mssql_pyodbc": [Dependency("pyodbc", "*")],
"mysql": [Dependency("mysqlclient", "*")],
"oracle": [Dependency("cx_oracle", "*")],
"postgresql": [Dependency("psycopg2", "*")],
"postgresql_pg8000": [Dependency("pg8000", "*")],
"postgresql_psycopg2binary": [Dependency("psycopg2-binary", "*")],
"postgresql_psycopg2cffi": [Dependency("psycopg2cffi", "*")],
"pymysql": [Dependency("pymysql", "*")],
}
def test_pypi_repository_supports_reading_bz2_files():
repo = MockRepository(fallback=True)
package = repo.package("twisted", "18.9.0")
assert package.name == "twisted"
assert sorted(package.requires, key=lambda r: r.name) == [
Dependency("attrs", ">=17.4.0"),
Dependency("Automat", ">=0.3.0"),
Dependency("constantly", ">=15.1"),
Dependency("hyperlink", ">=17.1.1"),
Dependency("incremental", ">=16.10.1"),
Dependency("PyHamcrest", ">=1.9.0"),
Dependency("zope.interface", ">=4.4.2"),
]
expected_extras = {
"all_non_platform": [
Dependency("appdirs", ">=1.4.0"),
Dependency("cryptography", ">=1.5"),
Dependency("h2", ">=3.0,<4.0"),
Dependency("idna", ">=0.6,!=2.3"),
Dependency("priority", ">=1.1.0,<2.0"),
Dependency("pyasn1", "*"),
Dependency("pyopenssl", ">=16.0.0"),
Dependency("pyserial", ">=3.0"),
Dependency("service_identity", "*"),
Dependency("soappy", "*"),
]
}
for name, deps in expected_extras.items():
assert expected_extras[name] == sorted(
package.extras[name], key=lambda r: r.name
)
......@@ -64,25 +64,21 @@ def test_poetry():
assert demo.is_file()
assert not demo.is_vcs()
assert demo.name == "demo"
assert demo.pretty_constraint == "0.1.0"
assert demo.pretty_constraint == "*"
demo = dependencies["my-package"]
assert not demo.is_file()
assert demo.is_directory()
assert not demo.is_vcs()
assert demo.name == "my-package"
assert demo.pretty_constraint == "0.1.2"
assert demo.package.requires[0].name == "pendulum"
assert demo.package.requires[1].name == "cachy"
assert demo.package.requires[1].extras == ["msgpack"]
assert demo.pretty_constraint == "*"
simple_project = dependencies["simple-project"]
assert not simple_project.is_file()
assert simple_project.is_directory()
assert not simple_project.is_vcs()
assert simple_project.name == "simple-project"
assert simple_project.pretty_constraint == "1.2.3"
assert simple_project.package.requires == []
assert simple_project.pretty_constraint == "*"
assert "db" in package.extras
......
# Note: this requirements.txt file is used to specify what dependencies are
# needed to make the package run rather than for deployment of a tested set of
# packages. Thus, this should be the loosest set possible (only required
# packages, not optional ones, and with the widest range of versions that could
# be suitable)
jinja2
PyYAML
paramiko
cryptography
setuptools
from __future__ import print_function
import json
import os
import os.path
import re
import sys
import warnings
from collections import defaultdict
from distutils.command.build_scripts import build_scripts as BuildScripts
from distutils.command.sdist import sdist as SDist
try:
from setuptools import setup, find_packages
from setuptools.command.build_py import build_py as BuildPy
from setuptools.command.install_lib import install_lib as InstallLib
from setuptools.command.install_scripts import install_scripts as InstallScripts
except ImportError:
print(
"Ansible now needs setuptools in order to build. Install it using"
" your package manager (usually python-setuptools) or via pip (pip"
" install setuptools).",
file=sys.stderr,
)
sys.exit(1)
sys.path.insert(0, os.path.abspath("lib"))
from ansible.release import __version__, __author__
SYMLINK_CACHE = "SYMLINK_CACHE.json"
def _find_symlinks(topdir, extension=""):
"""Find symlinks that should be maintained
Maintained symlinks exist in the bin dir or are modules which have
aliases. Our heuristic is that they are a link in a certain path which
point to a file in the same directory.
"""
symlinks = defaultdict(list)
for base_path, dirs, files in os.walk(topdir):
for filename in files:
filepath = os.path.join(base_path, filename)
if os.path.islink(filepath) and filename.endswith(extension):
target = os.readlink(filepath)
if os.path.dirname(target) == "":
link = filepath[len(topdir) :]
if link.startswith("/"):
link = link[1:]
symlinks[os.path.basename(target)].append(link)
return symlinks
def _cache_symlinks(symlink_data):
with open(SYMLINK_CACHE, "w") as f:
json.dump(symlink_data, f)
def _maintain_symlinks(symlink_type, base_path):
"""Switch a real file into a symlink"""
try:
# Try the cache first because going from git checkout to sdist is the
# only time we know that we're going to cache correctly
with open(SYMLINK_CACHE, "r") as f:
symlink_data = json.load(f)
except (IOError, OSError) as e:
# IOError on py2, OSError on py3. Both have errno
if e.errno == 2:
# SYMLINKS_CACHE doesn't exist. Fallback to trying to create the
# cache now. Will work if we're running directly from a git
# checkout or from an sdist created earlier.
symlink_data = {
"script": _find_symlinks("bin"),
"library": _find_symlinks("lib", ".py"),
}
# Sanity check that something we know should be a symlink was
# found. We'll take that to mean that the current directory
# structure properly reflects symlinks in the git repo
if "ansible-playbook" in symlink_data["script"]["ansible"]:
_cache_symlinks(symlink_data)
else:
raise
else:
raise
symlinks = symlink_data[symlink_type]
for source in symlinks:
for dest in symlinks[source]:
dest_path = os.path.join(base_path, dest)
if not os.path.islink(dest_path):
try:
os.unlink(dest_path)
except OSError as e:
if e.errno == 2:
# File does not exist which is all we wanted
pass
os.symlink(source, dest_path)
class BuildPyCommand(BuildPy):
def run(self):
BuildPy.run(self)
_maintain_symlinks("library", self.build_lib)
class BuildScriptsCommand(BuildScripts):
def run(self):
BuildScripts.run(self)
_maintain_symlinks("script", self.build_dir)
class InstallLibCommand(InstallLib):
def run(self):
InstallLib.run(self)
_maintain_symlinks("library", self.install_dir)
class InstallScriptsCommand(InstallScripts):
def run(self):
InstallScripts.run(self)
_maintain_symlinks("script", self.install_dir)
class SDistCommand(SDist):
def run(self):
# have to generate the cache of symlinks for release as sdist is the
# only command that has access to symlinks from the git repo
symlinks = {
"script": _find_symlinks("bin"),
"library": _find_symlinks("lib", ".py"),
}
_cache_symlinks(symlinks)
SDist.run(self)
def read_file(file_name):
"""Read file and return its contents."""
with open(file_name, "r") as f:
return f.read()
def read_requirements(file_name):
"""Read requirements file as a list."""
reqs = read_file(file_name).splitlines()
if not reqs:
raise RuntimeError(
"Unable to read requirements from the %s file"
"That indicates this copy of the source code is incomplete." % file_name
)
return reqs
PYCRYPTO_DIST = "pycrypto"
def get_crypto_req():
"""Detect custom crypto from ANSIBLE_CRYPTO_BACKEND env var.
pycrypto or cryptography. We choose a default but allow the user to
override it. This translates into pip install of the sdist deciding what
package to install and also the runtime dependencies that pkg_resources
knows about.
"""
crypto_backend = os.environ.get("ANSIBLE_CRYPTO_BACKEND", "").strip()
if crypto_backend == PYCRYPTO_DIST:
# Attempt to set version requirements
return "%s >= 2.6" % PYCRYPTO_DIST
return crypto_backend or None
def substitute_crypto_to_req(req):
"""Replace crypto requirements if customized."""
crypto_backend = get_crypto_req()
if crypto_backend is None:
return req
def is_not_crypto(r):
CRYPTO_LIBS = PYCRYPTO_DIST, "cryptography"
return not any(r.lower().startswith(c) for c in CRYPTO_LIBS)
return [r for r in req if is_not_crypto(r)] + [crypto_backend]
def read_extras():
"""Specify any extra requirements for installation."""
extras = dict()
extra_requirements_dir = "packaging/requirements"
for extra_requirements_filename in os.listdir(extra_requirements_dir):
filename_match = re.search(
r"^requirements-(\w*).txt$", extra_requirements_filename
)
if not filename_match:
continue
extra_req_file_path = os.path.join(
extra_requirements_dir, extra_requirements_filename
)
try:
extras[filename_match.group(1)] = read_file(
extra_req_file_path
).splitlines()
except RuntimeError:
pass
return extras
def get_dynamic_setup_params():
"""Add dynamically calculated setup params to static ones."""
return {
# Retrieve the long description from the README
"long_description": read_file("README.rst"),
"install_requires": substitute_crypto_to_req(
read_requirements("requirements.txt")
),
"extras_require": read_extras(),
}
static_setup_params = dict(
# Use the distutils SDist so that symlinks are not expanded
# Use a custom Build for the same reason
cmdclass={
"build_py": BuildPyCommand,
"build_scripts": BuildScriptsCommand,
"install_lib": InstallLibCommand,
"install_scripts": InstallScriptsCommand,
"sdist": SDistCommand,
},
name="ansible",
version=__version__,
description="Radically simple IT automation",
author=__author__,
author_email="info@ansible.com",
url="https://ansible.com/",
project_urls={
"Bug Tracker": "https://github.com/ansible/ansible/issues",
"CI: Shippable": "https://app.shippable.com/github/ansible/ansible",
"Code of Conduct": "https://docs.ansible.com/ansible/latest/community/code_of_conduct.html",
"Documentation": "https://docs.ansible.com/ansible/",
"Mailing lists": "https://docs.ansible.com/ansible/latest/community/communication.html#mailing-list-information",
"Source Code": "https://github.com/ansible/ansible",
},
license="GPLv3+",
# Ansible will also make use of a system copy of python-six and
# python-selectors2 if installed but use a Bundled copy if it's not.
python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*",
package_dir={"": "lib"},
packages=find_packages("lib"),
package_data={
"": [
"module_utils/powershell/*.psm1",
"module_utils/powershell/*/*.psm1",
"modules/windows/*.ps1",
"modules/windows/*/*.ps1",
"galaxy/data/*/*.*",
"galaxy/data/*/*/.*",
"galaxy/data/*/*/*.*",
"galaxy/data/*/tests/inventory",
"config/base.yml",
"config/module_defaults.yml",
]
},
classifiers=[
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
"Intended Audience :: Developers",
"Intended Audience :: Information Technology",
"Intended Audience :: System Administrators",
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
"Natural Language :: English",
"Operating System :: POSIX",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Topic :: System :: Installation/Setup",
"Topic :: System :: Systems Administration",
"Topic :: Utilities",
],
scripts=[
"bin/ansible",
"bin/ansible-playbook",
"bin/ansible-pull",
"bin/ansible-doc",
"bin/ansible-galaxy",
"bin/ansible-console",
"bin/ansible-connection",
"bin/ansible-vault",
"bin/ansible-config",
"bin/ansible-inventory",
],
data_files=[],
# Installing as zip files would break due to references to __file__
zip_safe=False,
)
def main():
"""Invoke installation process using setuptools."""
setup_params = dict(static_setup_params, **get_dynamic_setup_params())
ignore_warning_regex = (
r"Unknown distribution option: '(project_urls|python_requires)'"
)
warnings.filterwarnings(
"ignore",
message=ignore_warning_regex,
category=UserWarning,
module="distutils.dist",
)
setup(**setup_params)
warnings.resetwarnings()
if __name__ == "__main__":
main()
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import io
import re
from collections import OrderedDict
from setuptools import setup
with io.open("README.rst", "rt", encoding="utf8") as f:
readme = f.read()
with io.open("flask/__init__.py", "rt", encoding="utf8") as f:
version = re.search(r"__version__ = \'(.*?)\'", f.read()).group(1)
setup(
name="Flask",
version=version,
url="https://www.palletsprojects.com/p/flask/",
project_urls=OrderedDict(
(
("Documentation", "http://flask.pocoo.org/docs/"),
("Code", "https://github.com/pallets/flask"),
("Issue tracker", "https://github.com/pallets/flask/issues"),
)
),
license="BSD",
author="Armin Ronacher",
author_email="armin.ronacher@active-4.com",
maintainer="Pallets team",
maintainer_email="contact@palletsprojects.com",
description="A simple framework for building complex web applications.",
long_description=readme,
packages=["flask", "flask.json"],
include_package_data=True,
zip_safe=False,
platforms="any",
python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*",
install_requires=[
"Werkzeug>=0.14",
"Jinja2>=2.10",
"itsdangerous>=0.24",
"click>=5.1",
],
extras_require={
"dotenv": ["python-dotenv"],
"dev": [
"pytest>=3",
"coverage",
"tox",
"sphinx",
"pallets-sphinx-themes",
"sphinxcontrib-log-cabinet",
],
"docs": ["sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet"],
},
classifiers=[
"Development Status :: 5 - Production/Stable",
"Environment :: Web Environment",
"Framework :: Flask",
"Intended Audience :: Developers",
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
"Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
"Topic :: Software Development :: Libraries :: Application Frameworks",
"Topic :: Software Development :: Libraries :: Python Modules",
],
entry_points={"console_scripts": ["flask = flask.cli:main"]},
)
# -*- coding: utf-8 -*-
from distutils.core import setup
packages = [
"pendulum",
"pendulum._extensions",
"pendulum.formatting",
"pendulum.locales",
"pendulum.locales.da",
"pendulum.locales.de",
"pendulum.locales.en",
"pendulum.locales.es",
"pendulum.locales.fa",
"pendulum.locales.fo",
"pendulum.locales.fr",
"pendulum.locales.ko",
"pendulum.locales.lt",
"pendulum.locales.pt_br",
"pendulum.locales.zh",
"pendulum.mixins",
"pendulum.parsing",
"pendulum.parsing.exceptions",
"pendulum.tz",
"pendulum.tz.data",
"pendulum.tz.zoneinfo",
"pendulum.utils",
]
package_data = {"": ["*"]}
install_requires = ["python-dateutil>=2.6,<3.0", "pytzdata>=2018.3"]
extras_require = {':python_version < "3.5"': ["typing>=3.6,<4.0"]}
setup_kwargs = {
"name": "pendulum",
"version": "2.0.4",
"description": "Python datetimes made easy",
"author": "Sébastien Eustace",
"author_email": "sebastien@eustace.io",
"url": "https://pendulum.eustace.io",
"packages": packages,
"package_data": package_data,
"install_requires": install_requires,
"extras_require": extras_require,
"python_requires": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*",
}
from build import *
build(setup_kwargs)
setup(**setup_kwargs)
NAME = "PyYAML"
VERSION = "3.13"
DESCRIPTION = "YAML parser and emitter for Python"
LONG_DESCRIPTION = """\
YAML is a data serialization format designed for human readability
and interaction with scripting languages. PyYAML is a YAML parser
and emitter for Python.
PyYAML features a complete YAML 1.1 parser, Unicode support, pickle
support, capable extension API, and sensible error messages. PyYAML
supports standard YAML tags and provides Python-specific tags that
allow to represent an arbitrary Python object.
PyYAML is applicable for a broad range of tasks from complex
configuration files to object serialization and persistance."""
AUTHOR = "Kirill Simonov"
AUTHOR_EMAIL = "xi@resolvent.net"
LICENSE = "MIT"
PLATFORMS = "Any"
URL = "http://pyyaml.org/wiki/PyYAML"
DOWNLOAD_URL = "http://pyyaml.org/download/pyyaml/%s-%s.tar.gz" % (NAME, VERSION)
CLASSIFIERS = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Text Processing :: Markup",
]
LIBYAML_CHECK = """
#include <yaml.h>
int main(void) {
yaml_parser_t parser;
yaml_emitter_t emitter;
yaml_parser_initialize(&parser);
yaml_parser_delete(&parser);
yaml_emitter_initialize(&emitter);
yaml_emitter_delete(&emitter);
return 0;
}
"""
import sys, os.path, platform
from distutils import log
from distutils.core import setup, Command
from distutils.core import Distribution as _Distribution
from distutils.core import Extension as _Extension
from distutils.dir_util import mkpath
from distutils.command.build_ext import build_ext as _build_ext
from distutils.command.bdist_rpm import bdist_rpm as _bdist_rpm
from distutils.errors import (
DistutilsError,
CompileError,
LinkError,
DistutilsPlatformError,
)
if "setuptools.extension" in sys.modules:
_Extension = sys.modules["setuptools.extension"]._Extension
sys.modules["distutils.core"].Extension = _Extension
sys.modules["distutils.extension"].Extension = _Extension
sys.modules["distutils.command.build_ext"].Extension = _Extension
with_cython = False
try:
from Cython.Distutils.extension import Extension as _Extension
from Cython.Distutils import build_ext as _build_ext
with_cython = True
except ImportError:
pass
try:
from wheel.bdist_wheel import bdist_wheel
except ImportError:
bdist_wheel = None
class Distribution(_Distribution):
def __init__(self, attrs=None):
_Distribution.__init__(self, attrs)
if not self.ext_modules:
return
for idx in range(len(self.ext_modules) - 1, -1, -1):
ext = self.ext_modules[idx]
if not isinstance(ext, Extension):
continue
setattr(self, ext.attr_name, None)
self.global_options = [
(
ext.option_name,
None,
"include %s (default if %s is available)"
% (ext.feature_description, ext.feature_name),
),
(ext.neg_option_name, None, "exclude %s" % ext.feature_description),
] + self.global_options
self.negative_opt = self.negative_opt.copy()
self.negative_opt[ext.neg_option_name] = ext.option_name
def has_ext_modules(self):
if not self.ext_modules:
return False
for ext in self.ext_modules:
with_ext = self.ext_status(ext)
if with_ext is None or with_ext:
return True
return False
def ext_status(self, ext):
implementation = platform.python_implementation()
if implementation != "CPython":
return False
if isinstance(ext, Extension):
with_ext = getattr(self, ext.attr_name)
return with_ext
else:
return True
class Extension(_Extension):
def __init__(
self, name, sources, feature_name, feature_description, feature_check, **kwds
):
if not with_cython:
for filename in sources[:]:
base, ext = os.path.splitext(filename)
if ext == ".pyx":
sources.remove(filename)
sources.append("%s.c" % base)
_Extension.__init__(self, name, sources, **kwds)
self.feature_name = feature_name
self.feature_description = feature_description
self.feature_check = feature_check
self.attr_name = "with_" + feature_name.replace("-", "_")
self.option_name = "with-" + feature_name
self.neg_option_name = "without-" + feature_name
class build_ext(_build_ext):
def run(self):
optional = True
disabled = True
for ext in self.extensions:
with_ext = self.distribution.ext_status(ext)
if with_ext is None:
disabled = False
elif with_ext:
optional = False
disabled = False
break
if disabled:
return
try:
_build_ext.run(self)
except DistutilsPlatformError:
exc = sys.exc_info()[1]
if optional:
log.warn(str(exc))
log.warn("skipping build_ext")
else:
raise
def get_source_files(self):
self.check_extensions_list(self.extensions)
filenames = []
for ext in self.extensions:
if with_cython:
self.cython_sources(ext.sources, ext)
for filename in ext.sources:
filenames.append(filename)
base = os.path.splitext(filename)[0]
for ext in ["c", "h", "pyx", "pxd"]:
filename = "%s.%s" % (base, ext)
if filename not in filenames and os.path.isfile(filename):
filenames.append(filename)
return filenames
def get_outputs(self):
self.check_extensions_list(self.extensions)
outputs = []
for ext in self.extensions:
fullname = self.get_ext_fullname(ext.name)
filename = os.path.join(self.build_lib, self.get_ext_filename(fullname))
if os.path.isfile(filename):
outputs.append(filename)
return outputs
def build_extensions(self):
self.check_extensions_list(self.extensions)
for ext in self.extensions:
with_ext = self.distribution.ext_status(ext)
if with_ext is None:
with_ext = self.check_extension_availability(ext)
if not with_ext:
continue
if with_cython:
ext.sources = self.cython_sources(ext.sources, ext)
self.build_extension(ext)
def check_extension_availability(self, ext):
cache = os.path.join(self.build_temp, "check_%s.out" % ext.feature_name)
if not self.force and os.path.isfile(cache):
data = open(cache).read().strip()
if data == "1":
return True
elif data == "0":
return False
mkpath(self.build_temp)
src = os.path.join(self.build_temp, "check_%s.c" % ext.feature_name)
open(src, "w").write(ext.feature_check)
log.info("checking if %s is compilable" % ext.feature_name)
try:
[obj] = self.compiler.compile(
[src],
macros=ext.define_macros + [(undef,) for undef in ext.undef_macros],
include_dirs=ext.include_dirs,
extra_postargs=(ext.extra_compile_args or []),
depends=ext.depends,
)
except CompileError:
log.warn("")
log.warn(
"%s is not found or a compiler error: forcing --%s"
% (ext.feature_name, ext.neg_option_name)
)
log.warn(
"(if %s is installed correctly, you may need to" % ext.feature_name
)
log.warn(" specify the option --include-dirs or uncomment and")
log.warn(" modify the parameter include_dirs in setup.cfg)")
open(cache, "w").write("0\n")
return False
prog = "check_%s" % ext.feature_name
log.info("checking if %s is linkable" % ext.feature_name)
try:
self.compiler.link_executable(
[obj],
prog,
output_dir=self.build_temp,
libraries=ext.libraries,
library_dirs=ext.library_dirs,
runtime_library_dirs=ext.runtime_library_dirs,
extra_postargs=(ext.extra_link_args or []),
)
except LinkError:
log.warn("")
log.warn(
"%s is not found or a linker error: forcing --%s"
% (ext.feature_name, ext.neg_option_name)
)
log.warn(
"(if %s is installed correctly, you may need to" % ext.feature_name
)
log.warn(" specify the option --library-dirs or uncomment and")
log.warn(" modify the parameter library_dirs in setup.cfg)")
open(cache, "w").write("0\n")
return False
open(cache, "w").write("1\n")
return True
class bdist_rpm(_bdist_rpm):
def _make_spec_file(self):
argv0 = sys.argv[0]
features = []
for ext in self.distribution.ext_modules:
if not isinstance(ext, Extension):
continue
with_ext = getattr(self.distribution, ext.attr_name)
if with_ext is None:
continue
if with_ext:
features.append("--" + ext.option_name)
else:
features.append("--" + ext.neg_option_name)
sys.argv[0] = " ".join([argv0] + features)
spec_file = _bdist_rpm._make_spec_file(self)
sys.argv[0] = argv0
return spec_file
class test(Command):
user_options = []
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
build_cmd = self.get_finalized_command("build")
build_cmd.run()
sys.path.insert(0, build_cmd.build_lib)
if sys.version_info[0] < 3:
sys.path.insert(0, "tests/lib")
else:
sys.path.insert(0, "tests/lib3")
import test_all
if not test_all.main([]):
raise DistutilsError("Tests failed")
cmdclass = {"build_ext": build_ext, "bdist_rpm": bdist_rpm, "test": test}
if bdist_wheel:
cmdclass["bdist_wheel"] = bdist_wheel
if __name__ == "__main__":
setup(
name=NAME,
version=VERSION,
description=DESCRIPTION,
long_description=LONG_DESCRIPTION,
author=AUTHOR,
author_email=AUTHOR_EMAIL,
license=LICENSE,
platforms=PLATFORMS,
url=URL,
download_url=DOWNLOAD_URL,
classifiers=CLASSIFIERS,
package_dir={"": {2: "lib", 3: "lib3"}[sys.version_info[0]]},
packages=["yaml"],
ext_modules=[
Extension(
"_yaml",
["ext/_yaml.pyx"],
"libyaml",
"LibYAML bindings",
LIBYAML_CHECK,
libraries=["yaml"],
)
],
distclass=Distribution,
cmdclass=cmdclass,
)
#!/usr/bin/env python
# Learn more: https://github.com/kennethreitz/setup.py
import os
import re
import sys
from codecs import open
from setuptools import setup
from setuptools.command.test import test as TestCommand
here = os.path.abspath(os.path.dirname(__file__))
class PyTest(TestCommand):
user_options = [("pytest-args=", "a", "Arguments to pass into py.test")]
def initialize_options(self):
TestCommand.initialize_options(self)
try:
from multiprocessing import cpu_count
self.pytest_args = ["-n", str(cpu_count()), "--boxed"]
except (ImportError, NotImplementedError):
self.pytest_args = ["-n", "1", "--boxed"]
def finalize_options(self):
TestCommand.finalize_options(self)
self.test_args = []
self.test_suite = True
def run_tests(self):
import pytest
errno = pytest.main(self.pytest_args)
sys.exit(errno)
# 'setup.py publish' shortcut.
if sys.argv[-1] == "publish":
os.system("python setup.py sdist bdist_wheel")
os.system("twine upload dist/*")
sys.exit()
packages = ["requests"]
requires = [
"chardet>=3.0.2,<3.1.0",
"idna>=2.5,<2.8",
"urllib3>=1.21.1,<1.25",
"certifi>=2017.4.17",
]
test_requirements = [
"pytest-httpbin==0.0.7",
"pytest-cov",
"pytest-mock",
"pytest-xdist",
"PySocks>=1.5.6, !=1.5.7",
"pytest>=2.8.0",
]
about = {}
with open(os.path.join(here, "requests", "__version__.py"), "r", "utf-8") as f:
exec(f.read(), about)
with open("README.md", "r", "utf-8") as f:
readme = f.read()
with open("HISTORY.md", "r", "utf-8") as f:
history = f.read()
setup(
name=about["__title__"],
version=about["__version__"],
description=about["__description__"],
long_description=readme,
long_description_content_type="text/markdown",
author=about["__author__"],
author_email=about["__author_email__"],
url=about["__url__"],
packages=packages,
package_data={"": ["LICENSE", "NOTICE"], "requests": ["*.pem"]},
package_dir={"requests": "requests"},
include_package_data=True,
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*",
install_requires=requires,
license=about["__license__"],
zip_safe=False,
classifiers=[
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"Natural Language :: English",
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
],
cmdclass={"test": PyTest},
tests_require=test_requirements,
extras_require={
"security": ["pyOpenSSL >= 0.14", "cryptography>=1.3.4", "idna>=2.0.0"],
"socks": ["PySocks>=1.5.6, !=1.5.7"],
'socks:sys_platform == "win32" and python_version == "2.7"': ["win_inet_pton"],
},
)
import os
import platform
import re
import sys
from distutils.command.build_ext import build_ext
from distutils.errors import CCompilerError
from distutils.errors import DistutilsExecError
from distutils.errors import DistutilsPlatformError
from setuptools import Distribution as _Distribution, Extension
from setuptools import setup
from setuptools import find_packages
from setuptools.command.test import test as TestCommand
cmdclass = {}
if sys.version_info < (2, 7):
raise Exception("SQLAlchemy requires Python 2.7 or higher.")
cpython = platform.python_implementation() == "CPython"
ext_modules = [
Extension(
"sqlalchemy.cprocessors", sources=["lib/sqlalchemy/cextension/processors.c"]
),
Extension(
"sqlalchemy.cresultproxy", sources=["lib/sqlalchemy/cextension/resultproxy.c"]
),
Extension("sqlalchemy.cutils", sources=["lib/sqlalchemy/cextension/utils.c"]),
]
ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError)
if sys.platform == "win32":
# 2.6's distutils.msvc9compiler can raise an IOError when failing to
# find the compiler
ext_errors += (IOError,)
class BuildFailed(Exception):
def __init__(self):
self.cause = sys.exc_info()[1] # work around py 2/3 different syntax
class ve_build_ext(build_ext):
# This class allows C extension building to fail.
def run(self):
try:
build_ext.run(self)
except DistutilsPlatformError:
raise BuildFailed()
def build_extension(self, ext):
try:
build_ext.build_extension(self, ext)
except ext_errors:
raise BuildFailed()
except ValueError:
# this can happen on Windows 64 bit, see Python issue 7511
if "'path'" in str(sys.exc_info()[1]): # works with both py 2/3
raise BuildFailed()
raise
cmdclass["build_ext"] = ve_build_ext
class Distribution(_Distribution):
def has_ext_modules(self):
# We want to always claim that we have ext_modules. This will be fine
# if we don't actually have them (such as on PyPy) because nothing
# will get built, however we don't want to provide an overally broad
# Wheel package when building a wheel without C support. This will
# ensure that Wheel knows to treat us as if the build output is
# platform specific.
return True
class PyTest(TestCommand):
# from http://pytest.org/latest/goodpractices.html\
# #integrating-with-setuptools-python-setup-py-test-pytest-runner
# TODO: prefer pytest-runner package at some point, however it was
# not working at the time of this comment.
user_options = [("pytest-args=", "a", "Arguments to pass to py.test")]
default_options = ["-n", "4", "-q", "--nomemory"]
def initialize_options(self):
TestCommand.initialize_options(self)
self.pytest_args = ""
def finalize_options(self):
TestCommand.finalize_options(self)
self.test_args = []
self.test_suite = True
def run_tests(self):
import shlex
# import here, cause outside the eggs aren't loaded
import pytest
errno = pytest.main(self.default_options + shlex.split(self.pytest_args))
sys.exit(errno)
cmdclass["test"] = PyTest
def status_msgs(*msgs):
print("*" * 75)
for msg in msgs:
print(msg)
print("*" * 75)
with open(
os.path.join(os.path.dirname(__file__), "lib", "sqlalchemy", "__init__.py")
) as v_file:
VERSION = re.compile(r".*__version__ = '(.*?)'", re.S).match(v_file.read()).group(1)
with open(os.path.join(os.path.dirname(__file__), "README.rst")) as r_file:
readme = r_file.read()
def run_setup(with_cext):
kwargs = {}
if with_cext:
kwargs["ext_modules"] = ext_modules
else:
kwargs["ext_modules"] = []
setup(
name="SQLAlchemy",
version=VERSION,
description="Database Abstraction Library",
author="Mike Bayer",
author_email="mike_mp@zzzcomputing.com",
url="http://www.sqlalchemy.org",
packages=find_packages("lib"),
package_dir={"": "lib"},
license="MIT License",
cmdclass=cmdclass,
tests_require=["pytest >= 2.5.2", "mock", "pytest-xdist"],
long_description=readme,
classifiers=[
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
"Topic :: Database :: Front-Ends",
"Operating System :: OS Independent",
],
distclass=Distribution,
extras_require={
"mysql": ["mysqlclient"],
"pymysql": ["pymysql"],
"postgresql": ["psycopg2"],
"postgresql_pg8000": ["pg8000"],
"postgresql_psycopg2cffi": ["psycopg2cffi"],
"oracle": ["cx_oracle"],
"mssql_pyodbc": ["pyodbc"],
"mssql_pymssql": ["pymssql"],
},
**kwargs
)
if not cpython:
run_setup(False)
status_msgs(
"WARNING: C extensions are not supported on "
+ "this Python platform, speedups are not enabled.",
"Plain-Python build succeeded.",
)
elif os.environ.get("DISABLE_SQLALCHEMY_CEXT"):
run_setup(False)
status_msgs(
"DISABLE_SQLALCHEMY_CEXT is set; " + "not attempting to build C extensions.",
"Plain-Python build succeeded.",
)
else:
try:
run_setup(True)
except BuildFailed as exc:
status_msgs(
exc.cause,
"WARNING: The C extension could not be compiled, "
+ "speedups are not enabled.",
"Failure information, if any, is above.",
"Retrying the build without the C extension now.",
)
run_setup(False)
status_msgs(
"WARNING: The C extension could not be compiled, "
+ "speedups are not enabled.",
"Plain-Python build succeeded.",
)
[metadata]
name = with-setup-cfg
version = 1.2.3
[options]
zip_safe = true
python_requires = >=2.6,!=3.0,!=3.1,!=3.2,!=3.3
setup_requires = setuptools>=36.2.2
install_requires =
six
tomlkit
[options.extras_require]
validation =
cerberus
tests =
pytest
pytest-xdist
pytest-cov
from setuptools import setup
setup()
import os
import pytest
from poetry.utils._compat import PY35
from poetry.utils.setup_reader import SetupReader
@pytest.fixture()
def setup():
def _setup(name):
return os.path.join(os.path.dirname(__file__), "fixtures", "setups", name)
return _setup
@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4")
def test_setup_reader_read_first_level_setup_call_with_direct_types(setup):
result = SetupReader.read_from_directory(setup("flask"))
expected_name = "Flask"
expected_version = None
expected_install_requires = [
"Werkzeug>=0.14",
"Jinja2>=2.10",
"itsdangerous>=0.24",
"click>=5.1",
]
expected_extras_require = {
"dotenv": ["python-dotenv"],
"dev": [
"pytest>=3",
"coverage",
"tox",
"sphinx",
"pallets-sphinx-themes",
"sphinxcontrib-log-cabinet",
],
"docs": ["sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet"],
}
expected_python_requires = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
assert expected_name == result["name"]
assert expected_version == result["version"]
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_read_first_level_setup_call_with_variables(setup):
result = SetupReader.read_from_directory(setup("requests"))
expected_name = None
expected_version = None
expected_install_requires = [
"chardet>=3.0.2,<3.1.0",
"idna>=2.5,<2.8",
"urllib3>=1.21.1,<1.25",
"certifi>=2017.4.17",
]
expected_extras_require = {
"security": ["pyOpenSSL >= 0.14", "cryptography>=1.3.4", "idna>=2.0.0"],
"socks": ["PySocks>=1.5.6, !=1.5.7"],
'socks:sys_platform == "win32" and python_version == "2.7"': ["win_inet_pton"],
}
expected_python_requires = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
assert expected_name == result["name"]
assert expected_version == result["version"]
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_read_sub_level_setup_call_with_direct_types(setup):
result = SetupReader.read_from_directory(setup("sqlalchemy"))
expected_name = "SQLAlchemy"
expected_version = None
expected_install_requires = []
expected_extras_require = {
"mysql": ["mysqlclient"],
"pymysql": ["pymysql"],
"postgresql": ["psycopg2"],
"postgresql_pg8000": ["pg8000"],
"postgresql_psycopg2cffi": ["psycopg2cffi"],
"oracle": ["cx_oracle"],
"mssql_pyodbc": ["pyodbc"],
"mssql_pymssql": ["pymssql"],
}
assert expected_name == result["name"]
assert expected_version == result["version"]
assert expected_install_requires == result["install_requires"]
assert expected_extras_require == result["extras_require"]
assert result["python_requires"] is None
def test_setup_reader_read_setup_cfg(setup):
result = SetupReader.read_from_directory(setup("with-setup-cfg"))
expected_name = "with-setup-cfg"
expected_version = "1.2.3"
expected_install_requires = ["six", "tomlkit"]
expected_extras_require = {
"validation": ["cerberus"],
"tests": ["pytest", "pytest-xdist", "pytest-cov"],
}
expected_python_requires = ">=2.6,!=3.0,!=3.1,!=3.2,!=3.3"
assert expected_name == result["name"]
assert expected_version == result["version"]
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_read_setup_kwargs(setup):
result = SetupReader.read_from_directory(setup("pendulum"))
expected_name = "pendulum"
expected_version = "2.0.4"
expected_install_requires = ["python-dateutil>=2.6,<3.0", "pytzdata>=2018.3"]
expected_extras_require = {':python_version < "3.5"': ["typing>=3.6,<4.0"]}
expected_python_requires = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
assert expected_name == result["name"]
assert expected_version == result["version"]
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_read_setup_call_in_main(setup):
result = SetupReader.read_from_directory(setup("pyyaml"))
expected_name = "PyYAML"
expected_version = "3.13"
expected_install_requires = []
expected_extras_require = {}
expected_python_requires = None
assert expected_name == result["name"]
assert expected_version == result["version"]
assert expected_install_requires == result["install_requires"]
assert expected_extras_require == result["extras_require"]
assert expected_python_requires == result["python_requires"]
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