Commit 4fa0dcee by Sébastien Eustace

Merge branch 'master' into develop

parents 1cc05e18 6065675d
...@@ -20,16 +20,17 @@ script: pytest -q tests/ ...@@ -20,16 +20,17 @@ script: pytest -q tests/
jobs: jobs:
include: include:
- python: '2.7' - python: "2.7"
- python: '3.5' - python: "3.4"
- python: '3.6' - python: "3.5"
- python: '3.7' - python: "3.6"
dist: xenial - python: "3.7"
sudo: true dist: xenial
- stage: linting
python: '3.6' - stage: linting
install: python: "3.6"
- pip install pre-commit install:
- pre-commit install-hooks - pip install pre-commit
script: - pre-commit install-hooks
- pre-commit run --all-files script:
- pre-commit run --all-files
...@@ -102,7 +102,7 @@ $ poetry run pytest tests/ ...@@ -102,7 +102,7 @@ $ poetry run pytest tests/
Poetry uses the [black](https://github.com/ambv/black) coding style and you must ensure that your Poetry uses the [black](https://github.com/ambv/black) coding style and you must ensure that your
code follows it. If not, the CI will fail and your Pull Request will not be merged. code follows it. If not, the CI will fail and your Pull Request will not be merged.
To make sure that you don't accidently commit code that does not follow the coding style, you can To make sure that you don't accidentally commit code that does not follow the coding style, you can
install a pre-commit hook that will check that everything is in order: install a pre-commit hook that will check that everything is in order:
```bash ```bash
...@@ -117,4 +117,3 @@ will not be merged. ...@@ -117,4 +117,3 @@ will not be merged.
* Fill in [the required template](https://github.com/sdispater/poetry/blob/master/.github/PULL_REQUEST_TEMPLATE.md) * Fill in [the required template](https://github.com/sdispater/poetry/blob/master/.github/PULL_REQUEST_TEMPLATE.md)
* Be sure that you pull request contains tests that cover the changed or added code. * Be sure that you pull request contains tests that cover the changed or added code.
* If you changes warrant a documentation change, the pull request must also update the documentation. * If you changes warrant a documentation change, the pull request must also update the documentation.
...@@ -177,7 +177,7 @@ that I don't like. ...@@ -177,7 +177,7 @@ that I don't like.
#### Dependency resolution #### Dependency resolution
The dependency resolution is erratic and will fail even is there is a solution. Let's take an example: The dependency resolution is erratic and will fail even if there is a solution. Let's take an example:
```bash ```bash
pipenv install oslo.utils==1.4.0 pipenv install oslo.utils==1.4.0
......
# Basic usage # Basic usage
For the basic usage introduction we will be installing `pendulum`, a datetime library. For the basic usage introduction we will be installing `pendulum`, a datetime library.
If you have not yet installed Poetry, refer to the [Introduction](/) chapter. If you have not yet installed Poetry, refer to the [Introduction](/docs/) chapter.
## Project setup ## Project setup
...@@ -21,7 +21,7 @@ poetry-demo ...@@ -21,7 +21,7 @@ poetry-demo
│ └── __init__.py │ └── __init__.py
└── tests └── tests
├── __init__.py ├── __init__.py
└── test_poetry_demo └── test_poetry_demo.py
``` ```
The `pyproject.toml` file is what is the most important here. This will orchestrate The `pyproject.toml` file is what is the most important here. This will orchestrate
...@@ -69,7 +69,7 @@ It will automatically find a suitable version constraint. ...@@ -69,7 +69,7 @@ It will automatically find a suitable version constraint.
In our example, we are requesting the `pendulum` package with the version constraint `^1.4`. In our example, we are requesting the `pendulum` package with the version constraint `^1.4`.
This means any version greater or equal to 1.4.0 and less than 2.0.0 (`>=1.4.0 <2.0.0`). This means any version greater or equal to 1.4.0 and less than 2.0.0 (`>=1.4.0 <2.0.0`).
Please read [versions](/versions/) for more in-depth information on versions, how versions relate to each other, and on version constraints. Please read [versions](/docs/versions/) for more in-depth information on versions, how versions relate to each other, and on version constraints.
!!!note !!!note
......
...@@ -281,7 +281,7 @@ poetry config [options] [setting-key] [setting-value1] ... [setting-valueN] ...@@ -281,7 +281,7 @@ poetry config [options] [setting-key] [setting-value1] ... [setting-valueN]
```` ````
`setting-key` is a configuration option name and `setting-value1` is a configuration value. `setting-key` is a configuration option name and `setting-value1` is a configuration value.
See [Configuration](/configuration/) for all available settings. See [Configuration](/docs/configuration/) for all available settings.
### Options ### Options
...@@ -313,10 +313,21 @@ poetry run my-script ...@@ -313,10 +313,21 @@ poetry run my-script
Note that this command has no option. Note that this command has no option.
## shell
The `shell` command spawns a shell,
according to the `$SHELL` environment variable,
within the virtual environment.
If one doesn't exist yet, it will be created.
```bash
poetry shell
```
## check ## check
The `check` command validate the structure of the `pyproject.toml` file The `check` command validate the structure of the `pyproject.toml` file
and returns a detailed report is there are any errors. and returns a detailed report if there are any errors.
```bash ```bash
poetry check poetry check
......
# Configuration # Configuration
Poetry can be configured via the `config` command ([see more about its usage here](/cli/#config)) Poetry can be configured via the `config` command ([see more about its usage here](/docs/cli/#config))
or directly in the `config.toml` file that will be automatically be created when you first run that command. or directly in the `config.toml` file that will be automatically be created when you first run that command.
This file can typically be found in one of the following directories: This file can typically be found in one of the following directories:
...@@ -10,18 +10,20 @@ This file can typically be found in one of the following directories: ...@@ -10,18 +10,20 @@ This file can typically be found in one of the following directories:
For Unix, we follow the XDG spec and support `$XDG_CONFIG_HOME`. For Unix, we follow the XDG spec and support `$XDG_CONFIG_HOME`.
That means, by default `~/.config/pypoetry` That means, by default `~/.config/pypoetry`
## Available settings ## Available settings
### `settings.virtualenvs.create`: boolean ### `settings.virtualenvs.create`: boolean
Create a new virtualenv if one doesn't already exist. Create a new virtualenv if one doesn't already exist.
Defaults to `true`. Defaults to `true`.
### `settings.virtualenvs.in-project`: boolean ### `settings.virtualenvs.in-project`: boolean
Create the virtualenv inside the project's root directory. Create the virtualenv inside the project's root directory.
Defaults to `false`. Defaults to `false`.
### `settings.virtualenvs.path`: string ### `settings.virtualenvs.path`: string
Directory where virtualenvs will be created. Directory where virtualenvs will be created.
Defaults to one of the following directories: Defaults to one of the following directories:
...@@ -29,5 +31,6 @@ Defaults to one of the following directories: ...@@ -29,5 +31,6 @@ Defaults to one of the following directories:
- Windows: `C:\Users\<username>\AppData\Local\pypoetry\Cache/virtualenvs` - Windows: `C:\Users\<username>\AppData\Local\pypoetry\Cache/virtualenvs`
- Unix: `~/.cache/pypoetry/virtualenvs` - Unix: `~/.cache/pypoetry/virtualenvs`
### `repository.<name>`: string ### `repositories.<name>`: string
Set a new alternative repository. See [Repositories](/repositories/) for more information.
Set a new alternative repository. See [Repositories](/docs/repositories/) for more information.
...@@ -14,7 +14,7 @@ While Poetry does not enforce any convention regarding package versioning, ...@@ -14,7 +14,7 @@ While Poetry does not enforce any convention regarding package versioning,
it **strongly** recommends to follow [semantic versioning](https://semver.org). it **strongly** recommends to follow [semantic versioning](https://semver.org).
This has many advantages for the end users and allows them to set appropriate This has many advantages for the end users and allows them to set appropriate
[version constraints](/versions/). [version constraints](/docs/versions/).
## Lock file ## Lock file
...@@ -54,7 +54,7 @@ poetry publish ...@@ -54,7 +54,7 @@ poetry publish
``` ```
This will package and publish the library to PyPI, at the condition that you are a registered user This will package and publish the library to PyPI, at the condition that you are a registered user
and you have [configured your credentials](/repositories/#adding-credentials) properly. and you have [configured your credentials](/docs/repositories/#adding-credentials) properly.
!!!note !!!note
...@@ -73,7 +73,7 @@ Sometimes, you may want to keep your library private but also being accessible t ...@@ -73,7 +73,7 @@ Sometimes, you may want to keep your library private but also being accessible t
In this case, you will need to use a private repository. In this case, you will need to use a private repository.
In order to publish to a private repository, you will need to add it to your In order to publish to a private repository, you will need to add it to your
global list of repositories. See [Adding a repository](/repositories/#adding-a-repository) global list of repositories. See [Adding a repository](/docs/repositories/#adding-a-repository)
for more information. for more information.
Once this is done, you can actually publish to it like so: Once this is done, you can actually publish to it like so:
......
...@@ -68,6 +68,25 @@ An URL to the documentation of the project. **Optional** ...@@ -68,6 +68,25 @@ An URL to the documentation of the project. **Optional**
A list of keywords (max: 5) that the package is related to. **Optional** A list of keywords (max: 5) that the package is related to. **Optional**
## classifiers
A list of PyPI [trove classifiers](https://pypi.org/classifiers/) that describe the project. **Optional**
```toml
[tool.poetry]
# ...
classifiers = [
"Topic :: Software Development :: Build Tools",
"Topic :: Software Development :: Libraries :: Python Modules"
]
```
!!!note
Note that Python classifiers are still automatically added for you and are determined by your `python` requirement.
The `license` property will also set the License classifier automatically.
## packages ## packages
A list of packages and modules to include in the final distribution. A list of packages and modules to include in the final distribution.
...@@ -207,7 +226,7 @@ poetry install -E mysql -E pgsql ...@@ -207,7 +226,7 @@ poetry install -E mysql -E pgsql
## `plugins` ## `plugins`
Poetry supports arbitrary plugins wich work similarly to Poetry supports arbitrary plugins which work similarly to
[setuptools entry points](http://setuptools.readthedocs.io/en/latest/setuptools.html). [setuptools entry points](http://setuptools.readthedocs.io/en/latest/setuptools.html).
To match the example in the setuptools documentation, you would use the following: To match the example in the setuptools documentation, you would use the following:
......
{%- if nav_item.url %} {%- if nav_item.url %}
<a class="{% if nav_item.active%}current{%endif%}" href="{{ nav_item.url }}">{{ nav_item.title }}</a> <a class="{% if nav_item.active%}current{%endif%}" href="{{ base_url }}/{{ nav_item.url }}">{{ nav_item.title }}</a>
{%- else %} {%- else %}
<span class="caption-text">{{ nav_item.title }}</span> <span class="caption-text">{{ nav_item.title }}</span>
{%- endif %} {%- endif %}
{%- if nav_item == page or nav_item.children %} {%- if nav_item == page or nav_item.children %}
<ul class="subnav"> <ul class="subnav">
{%- if nav_item == page %} {%- if nav_item == page %}
{% include 'toc.html' %} {% include 'toc.html' %}
{%- endif %} {%- endif %}
{%- if nav_item.children %} {%- if nav_item.children %}
{%- set navlevel = navlevel + 1%} {%- set navlevel = navlevel + 1%}
{%- for nav_item in nav_item.children %} {%- for nav_item in nav_item.children %}
<li class="{% if navlevel > 2 %}toctree-l{{ navlevel }}{% endif %}{% if nav_item.active%} current{%endif%}"> <li class="{% if navlevel > 2 %}toctree-l{{ navlevel }}{% endif %}{% if nav_item.active%} current{%endif%}">
{% include 'nav.html' %} {% include 'nav.html' %}
</li> </li>
{%- endfor %} {%- endfor %}
{%- set navlevel = navlevel - 1%} {%- set navlevel = navlevel - 1%}
{%- endif %} {%- endif %}
</ul> </ul>
{%- endif %} {%- endif %}
{% for toc_item in page.toc %} {% for toc_item in page.toc %}
<!--<li class="toctree-l{{ navlevel + 1 }}"><a href="{{ toc_item.url }}">{{ toc_item.title }}</a></li>--> <!--<li class="toctree-l{{ navlevel + 1 }}"><a href="{{ toc_item.url }}">{{ toc_item.title }}</a></li>-->
{% if toc_item.children %} {% if toc_item.children %}
<ul> <ul>
{% for toc_item in toc_item.children %} {% for toc_item in toc_item.children %}
<li><a class="toctree-l{{ navlevel + 2 }}" href="{{ toc_item.url }}">{{ toc_item.title }}</a></li> <li><a class="toctree-l{{ navlevel + 2 }}" href="{{ toc_item.url }}">{{ toc_item.title }}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
...@@ -275,13 +275,13 @@ class Installer: ...@@ -275,13 +275,13 @@ class Installer:
CURRENT_PYTHON_VERSION = sys.version_info[:2] CURRENT_PYTHON_VERSION = sys.version_info[:2]
METADATA_URL = "https://pypi.org/pypi/poetry/json" METADATA_URL = "https://pypi.org/pypi/poetry/json"
VERSION_REGEX = re.compile( VERSION_REGEX = re.compile(
"v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?" r"v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?"
"(" "("
"[._-]?" "[._-]?"
"(?:(stable|beta|b|RC|alpha|a|patch|pl|p)((?:[.-]?\d+)*)?)?" r"(?:(stable|beta|b|RC|alpha|a|patch|pl|p)((?:[.-]?\d+)*)?)?"
"([.-]?dev)?" "([.-]?dev)?"
")?" ")?"
"(?:\+[^\s]+)?" r"(?:\+[^\s]+)?"
) )
BASE_URL = "https://github.com/sdispater/poetry/releases/download/" BASE_URL = "https://github.com/sdispater/poetry/releases/download/"
......
...@@ -148,12 +148,12 @@ class Application(BaseApplication): ...@@ -148,12 +148,12 @@ class Application(BaseApplication):
formatter = o.get_formatter() formatter = o.get_formatter()
lines = [] lines = []
for line in re.split("\r?\n", str(e)): for line in re.split(r"\r?\n", str(e)):
for splitline in [ for splitline in [
line[x : x + (width - 4)] for x in range(0, len(line), width - 4) line[x : x + (width - 4)] for x in range(0, len(line), width - 4)
]: ]:
line_length = ( line_length = (
len(re.sub("\[[^m]*m", "", formatter.format(splitline))) + 4 len(re.sub(r"\[[^m]*m", "", formatter.format(splitline))) + 4
) )
lines.append((splitline, line_length)) lines.append((splitline, line_length))
......
...@@ -102,7 +102,7 @@ To remove a repository (repo is a short alias for repositories): ...@@ -102,7 +102,7 @@ To remove a repository (repo is a short alias for repositories):
# show the value if no value is provided # show the value if no value is provided
if not self.argument("value") and not self.option("unset"): if not self.argument("value") and not self.option("unset"):
m = re.match("^repos?(?:itories)?(?:\.(.+))?", self.argument("key")) m = re.match(r"^repos?(?:itories)?(?:\.(.+))?", self.argument("key"))
if m: if m:
if not m.group(1): if not m.group(1):
value = {} value = {}
...@@ -144,7 +144,7 @@ To remove a repository (repo is a short alias for repositories): ...@@ -144,7 +144,7 @@ To remove a repository (repo is a short alias for repositories):
) )
# handle repositories # handle repositories
m = re.match("^repos?(?:itories)?(?:\.(.+))?", self.argument("key")) m = re.match(r"^repos?(?:itories)?(?:\.(.+))?", self.argument("key"))
if m: if m:
if not m.group(1): if not m.group(1):
raise ValueError("You cannot remove the [repositories] section") raise ValueError("You cannot remove the [repositories] section")
...@@ -173,7 +173,7 @@ To remove a repository (repo is a short alias for repositories): ...@@ -173,7 +173,7 @@ To remove a repository (repo is a short alias for repositories):
) )
# handle auth # handle auth
m = re.match("^(http-basic)\.(.+)", self.argument("key")) m = re.match(r"^(http-basic)\.(.+)", self.argument("key"))
if m: if m:
if self.option("unset"): if self.option("unset"):
if not self._auth_config.setting( if not self._auth_config.setting(
...@@ -278,7 +278,7 @@ To remove a repository (repo is a short alias for repositories): ...@@ -278,7 +278,7 @@ To remove a repository (repo is a short alias for repositories):
if k is None: if k is None:
k = "" k = ""
k += re.sub("^config\.", "", key + ".") k += re.sub(r"^config\.", "", key + ".")
if setting and len(setting) > 1: if setting and len(setting) > 1:
setting = ".".join(setting.split(".")[1:]) setting = ".".join(setting.split(".")[1:])
......
...@@ -40,10 +40,10 @@ class PipInstaller(BaseInstaller): ...@@ -40,10 +40,10 @@ class PipInstaller(BaseInstaller):
if parsed.scheme == "http": if parsed.scheme == "http":
self._io.write_error( self._io.write_error(
" <warning>Installing from unsecure host: {}</warning>".format( " <warning>Installing from unsecure host: {}</warning>".format(
parsed.netloc parsed.hostname
) )
) )
args += ["--trusted-host", parsed.netloc] args += ["--trusted-host", parsed.hostname]
auth = get_http_basic_auth(package.source_reference) auth = get_http_basic_auth(package.source_reference)
if auth: if auth:
......
...@@ -15,7 +15,7 @@ from ..utils.module import Module ...@@ -15,7 +15,7 @@ from ..utils.module import Module
from ..utils.package_include import PackageInclude from ..utils.package_include import PackageInclude
AUTHOR_REGEX = re.compile("(?u)^(?P<name>[- .,\w\d'’\"()]+) <(?P<email>.+?)>$") AUTHOR_REGEX = re.compile(r"(?u)^(?P<name>[- .,\w\d'’\"()]+) <(?P<email>.+?)>$")
class Builder(object): class Builder(object):
......
...@@ -302,7 +302,7 @@ class SdistBuilder(Builder): ...@@ -302,7 +302,7 @@ class SdistBuilder(Builder):
): ):
main = [] main = []
extras = defaultdict(list) extras = defaultdict(list)
req_regex = re.compile("^(.+) \((.+)\)$") req_regex = re.compile(r"^(.+) \((.+)\)$")
for dependency in dependencies: for dependency in dependencies:
if dependency.is_optional(): if dependency.is_optional():
......
...@@ -184,8 +184,8 @@ class WheelBuilder(Builder): ...@@ -184,8 +184,8 @@ class WheelBuilder(Builder):
@property @property
def wheel_filename(self): # type: () -> str def wheel_filename(self): # type: () -> str
return "{}-{}-{}.whl".format( return "{}-{}-{}.whl".format(
re.sub("[^\w\d.]+", "_", self._package.pretty_name, flags=re.UNICODE), re.sub(r"[^\w\d.]+", "_", self._package.pretty_name, flags=re.UNICODE),
re.sub("[^\w\d.]+", "_", self._meta.version, flags=re.UNICODE), re.sub(r"[^\w\d.]+", "_", self._meta.version, flags=re.UNICODE),
self.tag, self.tag,
) )
...@@ -195,8 +195,8 @@ class WheelBuilder(Builder): ...@@ -195,8 +195,8 @@ class WheelBuilder(Builder):
) )
def dist_info_name(self, distribution, version): # type: (...) -> str def dist_info_name(self, distribution, version): # type: (...) -> str
escaped_name = re.sub("[^\w\d.]+", "_", distribution, flags=re.UNICODE) escaped_name = re.sub(r"[^\w\d.]+", "_", distribution, flags=re.UNICODE)
escaped_version = re.sub("[^\w\d.]+", "_", version, flags=re.UNICODE) escaped_version = re.sub(r"[^\w\d.]+", "_", version, flags=re.UNICODE)
return "{}-{}.dist-info".format(escaped_name, escaped_version) return "{}-{}.dist-info".format(escaped_name, escaped_version)
...@@ -301,7 +301,7 @@ class WheelBuilder(Builder): ...@@ -301,7 +301,7 @@ class WheelBuilder(Builder):
fp.write("Version: {}\n".format(self._meta.version)) fp.write("Version: {}\n".format(self._meta.version))
fp.write("Summary: {}\n".format(self._meta.summary)) fp.write("Summary: {}\n".format(self._meta.summary))
fp.write("Home-page: {}\n".format(self._meta.home_page or "UNKNOWN")) fp.write("Home-page: {}\n".format(self._meta.home_page or "UNKNOWN"))
fp.write("License: {}\n".format(self._meta.license or "UNKOWN")) fp.write("License: {}\n".format(self._meta.license or "UNKNOWN"))
# Optional fields # Optional fields
if self._meta.keywords: if self._meta.keywords:
......
...@@ -60,9 +60,9 @@ class Uploader: ...@@ -60,9 +60,9 @@ class Uploader:
dist.glob( dist.glob(
"{}-{}-*.whl".format( "{}-{}-*.whl".format(
re.sub( re.sub(
"[^\w\d.]+", "_", self._package.pretty_name, flags=re.UNICODE r"[^\w\d.]+", "_", self._package.pretty_name, flags=re.UNICODE
), ),
re.sub("[^\w\d.]+", "_", version, flags=re.UNICODE), re.sub(r"[^\w\d.]+", "_", version, flags=re.UNICODE),
) )
) )
) )
......
...@@ -64,7 +64,7 @@ def dependency_from_pep_508(name): ...@@ -64,7 +64,7 @@ def dependency_from_pep_508(name):
link = Link(path_to_url(os.path.normpath(os.path.abspath(link.path)))) link = Link(path_to_url(os.path.normpath(os.path.abspath(link.path))))
# wheel file # wheel file
if link.is_wheel: if link.is_wheel:
m = re.match("^(?P<namever>(?P<name>.+?)-(?P<ver>\d.*?))", link.filename) m = re.match(r"^(?P<namever>(?P<name>.+?)-(?P<ver>\d.*?))", link.filename)
if not m: if not m:
raise ValueError("Invalid wheel name: {}".format(link.filename)) raise ValueError("Invalid wheel name: {}".format(link.filename))
......
...@@ -6,7 +6,7 @@ from .constraint import Constraint ...@@ -6,7 +6,7 @@ from .constraint import Constraint
class WilcardConstraint(Constraint): class WilcardConstraint(Constraint):
def __init__(self, constraint): # type: (str) -> None def __init__(self, constraint): # type: (str) -> None
m = re.match( m = re.match(
"^(!= ?|==)?v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.[xX*])+$", constraint r"^(!= ?|==)?v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.[xX*])+$", constraint
) )
if not m: if not m:
raise ValueError("Invalid value for wildcard constraint") raise ValueError("Invalid value for wildcard constraint")
......
...@@ -164,7 +164,7 @@ class Locker: ...@@ -164,7 +164,7 @@ class Locker:
def _get_content_hash(self): # type: () -> str def _get_content_hash(self): # type: () -> str
""" """
Returns the sha256 hash of the sorted content of the composer file. Returns the sha256 hash of the sorted content of the pyproject file.
""" """
content = self._local_config content = self._local_config
......
...@@ -22,7 +22,7 @@ from .vcs_dependency import VCSDependency ...@@ -22,7 +22,7 @@ from .vcs_dependency import VCSDependency
from .utils.utils import convert_markers from .utils.utils import convert_markers
from .utils.utils import create_nested_marker from .utils.utils import create_nested_marker
AUTHOR_REGEX = re.compile("(?u)^(?P<name>[- .,\w\d'’\"()]+)(?: <(?P<email>.+?)>)?$") AUTHOR_REGEX = re.compile(r"(?u)^(?P<name>[- .,\w\d'’\"()]+)(?: <(?P<email>.+?)>)?$")
class Package(object): class Package(object):
......
...@@ -559,8 +559,8 @@ class Provider: ...@@ -559,8 +559,8 @@ class Provider:
if message.startswith("fact:"): if message.startswith("fact:"):
if "depends on" in message: if "depends on" in message:
m = re.match("fact: (.+?) depends on (.+?) \((.+?)\)", message) m = re.match(r"fact: (.+?) depends on (.+?) \((.+?)\)", message)
m2 = re.match("(.+?) \((.+?)\)", m.group(1)) m2 = re.match(r"(.+?) \((.+?)\)", m.group(1))
if m2: if m2:
name = m2.group(1) name = m2.group(1)
version = " (<comment>{}</comment>)".format(m2.group(2)) version = " (<comment>{}</comment>)".format(m2.group(2))
...@@ -582,19 +582,19 @@ class Provider: ...@@ -582,19 +582,19 @@ class Provider:
) )
else: else:
message = re.sub( message = re.sub(
"(?<=: )(.+?) \((.+?)\)", r"(?<=: )(.+?) \((.+?)\)",
"<info>\\1</info> (<comment>\\2</comment>)", "<info>\\1</info> (<comment>\\2</comment>)",
message, message,
) )
message = "<fg=blue>fact</>: {}".format(message.split("fact: ")[1]) message = "<fg=blue>fact</>: {}".format(message.split("fact: ")[1])
elif message.startswith("selecting "): elif message.startswith("selecting "):
message = re.sub( message = re.sub(
"selecting (.+?) \((.+?)\)", r"selecting (.+?) \((.+?)\)",
"<fg=blue>selecting</> <info>\\1</info> (<comment>\\2</comment>)", "<fg=blue>selecting</> <info>\\1</info> (<comment>\\2</comment>)",
message, message,
) )
elif message.startswith("derived:"): elif message.startswith("derived:"):
m = re.match("derived: (.+?) \((.+?)\)$", message) m = re.match(r"derived: (.+?) \((.+?)\)$", message)
if m: if m:
message = "<fg=blue>derived</>: <info>{}</info> (<comment>{}</comment>)".format( message = "<fg=blue>derived</>: <info>{}</info> (<comment>{}</comment>)".format(
m.group(1), m.group(2) m.group(1), m.group(2)
...@@ -604,9 +604,9 @@ class Provider: ...@@ -604,9 +604,9 @@ class Provider:
message.split("derived: ")[1] message.split("derived: ")[1]
) )
elif message.startswith("conflict:"): elif message.startswith("conflict:"):
m = re.match("conflict: (.+?) depends on (.+?) \((.+?)\)", message) m = re.match(r"conflict: (.+?) depends on (.+?) \((.+?)\)", message)
if m: if m:
m2 = re.match("(.+?) \((.+?)\)", m.group(1)) m2 = re.match(r"(.+?) \((.+?)\)", m.group(1))
if m2: if m2:
name = m2.group(1) name = m2.group(1)
version = " (<comment>{}</comment>)".format(m2.group(2)) version = " (<comment>{}</comment>)".format(m2.group(2))
......
...@@ -46,7 +46,7 @@ from .pypi_repository import PyPiRepository ...@@ -46,7 +46,7 @@ from .pypi_repository import PyPiRepository
class Page: class Page:
VERSION_REGEX = re.compile("(?i)([a-z0-9_\-.]+?)-(?=\d)([a-z0-9_.!+-]+)") VERSION_REGEX = re.compile(r"(?i)([a-z0-9_\-.]+?)-(?=\d)([a-z0-9_.!+-]+)")
SUPPORTED_FORMATS = [ SUPPORTED_FORMATS = [
".tar.gz", ".tar.gz",
".whl", ".whl",
...@@ -309,6 +309,12 @@ class LegacyRepository(PyPiRepository): ...@@ -309,6 +309,12 @@ class LegacyRepository(PyPiRepository):
} }
links = list(page.links_for_version(Version.parse(version))) links = list(page.links_for_version(Version.parse(version)))
if not links:
raise ValueError(
'No valid distribution links found for package: "{}" version: "{}"'.format(
name, version
)
)
urls = {} urls = {}
hashes = [] hashes = []
default_link = links[0] default_link = links[0]
......
...@@ -306,7 +306,7 @@ class PyPiRepository(Repository): ...@@ -306,7 +306,7 @@ class PyPiRepository(Repository):
# If bdist_wheel, check if it's universal # If bdist_wheel, check if it's universal
filename = url["filename"] filename = url["filename"]
if not re.search("-py2\.py3-none-any.whl", filename): if not re.search(r"-py2\.py3-none-any.whl", filename):
continue continue
urls[dist_type] = url["url"] urls[dist_type] = url["url"]
......
...@@ -16,7 +16,7 @@ def parse_constraint(constraints): # type: (str) -> VersionConstraint ...@@ -16,7 +16,7 @@ def parse_constraint(constraints): # type: (str) -> VersionConstraint
if constraints == "*": if constraints == "*":
return VersionRange() return VersionRange()
or_constraints = re.split("\s*\|\|?\s*", constraints.strip()) or_constraints = re.split(r"\s*\|\|?\s*", constraints.strip())
or_groups = [] or_groups = []
for constraints in or_constraints: for constraints in or_constraints:
and_constraints = re.split( and_constraints = re.split(
...@@ -46,7 +46,7 @@ def parse_constraint(constraints): # type: (str) -> VersionConstraint ...@@ -46,7 +46,7 @@ def parse_constraint(constraints): # type: (str) -> VersionConstraint
def parse_single_constraint(constraint): # type: (str) -> VersionConstraint def parse_single_constraint(constraint): # type: (str) -> VersionConstraint
m = re.match("(?i)^v?[xX*](\.[xX*])*$", constraint) m = re.match(r"(?i)^v?[xX*](\.[xX*])*$", constraint)
if m: if m:
return VersionRange() return VersionRange()
......
...@@ -2,20 +2,20 @@ import re ...@@ -2,20 +2,20 @@ import re
MODIFIERS = ( MODIFIERS = (
"[._-]?" "[._-]?"
"((?!post)(?:beta|b|c|pre|RC|alpha|a|patch|pl|p|dev)(?:(?:[.-]?\d+)*)?)?" r"((?!post)(?:beta|b|c|pre|RC|alpha|a|patch|pl|p|dev)(?:(?:[.-]?\d+)*)?)?"
"([+-]?([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?" r"([+-]?([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?"
) )
_COMPLETE_VERSION = "v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?{}(?:\+[^\s]+)?".format( _COMPLETE_VERSION = r"v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?{}(?:\+[^\s]+)?".format(
MODIFIERS MODIFIERS
) )
COMPLETE_VERSION = re.compile("(?i)" + _COMPLETE_VERSION) COMPLETE_VERSION = re.compile("(?i)" + _COMPLETE_VERSION)
CARET_CONSTRAINT = re.compile("(?i)^\^({})$".format(_COMPLETE_VERSION)) CARET_CONSTRAINT = re.compile(r"(?i)^\^({})$".format(_COMPLETE_VERSION))
TILDE_CONSTRAINT = re.compile("(?i)^~(?!=)({})$".format(_COMPLETE_VERSION)) TILDE_CONSTRAINT = re.compile("(?i)^~(?!=)({})$".format(_COMPLETE_VERSION))
TILDE_PEP440_CONSTRAINT = re.compile("(?i)^~=({})$".format(_COMPLETE_VERSION)) TILDE_PEP440_CONSTRAINT = re.compile("(?i)^~=({})$".format(_COMPLETE_VERSION))
X_CONSTRAINT = re.compile("^(!=|==)?\s*v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.[xX*])+$") X_CONSTRAINT = re.compile(r"^(!=|==)?\s*v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.[xX*])+$")
BASIC_CONSTRAINT = re.compile( BASIC_CONSTRAINT = re.compile(
"(?i)^(<>|!=|>=?|<=?|==?)?\s*({}|dev)".format(_COMPLETE_VERSION) r"(?i)^(<>|!=|>=?|<=?|==?)?\s*({}|dev)".format(_COMPLETE_VERSION)
) )
...@@ -280,7 +280,7 @@ class Version(VersionRange): ...@@ -280,7 +280,7 @@ class Version(VersionRange):
if not pre: if not pre:
return return
m = re.match("(?i)^(a|alpha|b|beta|c|pre|rc|dev)[-.]?(\d+)?$", pre) m = re.match(r"(?i)^(a|alpha|b|beta|c|pre|rc|dev)[-.]?(\d+)?$", pre)
if not m: if not m:
return return
......
...@@ -64,7 +64,7 @@ def user_cache_dir(appname): ...@@ -64,7 +64,7 @@ def user_cache_dir(appname):
def user_data_dir(appname, roaming=False): def user_data_dir(appname, roaming=False):
""" r"""
Return full path to the user-specific data dir for this application. Return full path to the user-specific data dir for this application.
"appname" is the name of application. "appname" is the name of application.
...@@ -84,8 +84,8 @@ def user_data_dir(appname, roaming=False): ...@@ -84,8 +84,8 @@ def user_data_dir(appname, roaming=False):
...Application Data\<AppName> ...Application Data\<AppName>
Win XP (roaming): C:\Documents and Settings\<username>\Local ... Win XP (roaming): C:\Documents and Settings\<username>\Local ...
...Settings\Application Data\<AppName> ...Settings\Application Data\<AppName>
Win 7 (not roaming): C:\\Users\<username>\AppData\Local\<AppName> Win 7 (not roaming): C:\Users\<username>\AppData\Local\<AppName>
Win 7 (roaming): C:\\Users\<username>\AppData\Roaming\<AppName> Win 7 (roaming): C:\Users\<username>\AppData\Roaming\<AppName>
For Unix, we follow the XDG spec and support $XDG_DATA_HOME. For Unix, we follow the XDG spec and support $XDG_DATA_HOME.
That means, by default "~/.local/share/<AppName>". That means, by default "~/.local/share/<AppName>".
...@@ -137,7 +137,7 @@ def user_config_dir(appname, roaming=True): ...@@ -137,7 +137,7 @@ def user_config_dir(appname, roaming=True):
# for the discussion regarding site_config_dirs locations # for the discussion regarding site_config_dirs locations
# see <https://github.com/pypa/pip/issues/1733> # see <https://github.com/pypa/pip/issues/1733>
def site_config_dirs(appname): def site_config_dirs(appname):
"""Return a list of potential user-shared config dirs for this application. r"""Return a list of potential user-shared config dirs for this application.
"appname" is the name of application. "appname" is the name of application.
......
...@@ -12,7 +12,7 @@ _Version = namedtuple("_Version", ["epoch", "release", "dev", "pre", "post", "lo ...@@ -12,7 +12,7 @@ _Version = namedtuple("_Version", ["epoch", "release", "dev", "pre", "post", "lo
VERSION_PATTERN = re.compile( VERSION_PATTERN = re.compile(
""" r"""
^ ^
v? v?
(?: (?:
......
...@@ -75,7 +75,7 @@ def test_wheel_c_extension(): ...@@ -75,7 +75,7 @@ def test_wheel_c_extension():
Wheel-Version: 1.0 Wheel-Version: 1.0
Generator: poetry {} Generator: poetry {}
Root-Is-Purelib: false Root-Is-Purelib: false
Tag: cp[23]\d-cp[23]\dmu?-.+ Tag: cp[23]\\d-cp[23]\\dmu?-.+
$""".format( $""".format(
__version__ __version__
), ),
......
...@@ -7,6 +7,7 @@ from poetry.masonry.builders import WheelBuilder ...@@ -7,6 +7,7 @@ from poetry.masonry.builders import WheelBuilder
from poetry.poetry import Poetry from poetry.poetry import Poetry
from poetry.utils._compat import Path from poetry.utils._compat import Path
from poetry.utils.env import NullEnv from poetry.utils.env import NullEnv
from poetry.packages import ProjectPackage
fixtures_dir = Path(__file__).parent / "fixtures" fixtures_dir = Path(__file__).parent / "fixtures"
...@@ -123,3 +124,23 @@ def test_package_with_include(mocker): ...@@ -123,3 +124,23 @@ def test_package_with_include(mocker):
assert "my_module.py" in names assert "my_module.py" in names
assert "notes.txt" in names assert "notes.txt" in names
assert "package_with_include/__init__.py" in names assert "package_with_include/__init__.py" in names
def test_write_metadata_file_license_homepage_default(mocker):
# Preparation
mocked_poetry = mocker.Mock()
mocked_poetry.file.parent = Path(".")
mocked_poetry.package = ProjectPackage("pkg_name", "1.0.0")
mocked_file = mocker.Mock()
mocked_venv = mocker.Mock()
mocked_io = mocker.Mock()
# patch Module init inside Builder class
mocker.patch("poetry.masonry.builders.builder.Module")
w = WheelBuilder(mocked_poetry, mocked_venv, mocked_io)
# Action
w._write_metadata_file(mocked_file)
# Assertion
mocked_file.write.assert_any_call("Home-page: UNKNOWN\n")
mocked_file.write.assert_any_call("License: UNKNOWN\n")
<!DOCTYPE html>
<html>
<head>
<title>Links for poetry</title>
</head>
<body>
<h1>Links for poetry</h1>
<a href="poetry-0.1.0-py3-none-any.whl#sha256=1d85132efab8ead3c6f69202843da40a03823992091c29f8d65a31af68940163" data-requires-python="&gt;=3.6.0">poetry-0.1.0-py3-none-any.whl</a><br/>
</body>
</html>
<!--SERIAL 3907384-->
\ No newline at end of file
import pytest
from poetry.repositories.legacy_repository import LegacyRepository from poetry.repositories.legacy_repository import LegacyRepository
from poetry.repositories.legacy_repository import Page from poetry.repositories.legacy_repository import Page
from poetry.utils._compat import Path from poetry.utils._compat import Path
...@@ -48,3 +50,10 @@ def test_sdist_format_support(): ...@@ -48,3 +50,10 @@ def test_sdist_format_support():
bz2_links = list(filter(lambda link: link.ext == ".tar.bz2", page.links)) bz2_links = list(filter(lambda link: link.ext == ".tar.bz2", page.links))
assert len(bz2_links) == 1 assert len(bz2_links) == 1
assert bz2_links[0].filename == "poetry-0.1.1.tar.bz2" assert bz2_links[0].filename == "poetry-0.1.1.tar.bz2"
def test_missing_version(mocker):
repo = MockRepository()
with pytest.raises(ValueError):
repo._get_release_info("missing_version", "1.1.0")
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