Skip to content

Commit

Permalink
Fix regression in stub-only package (PEP-561) support
Browse files Browse the repository at this point in the history
Signed-off-by: oleg.hoefling <oleg.hoefling@gmail.com>
  • Loading branch information
hoefling authored Apr 22, 2020
1 parent dbc1e1b commit 9a28396
Show file tree
Hide file tree
Showing 21 changed files with 148 additions and 5 deletions.
23 changes: 18 additions & 5 deletions poetry/masonry/utils/package_include.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,21 @@ def refresh(self): # type: () -> PackageInclude

return self.check_elements()

def is_stub_only(self): # type: () -> bool
# returns `True` if this a PEP 561 stub-only package,
# see [PEP 561](https://www.python.org/dev/peps/pep-0561/#stub-only-packages)
return self.package.endswith("-stubs") and all(
el.suffix == ".pyi"
or (el.parent.name == self.package and el.name == "py.typed")
for el in self.elements
if el.is_file()
)

def has_modules(self): # type: () -> bool
# Packages no longer need an __init__.py in python3, but there must
# at least be one .py file for it to be considered a package
return any(element.suffix == ".py" for element in self.elements)

def check_elements(self): # type: () -> PackageInclude
if not self._elements:
raise ValueError(
Expand All @@ -43,20 +58,18 @@ def check_elements(self): # type: () -> PackageInclude
if len(self._elements) > 1:
# Probably glob
self._is_package = True
self._package = root.parent.name

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

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

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

self._is_package = True
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"""Example module"""
from typing import Tuple

version_info = Tuple[int, int, int]
Empty file.
14 changes: 14 additions & 0 deletions tests/masonry/builders/fixtures/pep_561_stub_only/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[tool.poetry]
name = "pep-561-stubs"
version = "0.1"
description = "PEP 561 stub package example"
authors = [
"Oleg Höfling <oleg.hoefling@gmail.com>"
]
license = "MIT"
packages = [
{include = "pkg-stubs"}
]

[tool.poetry.dependencies]
python = "^3.6"
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"""Example module"""
from typing import Tuple

version_info = Tuple[int, int, int]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
partial
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[tool.poetry]
name = "pep-561-stubs"
version = "0.1"
description = "PEP 561 stub package example with the py.typed marker file"
authors = [
"Oleg Höfling <oleg.hoefling@gmail.com>"
]
license = "MIT"
packages = [
{include = "pkg-stubs"}
]

[tool.poetry.dependencies]
python = "^3.6"
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[tool.poetry]
name = "pep-561-stubs"
version = "0.1"
description = "PEP 561 stub package example with an src layout"
authors = [
"Oleg Höfling <oleg.hoefling@gmail.com>"
]
license = "MIT"
packages = [
{include = "pkg-stubs", from = "src"}
]

[tool.poetry.dependencies]
python = "^3.6"
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"""Example module"""
from typing import Tuple

version_info = Tuple[int, int, int]
Empty file.
18 changes: 18 additions & 0 deletions tests/masonry/builders/test_sdist.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,3 +488,21 @@ def test_excluded_subpackage():
exec(compile(setup_ast, filename="setup.py", mode="exec"), ns)

assert ns["packages"] == ["example"]


def test_sdist_package_pep_561_stub_only():
root = fixtures_dir / "pep_561_stub_only"
poetry = Factory().create_poetry(root)

builder = SdistBuilder(poetry, NullEnv(), NullIO())
builder.build()

sdist = root / "dist" / "pep-561-stubs-0.1.tar.gz"

assert sdist.exists()

with tarfile.open(str(sdist), "r") as tar:
names = tar.getnames()
assert "pep-561-stubs-0.1/pkg-stubs/__init__.pyi" in names
assert "pep-561-stubs-0.1/pkg-stubs/module.pyi" in names
assert "pep-561-stubs-0.1/pkg-stubs/subpkg/__init__.pyi" in names
30 changes: 30 additions & 0 deletions tests/masonry/builders/test_wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,33 @@ def test_dist_info_file_permissions():
z.getinfo("my_package-1.2.3.dist-info/entry_points.txt").external_attr
== 0o644 << 16
)


@pytest.mark.parametrize(
"package",
["pep_561_stub_only", "pep_561_stub_only_partial", "pep_561_stub_only_src"],
)
def test_wheel_package_pep_561_stub_only(package):
root = fixtures_dir / package
WheelBuilder.make(Factory().create_poetry(root), NullEnv(), NullIO())

whl = root / "dist" / "pep_561_stubs-0.1-py3-none-any.whl"

assert whl.exists()

with zipfile.ZipFile(str(whl)) as z:
assert "pkg-stubs/__init__.pyi" in z.namelist()
assert "pkg-stubs/module.pyi" in z.namelist()
assert "pkg-stubs/subpkg/__init__.pyi" in z.namelist()


def test_wheel_package_pep_561_stub_only_includes_typed_marker():
root = fixtures_dir / "pep_561_stub_only_partial"
WheelBuilder.make(Factory().create_poetry(root), NullEnv(), NullIO())

whl = root / "dist" / "pep_561_stubs-0.1-py3-none-any.whl"

assert whl.exists()

with zipfile.ZipFile(str(whl)) as z:
assert "pkg-stubs/py.typed" in z.namelist()
Empty file.
5 changes: 5 additions & 0 deletions tests/masonry/utils/fixtures/pep_561_stub_only/bad/module.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""Example module"""
from typing import Tuple


version_info = Tuple[int, int, int]
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""Example module"""
from typing import Tuple


version_info = Tuple[int, int, int]
17 changes: 17 additions & 0 deletions tests/masonry/utils/test_package_include.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,20 @@ def test_package_include_with_non_existent_directory():
err_str = str(with_includes / "not_a_dir") + " does not contain any element"

assert str(e.value) == err_str


def test_pep_561_stub_only_package_good_name_suffix():
pkg_include = PackageInclude(
base=fixtures_dir / "pep_561_stub_only", include="good-stubs"
)
assert pkg_include.elements == [
fixtures_dir / "pep_561_stub_only/good-stubs/__init__.pyi",
fixtures_dir / "pep_561_stub_only/good-stubs/module.pyi",
]


def test_pep_561_stub_only_package_bad_name_suffix():
with pytest.raises(ValueError) as e:
PackageInclude(base=fixtures_dir / "pep_561_stub_only", include="bad")

assert str(e.value) == "bad is not a package."

0 comments on commit 9a28396

Please sign in to comment.