Commit 7b1fd0cd by Sébastien Eustace Committed by GitHub

Merge master into develop (#2070)

* Fix Github actions cache issues (#1908)

* Fix case of `-f` flag

* Make it clearer what options to pass to `--format`

* fix (masonry.api): `get_requires_for_build_wheel` must return additional list of requirements for building a package, not listed in `pyproject.toml` and not dependencies for the package itself (#1875)

fix (tests): adopted tests

* Lazy Keyring intialization for PasswordManager (#1892)

* Fix Github Actions cache issues (#1928)

* Avoid nested quantifiers with overlapping character space on git url parsing (#1902 (#1913)

* fix (git): match for `\w` instead of `.` for getting user

* change (vcs.git): hold pattern of the regex parts in a dictionary to be consistent over all regexs

* new (vcs.git): test for `parse_url` and some fixes for the regex pattern

* new (vcs.git): test for `parse_url` with string that should fail

* fix (test.vcs.git): make flake8 happy

* fix: correct parsing of wheel version with regex. (#1932)

The previous regexp was only taking the first integer of the version number,
this presented problems when the major version number reached double digits.

Poetry would determine that the version of the dependency is '1', rather than,
ie: '14'. This caused failures to solve versions.

* Fix errors when using the --help option (#1910)

* Fix how repository credentials are retrieved from env vars (#1909)

# Conflicts:
#	poetry/utils/password_manager.py

* Fix downloading packages from Simplepypi (#1851)

* fix downloading packages from simplepypi

* unused code removed

* remove unused imports

* Upgrade dependencies for the 1.0.3 release (#1965)

* Bump version to 1.0.3 (#1966)

* Fix non-compliant Git URL matching

RFC 3986 § 2.3 permits more characters in a URL than were matched. This
corrects that, though there may be other deficiencies. This was a
regression from v1.0.2, where at least “.” was matched without error.

* Update README.md "Updating Poetry"

Currently the note in "Updating Poetry" is different from the one below in "Enable tab completion for Bash, Fish, or Zsh". This MR is to make them more consistent.

* init: change dev dependency prompt

* Fix CI issues (#2069)

Co-authored-by: brandonaut <brandon@hubermx.com>
Co-authored-by: finswimmer <finswimmer77@gmail.com>
Co-authored-by: Yannick PÉROUX <yannick.peroux@gmail.com>
Co-authored-by: Edward George <edwardgeorge@gmail.com>
Co-authored-by: Jan Škoda <skoda@jskoda.cz>
Co-authored-by: Andrew Marshall <andrew@johnandrewmarshall.com>
Co-authored-by: Andrew Selzer <andrewfselzer@gmail.com>
Co-authored-by: Andrii Maletskyi <andrii.maletskyi@gmail.com>
parent 4d05c156
......@@ -33,7 +33,7 @@ jobs:
- name: Get full python version
id: full-python-version
run: |
echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info[:3]))")
echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))")
- name: Install and set up Poetry
run: |
python get-poetry.py --preview -y
......@@ -69,7 +69,7 @@ jobs:
- name: Get full python version
id: full-python-version
run: |
echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info[:3]))")
echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))")
- name: Install and set up Poetry
run: |
python get-poetry.py --preview -y
......@@ -87,7 +87,7 @@ jobs:
- name: Test
run: |
source $HOME/.poetry/env
poetry run pytest -q tests
.venv/bin/pytest -q tests
Windows:
needs: Linting
......@@ -106,7 +106,7 @@ jobs:
id: full-python-version
shell: bash
run: |
echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info[:3]))")
echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))")
- name: Install and setup Poetry
run: |
python get-poetry.py --preview -y
......
# Change Log
## [1.0.3] - 2020-01-31
### Fixed
- Fixed an error which caused the configuration environment variables (like `POETRY_HTTP_BASIC_XXX_PASSWORD`) to not be used ([#1909](https://github.com/python-poetry/poetry/pull/1909)).
- Fixed an error where the `--help` option was not working ([#1910](https://github.com/python-poetry/poetry/pull/1910)).
- Fixed an error where packages from private indices were not decompressed properly ([#1851](https://github.com/python-poetry/poetry/pull/1851)).
- Fixed an error where the version of some PEP-508-formatted wheel dependencies was not properly retrieved ([#1932](https://github.com/python-poetry/poetry/pull/1932)).
- Fixed internal regexps to avoid potential catastrophic backtracking errors ([#1913](https://github.com/python-poetry/poetry/pull/1913)).
- Fixed performance issues when custom indices were defined in the `pyproject.toml` file ([#1892](https://github.com/python-poetry/poetry/pull/1892)).
- Fixed the `get_requires_for_build_wheel()` function of `masonry.api` which wasn't returning the proper result ([#1875](https://github.com/python-poetry/poetry/pull/1875)).
## [1.0.2] - 2020-01-10
### Fixed
......@@ -783,7 +796,8 @@ Initial release
[Unreleased]: https://github.com/python-poetry/poetry/compare/1.0.2...master
[Unreleased]: https://github.com/python-poetry/poetry/compare/1.0.3...master
[1.0.3]: https://github.com/python-poetry/poetry/releases/tag/1.0.3
[1.0.2]: https://github.com/python-poetry/poetry/releases/tag/1.0.2
[1.0.1]: https://github.com/python-poetry/poetry/releases/tag/1.0.1
[1.0.0]: https://github.com/python-poetry/poetry/releases/tag/1.0.0
......
......@@ -65,7 +65,7 @@ to `self update`.
poetry self update 1.0.0
```
!!!note
*Note:*
If you are still on poetry version < 1.0 use `poetry self:update` instead.
......
......@@ -305,7 +305,7 @@ Note that, at the moment, only pure python wheels are supported.
### Options
* `--format (-F)`: Limit the format to either wheel or sdist.
* `--format (-f)`: Limit the format to either `wheel` or `sdist`.
## publish
......
__version__ = "1.0.2"
__version__ = "1.0.3"
......@@ -165,8 +165,7 @@ The <c1>init</c1> command creates a basic <comment>pyproject.toml</> file in the
dev_requirements = {}
question = (
"Would you like to define your dev dependencies"
" (require-dev) interactively"
"Would you like to define your development dependencies interactively?"
)
if self.confirm(question, True):
if not help_displayed:
......
......@@ -9,6 +9,7 @@ from clikit.api.event import PRE_HANDLE
from clikit.api.event import PreHandleEvent
from clikit.api.event import PreResolveEvent
from clikit.api.event.event_dispatcher import EventDispatcher
from clikit.api.exceptions import CliKitException
from clikit.api.formatter import Style
from clikit.api.io import Input
from clikit.api.io import InputStream
......@@ -101,7 +102,16 @@ class ApplicationConfig(BaseApplicationConfig):
if args.has_option_token("-h") or args.has_option_token("--help"):
from clikit.api.resolver import ResolvedCommand
resolved_command = self.command_resolver.resolve(args, application)
try:
resolved_command = self.command_resolver.resolve(args, application)
except CliKitException:
# We weren't able to resolve the command,
# due to a parse error most likely,
# so we fall back on the default behavior
return super(ApplicationConfig, self).resolve_help_command(
event, event_name, dispatcher
)
# If the current command is the run one, skip option
# check and interpret them as part of the executed command
if resolved_command.command.name == "run":
......
......@@ -20,13 +20,14 @@ log = logging.getLogger(__name__)
def get_requires_for_build_wheel(config_settings=None):
"""
Returns a list of requirements for building, as strings
"""
poetry = Factory().create_poetry(Path("."))
Returns an additional list of requirements for building, as PEP508 strings,
above and beyond those specified in the pyproject.toml file.
main, _ = SdistBuilder.convert_dependencies(poetry.package, poetry.package.requires)
This implementation is optional. At the moment it only returns an empty list, which would be the same as if
not define. So this is just for completeness for future implementation.
"""
return main
return []
# For now, we require all dependencies to build either a wheel or an sdist.
......
......@@ -2,6 +2,7 @@ import os
import re
from poetry.semver import Version
from poetry.utils.patterns import wheel_file_re
from poetry.version.requirements import Requirement
from .dependency import Dependency
......@@ -70,7 +71,7 @@ def dependency_from_pep_508(name):
link = Link(path_to_url(os.path.normpath(os.path.abspath(link.path))))
# wheel file
if link.is_wheel:
m = re.match(r"^(?P<namever>(?P<name>.+?)-(?P<ver>\d.*?))", link.filename)
m = wheel_file_re.match(link.filename)
if not m:
raise ValueError("Invalid wheel name: {}".format(link.filename))
......
......@@ -3,8 +3,6 @@ import os
import tarfile
import zipfile
from bz2 import BZ2File
from gzip import GzipFile
from typing import Dict
from typing import List
from typing import Union
......@@ -114,27 +112,21 @@ class Inspector:
# Still not dependencies found
# So, we unpack and introspect
suffix = file_path.suffix
gz = None
if suffix == ".zip":
tar = zipfile.ZipFile(str(file_path))
else:
if suffix == ".bz2":
gz = BZ2File(str(file_path))
suffixes = file_path.suffixes
if len(suffixes) > 1 and suffixes[-2] == ".tar":
suffix = ".tar.bz2"
else:
gz = GzipFile(str(file_path))
suffix = ".tar.gz"
tar = tarfile.TarFile(str(file_path), fileobj=gz)
tar = tarfile.open(str(file_path))
try:
tar.extractall(os.path.join(str(file_path.parent), "unpacked"))
finally:
if gz:
gz.close()
tar.close()
unpacked = file_path.parent / "unpacked"
......
......@@ -117,44 +117,52 @@ class KeyRing:
class PasswordManager:
def __init__(self, config):
self._config = config
self._keyring = KeyRing("poetry-repository")
if not self._keyring.is_available():
logger.warning("Using a plaintext file to store and retrieve credentials")
self._keyring = None
@property
def keyring(self):
if self._keyring is None:
self._keyring = KeyRing("poetry-repository")
if not self._keyring.is_available():
logger.warning(
"Using a plaintext file to store and retrieve credentials"
)
return self._keyring
def set_pypi_token(self, name, token):
if not self._keyring.is_available():
if not self.keyring.is_available():
self._config.auth_config_source.add_property(
"pypi-token.{}".format(name), token
)
else:
self._keyring.set_password(name, "__token__", token)
self.keyring.set_password(name, "__token__", token)
def get_pypi_token(self, name):
if not self._keyring.is_available():
if not self.keyring.is_available():
return self._config.get("pypi-token.{}".format(name))
return self._keyring.get_password(name, "__token__")
return self.keyring.get_password(name, "__token__")
def delete_pypi_token(self, name):
if not self._keyring.is_available():
if not self.keyring.is_available():
return self._config.auth_config_source.remove_property(
"pypi-token.{}".format(name)
)
self._keyring.delete_password(name, "__token__")
self.keyring.delete_password(name, "__token__")
def get_http_auth(self, name):
auth = self._config.get("http-basic.{}".format(name))
if not auth:
return None
username, password = auth["username"], auth.get("password")
if password is None:
password = self._keyring.get_password(name, username)
username = self._config.get("http-basic.{}.username".format(name))
password = self._config.get("http-basic.{}.password".format(name))
if not username and not password:
return None
else:
username, password = auth["username"], auth.get("password")
if password is None:
password = self.keyring.get_password(name, username)
return {
"username": username,
......@@ -164,10 +172,10 @@ class PasswordManager:
def set_http_password(self, name, username, password):
auth = {"username": username}
if not self._keyring.is_available():
if not self.keyring.is_available():
auth["password"] = password
else:
self._keyring.set_password(name, username, password)
self.keyring.set_password(name, username, password)
self._config.auth_config_source.add_property("http-basic.{}".format(name), auth)
......@@ -177,7 +185,7 @@ class PasswordManager:
return
try:
self._keyring.delete_password(name, auth["username"])
self.keyring.delete_password(name, auth["username"])
except KeyRingError:
pass
......
......@@ -7,46 +7,84 @@ from collections import namedtuple
from poetry.utils._compat import decode
pattern_formats = {
"protocol": r"\w+",
"user": r"[a-zA-Z0-9_.-]+",
"resource": r"[a-zA-Z0-9_.-]+",
"port": r"\d+",
"path": r"[\w~.\-/\\]+",
"name": r"[\w~.\-]+",
"rev": r"[^@#]+",
}
PATTERNS = [
re.compile(
r"(git\+)?"
r"((?P<protocol>\w+)://)"
r"((?P<user>\w+)@)?"
r"(?P<resource>[\w.\-]+)"
r"(:(?P<port>\d+))?"
r"(?P<pathname>(/(?P<owner>\w+)/)"
r"((?P<projects>([\w\-/]+)/)?(?P<name>[\w\-]+)(\.git|/)?)?)"
r"([@#](?P<rev>[^@#]+))?"
r"$"
),
re.compile(
r"^(git\+)?"
r"(?P<protocol>https?|git|ssh|rsync|file)://"
r"(?:(?P<user>.+)@)*"
r"(?P<resource>[a-z0-9_.-]*)"
r"(:?P<port>[\d]+)?"
r"(?P<pathname>[:/]((?P<owner>[\w\-]+)/(?P<projects>([\w\-/]+)/)?)?"
r"((?P<name>[\w\-.]+?)(\.git|/)?)?)"
r"([@#](?P<rev>[^@#]+))?"
r"$"
r"(?:(?P<user>{user})@)?"
r"(?P<resource>{resource})?"
r"(:(?P<port>{port}))?"
r"(?P<pathname>[:/\\]({path}[/\\])?"
r"((?P<name>{name}?)(\.git|[/\\])?)?)"
r"([@#](?P<rev>{rev}))?"
r"$".format(
user=pattern_formats["user"],
resource=pattern_formats["resource"],
port=pattern_formats["port"],
path=pattern_formats["path"],
name=pattern_formats["name"],
rev=pattern_formats["rev"],
)
),
re.compile(
r"^(?:(?P<user>.+)@)*"
r"(?P<resource>[a-z0-9_.-]*)[:]*"
r"(?P<port>[\d]+)?"
r"(?P<pathname>/?(?P<owner>.+)/(?P<projects>([\w\-/]+)/)?(?P<name>.+).git)"
r"([@#](?P<rev>[^@#]+))?"
r"$"
r"(git\+)?"
r"((?P<protocol>{protocol})://)"
r"(?:(?P<user>{user})@)?"
r"(?P<resource>{resource}:?)"
r"(:(?P<port>{port}))?"
r"(?P<pathname>({path})"
r"(?P<name>{name})(\.git|/)?)"
r"([@#](?P<rev>{rev}))?"
r"$".format(
protocol=pattern_formats["protocol"],
user=pattern_formats["user"],
resource=pattern_formats["resource"],
port=pattern_formats["port"],
path=pattern_formats["path"],
name=pattern_formats["name"],
rev=pattern_formats["rev"],
)
),
re.compile(
r"((?P<user>\w+)@)?"
r"(?P<resource>[\w.\-]+)"
r"[:/]{1,2}"
r"(?P<pathname>((?P<owner>\w+)/)?"
r"(?P<projects>([\w\-/]+)/)?"
r"((?P<name>[\w\-]+)(\.git|/)?)?)"
r"([@#](?P<rev>[^@#]+))?"
r"$"
r"^(?:(?P<user>{user})@)?"
r"(?P<resource>{resource})"
r"(:(?P<port>{port}))?"
r"(?P<pathname>([:/]{path}/)"
r"(?P<name>{name})(\.git|/)?)"
r"([@#](?P<rev>{rev}))?"
r"$".format(
user=pattern_formats["user"],
resource=pattern_formats["resource"],
port=pattern_formats["port"],
path=pattern_formats["path"],
name=pattern_formats["name"],
rev=pattern_formats["rev"],
)
),
re.compile(
r"((?P<user>{user})@)?"
r"(?P<resource>{resource})"
r"[:/]{{1,2}}"
r"(?P<pathname>({path})"
r"(?P<name>{name})(\.git|/)?)"
r"([@#](?P<rev>{rev}))?"
r"$".format(
user=pattern_formats["user"],
resource=pattern_formats["resource"],
path=pattern_formats["path"],
name=pattern_formats["name"],
rev=pattern_formats["rev"],
)
),
]
......
[tool.poetry]
name = "poetry"
version = "1.0.2"
version = "1.0.3"
description = "Python dependency management and packaging made easy."
authors = [
"Sébastien Eustace <sebastien@eustace.io>"
......
......@@ -7,7 +7,9 @@ from poetry.utils.env import EnvManager
from poetry.utils.toml_file import TomlFile
def test_none_activated(app, tmp_dir):
def test_none_activated(app, tmp_dir, mocker, env):
mocker.patch("poetry.utils.env.EnvManager.get", return_value=env)
app.poetry.config.merge({"virtualenvs": {"path": str(tmp_dir)}})
venv_name = EnvManager.generate_env_name(
......
......@@ -37,6 +37,7 @@ def check_output_wrapper(version=Version.parse("3.7.1")):
def test_activate_activates_non_existing_virtualenv_no_envs_file(app, tmp_dir, mocker):
mocker.stopall()
if "VIRTUAL_ENV" in os.environ:
del os.environ["VIRTUAL_ENV"]
......@@ -85,6 +86,7 @@ Using virtualenv: {}
def test_get_prefers_explicitly_activated_virtualenvs_over_env_var(
app, tmp_dir, mocker
):
mocker.stopall()
os.environ["VIRTUAL_ENV"] = "/environment/prefix"
venv_name = EnvManager.generate_env_name(
......@@ -127,6 +129,7 @@ Using virtualenv: {}
def test_get_prefers_explicitly_activated_non_existing_virtualenvs_over_env_var(
app, tmp_dir, mocker
):
mocker.stopall()
os.environ["VIRTUAL_ENV"] = "/environment/prefix"
venv_name = EnvManager.generate_env_name(
......
from cleo.testers import CommandTester
from poetry.utils._compat import Path
from poetry.utils.env import MockEnv
def test_run_passes_all_args(app, mocker):
env = MockEnv(path=Path("/prefix"), base=Path("/base/prefix"), is_venv=True)
def test_run_passes_all_args(app, mocker, env):
mocker.patch("poetry.utils.env.EnvManager.get", return_value=env)
command = app.find("run")
......
......@@ -13,6 +13,7 @@ from poetry.repositories import Pool
from poetry.repositories import Repository as BaseRepository
from poetry.repositories.exceptions import PackageNotFound
from poetry.utils._compat import Path
from poetry.utils.env import MockEnv
from poetry.utils.toml_file import TomlFile
from tests.helpers import mock_clone
from tests.helpers import mock_download
......@@ -28,8 +29,13 @@ def installed():
return BaseRepository()
@pytest.fixture
def env():
return MockEnv(path=Path("/prefix"), base=Path("/base/prefix"), is_venv=True)
@pytest.fixture(autouse=True)
def setup(mocker, installer, installed, config):
def setup(mocker, installer, installed, config, env):
# Set Installer's installer
p = mocker.patch("poetry.installation.installer.Installer._get_installer")
p.return_value = installer
......@@ -51,6 +57,9 @@ def setup(mocker, installer, installed, config):
# Patch download to not download anything but to just copy from fixtures
mocker.patch("poetry.utils.inspector.Inspector.download", new=mock_download)
# Patch the virtual environment creation do actually do nothing
mocker.patch("poetry.utils.env.EnvManager.create_venv", return_value=env)
# Setting terminal width
environ = dict(os.environ)
os.environ["COLUMNS"] = "80"
......
......@@ -28,15 +28,15 @@ fixtures = os.path.join(os.path.dirname(__file__), "builders", "fixtures")
def test_get_requires_for_build_wheel():
expected = ["cleo>=0.6.0,<0.7.0", "cachy[msgpack]>=0.2.0,<0.3.0"]
expected = []
with cwd(os.path.join(fixtures, "complete")):
api.get_requires_for_build_wheel() == expected
assert api.get_requires_for_build_wheel() == expected
def test_get_requires_for_build_sdist():
expected = ["cleo>=0.6.0,<0.7.0", "cachy[msgpack]>=0.2.0,<0.3.0"]
expected = []
with cwd(os.path.join(fixtures, "complete")):
api.get_requires_for_build_sdist() == expected
assert api.get_requires_for_build_sdist() == expected
def test_build_wheel():
......
......@@ -191,3 +191,14 @@ def test_dependency_from_pep_508_with_url():
assert "django-utils" == dep.name
assert dep.is_url()
assert "https://example.com/django-utils-1.0.0.tar.gz" == dep.url
def test_dependency_from_pep_508_with_wheel_url():
name = (
"example_wheel @ https://example.com/example_wheel-14.0.2-py2.py3-none-any.whl"
)
dep = dependency_from_pep_508(name)
assert "example-wheel" == dep.name
assert str(dep.constraint) == "14.0.2"
import os
import pytest
from keyring.backend import KeyringBackend
......@@ -208,3 +210,17 @@ def test_keyring_with_chainer_backend_and_not_compatible_only_should_be_unavaila
key_ring = KeyRing("poetry")
assert not key_ring.is_available()
def test_get_http_auth_from_environment_variables(
environ, config, mock_available_backend
):
os.environ["POETRY_HTTP_BASIC_FOO_USERNAME"] = "bar"
os.environ["POETRY_HTTP_BASIC_FOO_PASSWORD"] = "baz"
manager = PasswordManager(config)
auth = manager.get_http_auth("foo")
assert "bar" == auth["username"]
assert "baz" == auth["password"]
......@@ -2,6 +2,7 @@ import pytest
from poetry.vcs.git import Git
from poetry.vcs.git import GitUrl
from poetry.vcs.git import ParsedUrl
@pytest.mark.parametrize(
......@@ -20,6 +21,10 @@ from poetry.vcs.git import GitUrl
GitUrl("https://user@hostname/project/blah.git", None),
),
(
"git+https://user@hostname/project~_-.foo/blah~_-.bar.git",
GitUrl("https://user@hostname/project~_-.foo/blah~_-.bar.git", None),
),
(
"git+https://user@hostname:project/blah.git",
GitUrl("https://user@hostname/project/blah.git", None),
),
......@@ -74,3 +79,183 @@ from poetry.vcs.git import GitUrl
)
def test_normalize_url(url, normalized):
assert normalized == Git.normalize_url(url)
@pytest.mark.parametrize(
"url, parsed",
[
(
"git+ssh://user@hostname:project.git#commit",
ParsedUrl(
"ssh", "hostname", ":project.git", "user", None, "project", "commit"
),
),
(
"git+http://user@hostname/project/blah.git@commit",
ParsedUrl(
"http", "hostname", "/project/blah.git", "user", None, "blah", "commit"
),
),
(
"git+https://user@hostname/project/blah.git",
ParsedUrl(
"https", "hostname", "/project/blah.git", "user", None, "blah", None
),
),
(
"git+https://user@hostname/project~_-.foo/blah~_-.bar.git",
ParsedUrl(
"https",
"hostname",
"/project~_-.foo/blah~_-.bar.git",
"user",
None,
"blah~_-.bar",
None,
),
),
(
"git+https://user@hostname:project/blah.git",
ParsedUrl(
"https", "hostname", ":project/blah.git", "user", None, "blah", None
),
),
(
"git+ssh://git@github.com:sdispater/poetry.git#v1.0.27",
ParsedUrl(
"ssh",
"github.com",
":sdispater/poetry.git",
"git",
None,
"poetry",
"v1.0.27",
),
),
(
"git+ssh://git@github.com:/sdispater/poetry.git",
ParsedUrl(
"ssh",
"github.com",
":/sdispater/poetry.git",
"git",
None,
"poetry",
None,
),
),
(
"git+ssh://git@github.com:org/repo",
ParsedUrl("ssh", "github.com", ":org/repo", "git", None, "repo", None),
),
(
"git+ssh://git@github.com/org/repo",
ParsedUrl("ssh", "github.com", "/org/repo", "git", None, "repo", None),
),
(
"git+ssh://foo:22/some/path",
ParsedUrl("ssh", "foo", "/some/path", None, "22", "path", None),
),
(
"git@github.com:org/repo",
ParsedUrl(None, "github.com", ":org/repo", "git", None, "repo", None),
),
(
"git+https://github.com/sdispater/pendulum",
ParsedUrl(
"https",
"github.com",
"/sdispater/pendulum",
None,
None,
"pendulum",
None,
),
),
(
"git+https://github.com/sdispater/pendulum#7a018f2d075b03a73409e8356f9b29c9ad4ea2c5",
ParsedUrl(
"https",
"github.com",
"/sdispater/pendulum",
None,
None,
"pendulum",
"7a018f2d075b03a73409e8356f9b29c9ad4ea2c5",
),
),
(
"git+ssh://git@git.example.com:b/b.git#v1.0.0",
ParsedUrl("ssh", "git.example.com", ":b/b.git", "git", None, "b", "v1.0.0"),
),
(
"git+ssh://git@github.com:sdispater/pendulum.git#foo/bar",
ParsedUrl(
"ssh",
"github.com",
":sdispater/pendulum.git",
"git",
None,
"pendulum",
"foo/bar",
),
),
(
"git+file:///foo/bar.git",
ParsedUrl("file", None, "/foo/bar.git", None, None, "bar", None),
),
(
"git+file://C:\\Users\\hello\\testing.git#zkat/windows-files",
ParsedUrl(
"file",
"C",
":\\Users\\hello\\testing.git",
None,
None,
"testing",
"zkat/windows-files",
),
),
(
"git+https://git.example.com/sdispater/project/my_repo.git",
ParsedUrl(
"https",
"git.example.com",
"/sdispater/project/my_repo.git",
None,
None,
"my_repo",
None,
),
),
(
"git+ssh://git@git.example.com:sdispater/project/my_repo.git",
ParsedUrl(
"ssh",
"git.example.com",
":sdispater/project/my_repo.git",
"git",
None,
"my_repo",
None,
),
),
],
)
def test_parse_url(url, parsed):
result = ParsedUrl.parse(url)
assert parsed.name == result.name
assert parsed.pathname == result.pathname
assert parsed.port == result.port
assert parsed.protocol == result.protocol
assert parsed.resource == result.resource
assert parsed.rev == result.rev
assert parsed.url == result.url
assert parsed.user == result.user
def test_parse_url_should_fail():
url = "https://" + "@" * 64 + "!"
with pytest.raises(ValueError):
ParsedUrl.parse(url)
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