Skip to content

Commit

Permalink
Add support for url dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
sdispater committed Jul 28, 2019
1 parent b6f4542 commit 15737fb
Show file tree
Hide file tree
Showing 20 changed files with 669 additions and 172 deletions.
18 changes: 18 additions & 0 deletions docs/docs/versions.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,24 @@ my-package = { path = "../my-package/dist/my-package-0.1.0.tar.gz" }
You can install path dependencies in editable/development mode.
Just pass `--develop my-package` (repeatable as much as you want) to
the `install` command.


### `url` dependencies

To depend on a library located on a remote archive,
you can use the `url` property:

```toml
[tool.poetry.dependencies]
# directory
my-package = { url = "https://example.com/my-package-0.1.0.tar.gz" }
```

with the corresponding `add` call:

```bash
poetry add https://example.com/my-package-0.1.0.tar.gz
```


### Python restricted dependencies
Expand Down
6 changes: 5 additions & 1 deletion poetry/console/commands/add.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,11 @@ def handle(self):
for key in poetry_content[section]:
if key.lower() == name.lower():
pair = self._parse_requirements([name])[0]
if "git" in pair or pair.get("version") == "latest":
if (
"git" in pair
or "url" in pair
or pair.get("version") == "latest"
):
continue

raise ValueError("Package {} is already present".format(name))
Expand Down
55 changes: 36 additions & 19 deletions poetry/console/commands/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

from poetry.utils._compat import Path
from poetry.utils._compat import OrderedDict
from poetry.utils._compat import urlparse
from poetry.utils.helpers import temporary_directory

from .command import Command
from .env_command import EnvCommand
Expand Down Expand Up @@ -211,6 +213,7 @@ def _determine_requirements(
constraint = self._parse_requirements([package])[0]
if (
"git" in constraint
or "url" in constraint
or "path" in constraint
or "version" in constraint
):
Expand Down Expand Up @@ -276,7 +279,7 @@ def _determine_requirements(
requires = self._parse_requirements(requires)
result = []
for requirement in requires:
if "git" in requirement or "path" in requirement:
if "git" in requirement or "url" in requirement or "path" in requirement:
result.append(requirement)
continue
elif "version" not in requirement:
Expand Down Expand Up @@ -343,28 +346,42 @@ def _parse_requirements(
extras = [e.strip() for e in extras_m.group(1).split(",")]
requirement, _ = requirement.split("[")

if requirement.startswith(("git+https://", "git+ssh://")):
url = requirement.lstrip("git+")
rev = None
if "@" in url:
url, rev = url.split("@")
url_parsed = urlparse.urlparse(requirement)
if url_parsed.scheme and url_parsed.netloc:
# Url
if url_parsed.scheme in ["git+https", "git+ssh"]:
url = requirement.lstrip("git+")
rev = None
if "@" in url:
url, rev = url.split("@")

pair = OrderedDict(
[("name", url.split("/")[-1].rstrip(".git")), ("git", url)]
)
if rev:
pair["rev"] = rev

pair = OrderedDict(
[("name", url.split("/")[-1].rstrip(".git")), ("git", url)]
)
if rev:
pair["rev"] = rev
if extras:
pair["extras"] = extras

if extras:
pair["extras"] = extras
package = Provider.get_package_from_vcs(
"git", url, reference=pair.get("rev")
)
pair["name"] = package.name
result.append(pair)

package = Provider.get_package_from_vcs(
"git", url, reference=pair.get("rev")
)
pair["name"] = package.name
result.append(pair)
continue
elif url_parsed.scheme in ["http", "https"]:
package = Provider.get_package_from_url(requirement)

continue
pair = OrderedDict(
[("name", package.name), ("url", package.source_url)]
)
if extras:
pair["extras"] = extras

result.append(pair)
continue
elif (os.path.sep in requirement or "/" in requirement) and cwd.joinpath(
requirement
).exists():
Expand Down
39 changes: 39 additions & 0 deletions poetry/json/schemas/poetry-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,9 @@
{
"$ref": "#/definitions/path-dependency"
},
{
"$ref": "#/definitions/url-dependency"
},
{
"$ref": "#/definitions/multiple-constraints-dependency"
}
Expand Down Expand Up @@ -394,6 +397,42 @@
}
}
},
"url-dependency": {
"type": "object",
"required": [
"url"
],
"additionalProperties": false,
"properties": {
"url": {
"type": "string",
"description": "The url to the file."
},
"python": {
"type": "string",
"description": "The python versions for which the dependency should be installed."
},
"platform": {
"type": "string",
"description": "The platform(s) for which the dependency should be installed."
},
"markers": {
"type": "string",
"description": "The PEP 508 compliant environment markers for which the dependency should be installed."
},
"optional": {
"type": "boolean",
"description": "Whether the dependency is optional or not."
},
"extras": {
"type": "array",
"description": "The required extras for this dependency.",
"items": {
"type": "string"
}
}
}
},
"multiple-constraints-dependency": {
"type": "array",
"minItems": 1,
Expand Down
1 change: 1 addition & 0 deletions poetry/packages/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from .utils.utils import is_url
from .utils.utils import path_to_url
from .utils.utils import strip_extras
from .url_dependency import URLDependency
from .vcs_dependency import VCSDependency


Expand Down
3 changes: 3 additions & 0 deletions poetry/packages/dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,9 @@ def is_file(self):
def is_directory(self):
return False

def is_url(self):
return False

def accepts(self, package): # type: (poetry.packages.Package) -> bool
"""
Determines if the given package matches this dependency.
Expand Down
6 changes: 4 additions & 2 deletions poetry/packages/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
from .dependency import Dependency
from .directory_dependency import DirectoryDependency
from .file_dependency import FileDependency
from .url_dependency import URLDependency
from .vcs_dependency import VCSDependency
from .utils.utils import convert_markers
from .utils.utils import create_nested_marker

AUTHOR_REGEX = re.compile(r"(?u)^(?P<name>[- .,\w\d'’\"()]+)(?: <(?P<email>.+?)>)?$")
Expand Down Expand Up @@ -111,7 +111,7 @@ def pretty_string(self):

@property
def full_pretty_version(self):
if self.source_type in ["file", "directory"]:
if self.source_type in ["file", "directory", "url"]:
return "{} {}".format(self._pretty_version, self.source_url)

if self.source_type not in ["hg", "git"]:
Expand Down Expand Up @@ -314,6 +314,8 @@ def add_dependency(
base=self.root_dir,
develop=constraint.get("develop", True),
)
elif "url" in constraint:
dependency = URLDependency(name, constraint["url"], category=category)
else:
version = constraint["version"]

Expand Down
50 changes: 50 additions & 0 deletions poetry/packages/url_dependency.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import hashlib
import io

from pkginfo.distribution import HEADER_ATTRS
from pkginfo.distribution import HEADER_ATTRS_2_0
from poetry.utils._compat import urlparse

from .dependency import Dependency

# Patching pkginfo to support Metadata version 2.1 (PEP 566)
HEADER_ATTRS.update(
{"2.1": HEADER_ATTRS_2_0 + (("Provides-Extra", "provides_extra", True),)}
)


class URLDependency(Dependency):
def __init__(
self,
name,
url, # type: str
category="main", # type: str
optional=False, # type: bool
):
self._url = url

parsed = urlparse.urlparse(url)
if not parsed.scheme or not parsed.netloc:
raise ValueError("{} does not seem like a valid url".format(url))

super(URLDependency, self).__init__(
name, "*", category=category, optional=optional, allows_prereleases=True
)

@property
def url(self):
return self._url

@property
def base_pep_508_name(self): # type: () -> str
requirement = self.pretty_name

if self.extras:
requirement += "[{}]".format(",".join(self.extras))

requirement += " @ {}".format(self._url)

return requirement

def is_url(self): # type: () -> bool
return True
Loading

0 comments on commit 15737fb

Please sign in to comment.