Commit e95a05c9 by Sébastien Eustace Committed by GitHub

Improve the export command (#1277)

* Improve the export command

* Update documentation
parent 93e562ec
......@@ -437,10 +437,13 @@ poetry export -f requirements.txt > requirements.txt
### Options
* `--format (-f)`: the format to export to. Currently, only
* `--format (-f)`: The format to export to. Currently, only
`requirements.txt` is supported.
* `--output (-o)`: the name of the output file. If omitted, print to standard
* `--output (-o)`: The name of the output file. If omitted, print to standard
output.
* `--dev`: Include development dependencies.
* `--without-hashes`: Exclude hashes from the exported file.
* `--with-credentials`: Include credentials for extra indices.
## env
......
......@@ -14,6 +14,7 @@ class ExportCommand(Command):
option("output", "o", "The name of the output file.", flag=False),
option("without-hashes", None, "Exclude hashes from the exported file."),
option("dev", None, "Include development dependencies."),
option("with-credentials", None, "Include credentials for extra indices."),
]
def handle(self):
......@@ -47,11 +48,12 @@ class ExportCommand(Command):
"</warning>"
)
exporter = Exporter(self.poetry.locker)
exporter = Exporter(self.poetry)
exporter.export(
fmt,
self.poetry.file.parent,
output or self.io,
with_hashes=not self.option("without-hashes"),
dev=self.option("dev"),
with_credentials=self.option("with-credentials"),
)
......@@ -15,6 +15,7 @@ from .constraints import parse_constraint as parse_generic_constraint
from .constraints.constraint import Constraint
from .constraints.multi_constraint import MultiConstraint
from .constraints.union_constraint import UnionConstraint
from .utils.utils import convert_markers
class Dependency(object):
......@@ -188,6 +189,7 @@ class Dependency(object):
requirement = self.base_pep_508_name
markers = []
has_extras = False
if not self.marker.is_any():
marker = self.marker
if not with_extras:
......@@ -195,6 +197,8 @@ class Dependency(object):
if not marker.is_empty():
markers.append(str(marker))
has_extras = "extra" in convert_markers(marker)
else:
# Python marker
if self.python_versions != "*":
......@@ -205,7 +209,7 @@ class Dependency(object):
)
in_extras = " || ".join(self._in_extras)
if in_extras and with_extras:
if in_extras and with_extras and not has_extras:
markers.append(
self._create_nested_marker("extra", parse_generic_constraint(in_extras))
)
......
from typing import Optional
from typing import Union
from clikit.api.io import IO
from poetry.packages.locker import Locker
from poetry.packages.directory_dependency import DirectoryDependency
from poetry.packages.file_dependency import FileDependency
from poetry.packages.url_dependency import URLDependency
from poetry.packages.vcs_dependency import VCSDependency
from poetry.poetry import Poetry
from poetry.utils._compat import Path
from poetry.utils._compat import decode
......@@ -13,55 +17,122 @@ class Exporter(object):
"""
ACCEPTED_FORMATS = ("requirements.txt",)
ALLOWED_HASH_ALGORITHMS = ("sha256", "sha384", "sha512")
def __init__(self, lock): # type: (Locker) -> None
self._lock = lock
def __init__(self, poetry): # type: (Poetry) -> None
self._poetry = poetry
def export(
self, fmt, cwd, output, with_hashes=True, dev=False
): # type: (str, Path, Union[IO, str], bool, bool) -> None
self, fmt, cwd, output, with_hashes=True, dev=False, with_credentials=False
): # type: (str, Path, Union[IO, str], bool, bool, bool) -> None
if fmt not in self.ACCEPTED_FORMATS:
raise ValueError("Invalid export format: {}".format(fmt))
getattr(self, "_export_{}".format(fmt.replace(".", "_")))(
cwd, output, with_hashes=with_hashes, dev=dev
cwd,
output,
with_hashes=with_hashes,
dev=dev,
with_credentials=with_credentials,
)
def _export_requirements_txt(
self, cwd, output, with_hashes=True, dev=False
): # type: (Path, Union[IO, str], bool, bool) -> None
self, cwd, output, with_hashes=True, dev=False, with_credentials=False
): # type: (Path, Union[IO, str], bool, bool, bool) -> None
indexes = []
content = ""
for package in sorted(
self._lock.locked_repository(dev).packages, key=lambda p: p.name
self._poetry.locker.locked_repository(dev).packages, key=lambda p: p.name
):
if package.source_type == "git":
dependency = VCSDependency(
package.name,
package.source_type,
package.source_url,
package.source_reference,
)
dependency.marker = package.marker
line = "-e git+{}@{}#egg={}".format(
package.source_url, package.source_reference, package.name
)
elif package.source_type in ["directory", "file"]:
line = ""
elif package.source_type in ["directory", "file", "url"]:
if package.source_type == "file":
dependency = FileDependency(package.name, Path(package.source_url))
elif package.source_type == "directory":
dependency = DirectoryDependency(
package.name, Path(package.source_url)
)
else:
dependency = URLDependency(package.name, package.source_url)
dependency.marker = package.marker
line = "{}".format(package.source_url)
if package.develop:
line += "-e "
line += package.source_url
line = "-e " + line
else:
line = "{}=={}".format(package.name, package.version.text)
dependency = package.to_dependency()
line = "{}=={}".format(package.name, package.version)
if package.source_type == "legacy" and package.source_url:
line += " \\\n"
line += " --index-url {}".format(package.source_url)
requirement = dependency.to_pep_508()
if ";" in requirement:
line += "; {}".format(requirement.split(";")[1].strip())
if package.source_type == "legacy" and package.source_url:
indexes.append(package.source_url)
if package.hashes and with_hashes:
hashes = []
for h in package.hashes:
algorithm = "sha256"
if ":" in h:
algorithm, h = h.split(":")
if package.hashes and with_hashes:
if algorithm not in self.ALLOWED_HASH_ALGORITHMS:
continue
hashes.append("{}:{}".format(algorithm, h))
if hashes:
line += " \\\n"
for i, h in enumerate(package.hashes):
line += " --hash=sha256:{}{}".format(
for i, h in enumerate(hashes):
line += " --hash={}{}".format(
h, " \\\n" if i < len(package.hashes) - 1 else ""
)
line += "\n"
content += line
if indexes:
# If we have extra indexes, we add them to the begin
# of the output
indexes_header = ""
for index in indexes:
repository = [
r
for r in self._poetry.pool.repositories
if r.url == index.rstrip("/")
][0]
if (
self._poetry.pool.has_default()
and repository is self._poetry.pool.repositories[0]
):
url = (
repository.authenticated_url
if with_credentials
else repository.url
)
indexes_header = "--index-url {}\n".format(url)
continue
url = (
repository.authenticated_url if with_credentials else repository.url
)
indexes_header += "--extra-index-url {}\n".format(url)
content = indexes_header + "\n" + content
self._output(content, cwd, output)
def _output(
......
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