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