Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support git subfolder #9

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions poetry/core/json/schemas/poetry-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,10 @@
"type": "string",
"description": "The revision to checkout."
},
"subdirectory": {
"type": "string",
"description": "path relative to repositories root, where package is located"
},
"python": {
"type": "string",
"description": "The python versions for which the dependency should be installed."
Expand Down
2 changes: 2 additions & 0 deletions poetry/core/packages/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ def __init__(self, name, version, pretty_version=None):
self.source_type = ""
self.source_reference = ""
self.source_url = ""
self.source_subdirectory = ""

self.requires = []
self.dev_requires = []
Expand Down Expand Up @@ -299,6 +300,7 @@ def add_dependency(
rev=constraint.get("rev", None),
category=category,
optional=optional,
subdirectory=constraint.get("subdirectory", None),
)
elif "file" in constraint:
file_path = Path(constraint["file"])
Expand Down
11 changes: 10 additions & 1 deletion poetry/core/packages/vcs_dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ def __init__(
branch=None,
tag=None,
rev=None,
category="main",
optional=False,
category="main",
subdirectory=None,
):
self._vcs = vcs
self._source = source
Expand All @@ -29,6 +30,7 @@ def __init__(
self._branch = branch
self._tag = tag
self._rev = rev
self._subdirectory = subdirectory

super(VCSDependency, self).__init__(
name, "*", category=category, optional=optional, allows_prereleases=True
Expand All @@ -54,6 +56,10 @@ def tag(self):
def rev(self):
return self._rev

@property
def subdirectory(self):
return self._subdirectory

@property
def reference(self): # type: () -> str
return self._branch or self._tag or self._rev
Expand Down Expand Up @@ -87,6 +93,9 @@ def base_pep_508_name(self): # type: () -> str
self._vcs, parsed_url.format(), self.reference
)

if self.subdirectory:
requirement += "#subdirectory={}".format(self.subdirectory)

return requirement

def is_vcs(self): # type: () -> bool
Expand Down
39 changes: 32 additions & 7 deletions poetry/core/vcs/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"port": r"\d+",
"path": r"[\w~.\-/\\]+",
"name": r"[\w~.\-]+",
"rev": r"[^@#]+",
"rev": r"[^@#?]+",
"subdir": r"[\w\-/\\]+",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Linking to previous discussion concerning this bit. python-poetry/poetry#1822 (comment)

}

PATTERNS = [
Expand All @@ -27,13 +28,15 @@
r"(?P<pathname>[:/\\]({path}[/\\])?"
r"((?P<name>{name}?)(\.git|[/\\])?)?)"
r"([@#](?P<rev>{rev}))?"
r"(\?subdirectory=(?P<subdirectory>{subdir}))?"
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"],
subdir=pattern_formats["subdir"],
)
),
re.compile(
Expand All @@ -45,6 +48,7 @@
r"(?P<pathname>({path})"
r"(?P<name>{name})(\.git|/)?)"
r"([@#](?P<rev>{rev}))?"
r"(\?subdirectory=(?P<subdirectory>{subdir}))?"
r"$".format(
protocol=pattern_formats["protocol"],
user=pattern_formats["user"],
Expand All @@ -53,6 +57,7 @@
path=pattern_formats["path"],
name=pattern_formats["name"],
rev=pattern_formats["rev"],
subdir=pattern_formats["subdir"],
)
),
re.compile(
Expand All @@ -62,13 +67,15 @@
r"(?P<pathname>([:/]{path}/)"
r"(?P<name>{name})(\.git|/)?)"
r"([@#](?P<rev>{rev}))?"
r"(\?subdirectory=(?P<subdirectory>{subdir}))?"
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"],
subdir=pattern_formats["subdir"],
)
),
re.compile(
Expand All @@ -78,26 +85,31 @@
r"(?P<pathname>({path})"
r"(?P<name>{name})(\.git|/)?)"
r"([@#](?P<rev>{rev}))?"
r"(\?subdirectory=(?P<subdirectory>{subdir}))?"
r"$".format(
user=pattern_formats["user"],
resource=pattern_formats["resource"],
path=pattern_formats["path"],
name=pattern_formats["name"],
rev=pattern_formats["rev"],
subdir=pattern_formats["subdir"],
)
),
]


class ParsedUrl:
def __init__(self, protocol, resource, pathname, user, port, name, rev):
def __init__(
self, protocol, resource, pathname, user, port, name, rev, subdirectory
):
self.protocol = protocol
self.resource = resource
self.pathname = pathname
self.user = user
self.port = port
self.name = name
self.rev = rev
self.subdirectory = subdirectory

@classmethod
def parse(cls, url): # type: () -> ParsedUrl
Expand All @@ -113,6 +125,7 @@ def parse(cls, url): # type: () -> ParsedUrl
groups.get("port"),
groups.get("name"),
groups.get("rev"),
groups.get("subdirectory"),
)

raise ValueError('Invalid git url "{}"'.format(url))
Expand All @@ -128,13 +141,17 @@ def url(self): # type: () -> str
)

def format(self):
return "{}".format(self.url, "#{}".format(self.rev) if self.rev else "",)
return "{}".format(
self.url,
"#{}".format(self.rev) if self.rev else "",
"?subdirectory={}".format(self.subdirectory) if self.subdirectory else "",
)

def __str__(self): # type: () -> str
return self.format()


GitUrl = namedtuple("GitUrl", ["url", "revision"])
GitUrl = namedtuple("GitUrl", ["url", "revision", "subdirectory"])


class GitConfig:
Expand Down Expand Up @@ -174,7 +191,11 @@ def normalize_url(cls, url): # type: (str) -> GitUrl

formatted = re.sub(r"^git\+", "", url)
if parsed.rev:
formatted = re.sub(r"[#@]{}$".format(parsed.rev), "", formatted)
formatted = re.sub(r"[#@]{}(\?.*)?$".format(parsed.rev), "", formatted)
if parsed.subdirectory:
formatted = re.sub(
r"\?subdirectory={}$".format(parsed.subdirectory), "", formatted
)

altered = parsed.format() != formatted

Expand All @@ -186,11 +207,13 @@ def normalize_url(cls, url): # type: (str) -> GitUrl
elif re.match(r"^git\+file", url):
normalized = re.sub(r"git\+", "", url)
else:
normalized = re.sub(r"^(?:git\+)?ssh://", "", url)
normalized = re.sub(r"^(?:git\+)?ssh://", "", formatted)
else:
normalized = parsed.format()

return GitUrl(re.sub(r"#[^#]*$", "", normalized), parsed.rev)
return GitUrl(
re.sub(r"#[^#]*$", "", normalized), parsed.rev, parsed.subdirectory
)

@property
def config(self): # type: () -> GitConfig
Expand Down Expand Up @@ -229,6 +252,8 @@ def rev_parse(self, rev, folder=None): # type: (...) -> str
folder.as_posix(),
]

# We need "^{commit}" to ensure that the commit SHA of the commit the
# tag points to is returned, even in the case of annotated tags.
args += ["rev-parse", rev + "^{commit}"]

return self.run(*args)
Expand Down
16 changes: 16 additions & 0 deletions tests/packages/test_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import pytest

from poetry.core.packages import Package
from poetry.core.packages import VCSDependency


def test_package_authors():
Expand Down Expand Up @@ -37,3 +38,18 @@ def test_package_add_dependency_vcs_category_default_main():
"poetry", constraint={"git": "https://github.com/python-poetry/poetry.git"}
)
assert dependency.category == "main"


def test_package_add_dependency_vcs__with_subdirectory():
package = Package("foo", "0.1.0")

dependency = package.add_dependency(
"poetry",
constraint={
"git": "https://github.com/demo/project_in_subdirectory.git",
"subdirectory": "mypackage",
},
)
assert dependency.source == "https://github.com/demo/project_in_subdirectory.git"
assert dependency.subdirectory == "mypackage"
assert isinstance(dependency, VCSDependency)
13 changes: 13 additions & 0 deletions tests/packages/test_vcs_dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,19 @@ def test_to_pep_508_in_extras():
assert expected == dependency.to_pep_508()


def test_to_pep_508_with_subdirectory():
dependency = VCSDependency(
"poetry",
"git",
"https://github.com/python-poetry/poetry.git",
subdirectory="mypackage",
)

expected = "poetry @ git+https://github.com/python-poetry/poetry.git@master#subdirectory=mypackage"

assert expected == dependency.to_pep_508()


@pytest.mark.parametrize("category", ["main", "dev"])
def test_category(category):
dependency = VCSDependency(
Expand Down
Loading