-
Notifications
You must be signed in to change notification settings - Fork 18
/
poetry.py
114 lines (91 loc) · 3.15 KB
/
poetry.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
"""Poetry interface."""
from enum import Enum
from pathlib import Path
from typing import List
from typing import Optional
import tomlkit
from nox.sessions import Session
class DistributionFormat(str, Enum):
"""Type of distribution archive for a Python package."""
WHEEL = "wheel"
SDIST = "sdist"
class Config:
"""Poetry configuration."""
def __init__(self, project: Path) -> None:
"""Initialize."""
path = project / "pyproject.toml"
text = path.read_text(encoding="utf-8")
data = tomlkit.parse(text)
self._config = data["tool"]["poetry"]
@property
def name(self) -> str:
"""Return the package name."""
name = self._config["name"]
assert isinstance(name, str) # noqa: S101
return name
@property
def extras(self) -> List[str]:
"""Return the package extras."""
extras = self._config.get("extras", {})
assert isinstance(extras, dict) and all( # noqa: S101
isinstance(extra, str) for extra in extras
)
return list(extras)
class Poetry:
"""Helper class for invoking Poetry inside a Nox session.
Attributes:
session: The Session object.
"""
def __init__(self, session: Session) -> None:
"""Initialize."""
self.session = session
self._config: Optional[Config] = None
@property
def config(self) -> Config:
"""Return the package configuration."""
if self._config is None:
self._config = Config(Path.cwd())
return self._config
def export(self, path: Path) -> None:
"""Export the lock file to requirements format.
Args:
path: The destination path.
"""
self.session.run(
"poetry",
"export",
"--format=requirements.txt",
f"--output={path}",
"--dev",
*[f"--extras={extra}" for extra in self.config.extras],
"--without-hashes",
external=True,
)
def build(self, *, format: str) -> str:
"""Build the package.
The filename of the archive is extracted from the output Poetry writes
to standard output, which currently looks like this::
Building foobar (0.1.0)
- Building wheel
- Built foobar-0.1.0-py3-none-any.whl
This is brittle, but it has the advantage that it does not rely on
assumptions such as having a clean ``dist`` directory, or
reconstructing the filename from the package metadata. (Poetry does not
use PEP 440 for version numbers, so this is non-trivial.)
Args:
format: The distribution format, either wheel or sdist.
Returns:
The basename of the wheel built by Poetry.
"""
if not isinstance(format, DistributionFormat):
format = DistributionFormat(format)
output = self.session.run(
"poetry",
"build",
f"--format={format}",
external=True,
silent=True,
stderr=None,
)
assert isinstance(output, str) # noqa: S101
return output.split()[-1]