Skip to content

Commit 96250e2

Browse files
committed
Add tests for Poetry requirements parsing
1 parent 2cb028d commit 96250e2

7 files changed

Lines changed: 445 additions & 11 deletions

File tree

micropipenv.py

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,13 @@
4747
]
4848

4949
import argparse
50-
import logging
51-
import sys
50+
import hashlib
5251
import json
52+
import logging
5353
import os
54-
import hashlib
54+
import re
5555
import subprocess
56+
import sys
5657
import tempfile
5758
from collections import deque
5859
from itertools import chain
@@ -449,13 +450,26 @@ def _maybe_print_pip_freeze(pip_bin): # type: (str) -> None
449450
_LOGGER.warning("Failed to perform pip freeze to check installed dependencies, the error is not fatal")
450451

451452

452-
def _poetry2pipfile_lock(): # type: () -> Dict[str, Any]
453+
def _translate_poetry_dependency(info): # type: (str) -> str
454+
"""Translate Poetry dependency specification as written in pyproject.toml into its Pipfile.lock equivalent."""
455+
if isinstance(info, str) and re.match(r"^\d", info):
456+
return "=={}".format(info)
457+
458+
# TODO: poetry uses version like ^0.10.4 that are not Pipfile.lock complaint.
459+
return info
460+
461+
462+
def _poetry2pipfile_lock(
463+
only_direct=False, no_default=False, no_dev=False
464+
): # type: (bool, bool, bool) -> Dict[str, Any]
453465
"""Convert Poetry files to Pipfile.lock as Pipenv would produce."""
454466
poetry_lock, pyproject_toml = _read_poetry()
455467

468+
pyproject_poetry_section = pyproject_toml.get("tool", {}).get("poetry", {})
469+
456470
sources = []
457471
has_default = False # If default flag is set, it disallows PyPI.
458-
for item in pyproject_toml.get("tool", {}).get("poetry", {}).get("source", []):
472+
for item in pyproject_poetry_section.get("source", []):
459473
sources.append(
460474
{"name": item["name"], "url": item["url"], "verify_ssl": True}
461475
)
@@ -473,6 +487,27 @@ def _poetry2pipfile_lock(): # type: () -> Dict[str, Any]
473487

474488
default = {}
475489
develop = {}
490+
491+
if only_direct:
492+
if not no_default:
493+
for dependency_name, info in pyproject_poetry_section.get("dependencies", {}).items():
494+
default[dependency_name] = _translate_poetry_dependency(info)
495+
496+
if not no_dev:
497+
for dependency_name, info in pyproject_poetry_section.get("dev-dependencies", {}).items():
498+
develop[dependency_name] = _translate_poetry_dependency(info)
499+
500+
return {
501+
"_meta": {
502+
"hash": {"sha256": poetry_lock["metadata"]["content-hash"]},
503+
"pipfile-spec": 6,
504+
"sources": sources,
505+
"requires": {"python_version": "{}.{}".format(sys.version_info.major, sys.version_info.minor)},
506+
},
507+
"default": default,
508+
"develop": develop,
509+
}
510+
476511
for entry in poetry_lock["package"]:
477512
hashes = []
478513
for file_entry in poetry_lock["metadata"]["files"][entry["name"]]:
@@ -521,9 +556,11 @@ def _poetry2pipfile_lock(): # type: () -> Dict[str, Any]
521556
requirement["extras"] = sorted(requirement["extras"])
522557

523558
if entry["category"] == "main":
524-
default[entry["name"]] = requirement
559+
if not no_default:
560+
default[entry["name"]] = requirement
525561
elif entry["category"] == "dev":
526-
develop[entry["name"]] = requirement
562+
if not no_dev:
563+
develop[entry["name"]] = requirement
527564
else:
528565
raise PoetryError("Unknown category for package {}: {}".format(entry["name"], entry["category"]))
529566

@@ -641,12 +678,16 @@ def _parse_pipfile_dependency_info(pipfile_entry): # type: (Union[str, Dict[str
641678

642679
def get_requirements_sections(
643680
*, pipfile=None, pipfile_lock=None, no_indexes=False, only_direct=False, no_default=False, no_dev=False
644-
): # type: (Optional[Dict[str, Any]], Optional[Dict[str, Any]], bool, bool, bool, bool) -> Dict[str, Dict[str, Any]]
681+
): # type: (Optional[Dict[str, Any]], Optional[Dict[str, Any]], bool, bool, bool, bool) -> Dict[str, Any]
645682
"""Compute requirements of an application, the output generated is compatible with pip-tools."""
646683
if no_dev and no_default:
647684
raise ArgumentsError("Cannot produce requirements as both, default and dev were asked to be discarded")
648685

649-
result = {}
686+
result = {
687+
"default": {},
688+
"develop": {},
689+
"sources": [],
690+
} # type: Dict[str, Any]
650691

651692
if only_direct:
652693
pipfile = pipfile or _read_pipfile()
@@ -777,7 +818,7 @@ def requirements(
777818
no_indexes=no_indexes, only_direct=only_direct, no_default=no_default, no_dev=no_dev
778819
)
779820
elif method == "poetry":
780-
sections = _poetry2pipfile_lock()
821+
sections = _poetry2pipfile_lock(only_direct=only_direct, no_default=no_default, no_dev=no_dev)
781822
else:
782823
raise MicropipenvException("Unhandled method for installing requirements: {}".format(method))
783824

@@ -826,7 +867,7 @@ def main(argv=None): # type: (Optional[List[str]]) -> int
826867
"--method",
827868
help="Source of packages for the installation, perform detection if not provided.",
828869
choices=["pipenv", "requirements", "poetry"],
829-
default=os.getenv("MICROPIPENV_METHOD", "pipenv")
870+
default=os.getenv("MICROPIPENV_METHOD")
830871
)
831872
parser_install.add_argument(
832873
"pip_args",

tests/data/parse/pip-tools/Pipfile.lock

Lines changed: 49 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/data/parse/pipenv/Pipfile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[[source]]
2+
name = "pypi"
3+
url = "https://pypi.org/simple"
4+
verify_ssl = true
5+
6+
[dev-packages]
7+
flexmock = "*"
8+
9+
[packages]
10+
daiquiri = "==2.0.0"
11+
12+
[requires]
13+
python_version = "3.7"

tests/data/parse/pipenv/Pipfile.lock

Lines changed: 44 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/data/parse/poetry2/poetry.lock

Lines changed: 46 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[tool.poetry]
2+
name = "aaa"
3+
version = "0.1.0"
4+
description = ""
5+
authors = ["Fridolin Pokorny <fridolin@redhat.com>"]
6+
7+
[tool.poetry.dependencies]
8+
daiquiri = "2.0.0"
9+
10+
[tool.poetry.dev-dependencies]
11+
flexmock = "^0.10.4"
12+
13+
[build-system]
14+
requires = ["poetry>=0.12"]
15+
build-backend = "poetry.masonry.api"

0 commit comments

Comments
 (0)