Skip to content

Commit c7b924b

Browse files
hoeflingabn
authored andcommitted
Signed-off-by: oleg.hoefling <[email protected]>
1 parent 6d0f8fd commit c7b924b

File tree

21 files changed

+148
-5
lines changed

21 files changed

+148
-5
lines changed

poetry/core/masonry/utils/package_include.py

+18-5
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,21 @@ def refresh(self): # type: () -> PackageInclude
3333

3434
return self.check_elements()
3535

36+
def is_stub_only(self): # type: () -> bool
37+
# returns `True` if this a PEP 561 stub-only package,
38+
# see [PEP 561](https://www.python.org/dev/peps/pep-0561/#stub-only-packages)
39+
return self.package.endswith("-stubs") and all(
40+
el.suffix == ".pyi"
41+
or (el.parent.name == self.package and el.name == "py.typed")
42+
for el in self.elements
43+
if el.is_file()
44+
)
45+
46+
def has_modules(self): # type: () -> bool
47+
# Packages no longer need an __init__.py in python3, but there must
48+
# at least be one .py file for it to be considered a package
49+
return any(element.suffix == ".py" for element in self.elements)
50+
3651
def check_elements(self): # type: () -> PackageInclude
3752
if not self._elements:
3853
raise ValueError(
@@ -43,20 +58,18 @@ def check_elements(self): # type: () -> PackageInclude
4358
if len(self._elements) > 1:
4459
# Probably glob
4560
self._is_package = True
61+
self._package = root.parent.name
4662

47-
# Packages no longer need an __init__.py in python3, but there must
48-
# at least be one .py file for it to be considered a package
49-
if not any([element.suffix == ".py" for element in self._elements]):
63+
if not self.is_stub_only() and not self.has_modules():
5064
raise ValueError("{} is not a package.".format(root.name))
5165

52-
self._package = root.parent.name
5366
else:
5467
if root.is_dir():
5568
# If it's a directory, we include everything inside it
5669
self._package = root.name
5770
self._elements = sorted(list(root.glob("**/*")))
5871

59-
if not any([element.suffix == ".py" for element in self._elements]):
72+
if not self.is_stub_only() and not self.has_modules():
6073
raise ValueError("{} is not a package.".format(root.name))
6174

6275
self._is_package = True

tests/masonry/builders/fixtures/pep_561_stub_only/pkg-stubs/__init__.pyi

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"""Example module"""
2+
from typing import Tuple
3+
4+
version_info = Tuple[int, int, int]

tests/masonry/builders/fixtures/pep_561_stub_only/pkg-stubs/subpkg/__init__.pyi

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[tool.poetry]
2+
name = "pep-561-stubs"
3+
version = "0.1"
4+
description = "PEP 561 stub package example"
5+
authors = [
6+
"Oleg Höfling <[email protected]>"
7+
]
8+
license = "MIT"
9+
packages = [
10+
{include = "pkg-stubs"}
11+
]
12+
13+
[tool.poetry.dependencies]
14+
python = "^3.6"

tests/masonry/builders/fixtures/pep_561_stub_only_partial/pkg-stubs/__init__.pyi

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"""Example module"""
2+
from typing import Tuple
3+
4+
version_info = Tuple[int, int, int]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
partial

tests/masonry/builders/fixtures/pep_561_stub_only_partial/pkg-stubs/subpkg/__init__.pyi

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[tool.poetry]
2+
name = "pep-561-stubs"
3+
version = "0.1"
4+
description = "PEP 561 stub package example with the py.typed marker file"
5+
authors = [
6+
"Oleg Höfling <[email protected]>"
7+
]
8+
license = "MIT"
9+
packages = [
10+
{include = "pkg-stubs"}
11+
]
12+
13+
[tool.poetry.dependencies]
14+
python = "^3.6"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[tool.poetry]
2+
name = "pep-561-stubs"
3+
version = "0.1"
4+
description = "PEP 561 stub package example with an src layout"
5+
authors = [
6+
"Oleg Höfling <[email protected]>"
7+
]
8+
license = "MIT"
9+
packages = [
10+
{include = "pkg-stubs", from = "src"}
11+
]
12+
13+
[tool.poetry.dependencies]
14+
python = "^3.6"

tests/masonry/builders/fixtures/pep_561_stub_only_src/src/pkg-stubs/__init__.pyi

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"""Example module"""
2+
from typing import Tuple
3+
4+
version_info = Tuple[int, int, int]

tests/masonry/builders/fixtures/pep_561_stub_only_src/src/pkg-stubs/subpkg/__init__.pyi

Whitespace-only changes.

tests/masonry/builders/test_sdist.py

+18
Original file line numberDiff line numberDiff line change
@@ -506,3 +506,21 @@ def test_excluded_subpackage():
506506
exec(compile(setup_ast, filename="setup.py", mode="exec"), ns)
507507

508508
assert ns["packages"] == ["example"]
509+
510+
511+
def test_sdist_package_pep_561_stub_only():
512+
root = fixtures_dir / "pep_561_stub_only"
513+
poetry = Factory().create_poetry(root)
514+
515+
builder = SdistBuilder(poetry)
516+
builder.build()
517+
518+
sdist = root / "dist" / "pep-561-stubs-0.1.tar.gz"
519+
520+
assert sdist.exists()
521+
522+
with tarfile.open(str(sdist), "r") as tar:
523+
names = tar.getnames()
524+
assert "pep-561-stubs-0.1/pkg-stubs/__init__.pyi" in names
525+
assert "pep-561-stubs-0.1/pkg-stubs/module.pyi" in names
526+
assert "pep-561-stubs-0.1/pkg-stubs/subpkg/__init__.pyi" in names

tests/masonry/builders/test_wheel.py

+30
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,33 @@ def test_dist_info_file_permissions():
155155
z.getinfo("my_package-1.2.3.dist-info/entry_points.txt").external_attr
156156
== 0o644 << 16
157157
)
158+
159+
160+
@pytest.mark.parametrize(
161+
"package",
162+
["pep_561_stub_only", "pep_561_stub_only_partial", "pep_561_stub_only_src"],
163+
)
164+
def test_wheel_package_pep_561_stub_only(package):
165+
root = fixtures_dir / package
166+
WheelBuilder.make(Factory().create_poetry(root))
167+
168+
whl = root / "dist" / "pep_561_stubs-0.1-py3-none-any.whl"
169+
170+
assert whl.exists()
171+
172+
with zipfile.ZipFile(str(whl)) as z:
173+
assert "pkg-stubs/__init__.pyi" in z.namelist()
174+
assert "pkg-stubs/module.pyi" in z.namelist()
175+
assert "pkg-stubs/subpkg/__init__.pyi" in z.namelist()
176+
177+
178+
def test_wheel_package_pep_561_stub_only_includes_typed_marker():
179+
root = fixtures_dir / "pep_561_stub_only_partial"
180+
WheelBuilder.make(Factory().create_poetry(root))
181+
182+
whl = root / "dist" / "pep_561_stubs-0.1-py3-none-any.whl"
183+
184+
assert whl.exists()
185+
186+
with zipfile.ZipFile(str(whl)) as z:
187+
assert "pkg-stubs/py.typed" in z.namelist()

tests/masonry/utils/fixtures/pep_561_stub_only/bad/__init__.pyi

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""Example module"""
2+
from typing import Tuple
3+
4+
5+
version_info = Tuple[int, int, int]

tests/masonry/utils/fixtures/pep_561_stub_only/good-stubs/__init__.pyi

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""Example module"""
2+
from typing import Tuple
3+
4+
5+
version_info = Tuple[int, int, int]

tests/masonry/utils/test_package_include.py

+17
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,20 @@ def test_package_include_with_non_existent_directory():
5050
err_str = str(with_includes / "not_a_dir") + " does not contain any element"
5151

5252
assert str(e.value) == err_str
53+
54+
55+
def test_pep_561_stub_only_package_good_name_suffix():
56+
pkg_include = PackageInclude(
57+
base=fixtures_dir / "pep_561_stub_only", include="good-stubs"
58+
)
59+
assert pkg_include.elements == [
60+
fixtures_dir / "pep_561_stub_only/good-stubs/__init__.pyi",
61+
fixtures_dir / "pep_561_stub_only/good-stubs/module.pyi",
62+
]
63+
64+
65+
def test_pep_561_stub_only_package_bad_name_suffix():
66+
with pytest.raises(ValueError) as e:
67+
PackageInclude(base=fixtures_dir / "pep_561_stub_only", include="bad")
68+
69+
assert str(e.value) == "bad is not a package."

0 commit comments

Comments
 (0)