Skip to content

Commit a7bac7b

Browse files
committed
implement rest of HatchlingProject
1 parent 49eb16a commit a7bac7b

File tree

4 files changed

+66
-22
lines changed

4 files changed

+66
-22
lines changed

plux/build/discovery.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ class SimplePackageFinder(PackageFinder):
8585
everything in the preceding path that's not a package.
8686
"""
8787

88+
DEFAULT_EXCLUDES = "__pycache__"
89+
8890
def __init__(self, path: str):
8991
self._path = path
9092

@@ -130,6 +132,10 @@ def find_packages(self) -> t.Iterable[str]:
130132
# If we're at the root and it's a package, use the directory name
131133
rel_path = os.path.basename(abs_path)
132134

135+
# skip excludes TODO: should re-use Filter API
136+
if os.path.basename(rel_path).strip(os.pathsep) in self.DEFAULT_EXCLUDES:
137+
continue
138+
133139
# Skip invalid package names (those containing dots in the path)
134140
if "." in os.path.basename(rel_path):
135141
continue

plux/build/hatchling.py

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import logging
22
import os
3+
import sys
34
import typing as t
45
from pathlib import Path
56

@@ -13,20 +14,11 @@
1314
LOG = logging.getLogger(__name__)
1415

1516

16-
def _path_to_module(path):
17-
"""
18-
Convert a path to a Python module to its module representation.
19-
20-
Example: plux/core/test -> plux.core.test
21-
"""
22-
return ".".join(Path(path).with_suffix("").parts)
23-
24-
2517
class HatchlingPackageFinder(PackageFinder):
2618
"""
2719
Uses hatchling's BuilderConfig abstraction to enumerate packages.
2820
29-
TODO: this might not be 100% correct and needs more thorough testing with different scenarios.
21+
TODO: include/exclude configuration of packages in hatch needs more thorough testing with different scenarios.
3022
"""
3123

3224
builder_config: BuilderConfig
@@ -111,27 +103,44 @@ def __init__(self, workdir: str = None):
111103
"Hatchling integration currently only works with entrypoint_build_mode=manual"
112104
)
113105

114-
# FIXME it's unclear whether this will really hold for all configs. most configs we assume will build a wheel,
115-
# and any associated package configuration will come from there. so currently we make the wheel build config
116-
# the source of truth, but we should revisit this once we know more about hatch build configurations.
106+
# we assume that a wheel will be the source of truth for the packages finally ending up in the distribution.
107+
# therefore we care foremost about the wheel configuration. this also builds on the assumption that building
108+
# the wheel from the local sources, and the sdist, will be the same.
117109
self.builder = WheelBuilder(workdir)
118110

111+
@property
112+
def hatchling_config(self) -> BuilderConfig:
113+
return self.builder.config
114+
119115
def create_package_finder(self) -> PackageFinder:
120116
return HatchlingPackageFinder(
121117
self.hatchling_config,
122118
exclude=self.config.exclude,
123119
include=self.config.include,
124120
)
125121

126-
@property
127-
def hatchling_config(self) -> BuilderConfig:
128-
return self.builder.config
129-
130122
def find_plux_index_file(self) -> Path:
131123
# TODO: extend as soon as we support EntryPointBuildMode = build-hook
132124
return Path(self.hatchling_config.root, self.config.entrypoint_static_file)
133125

134126
def find_entry_point_file(self) -> Path:
135-
# TODO: we'll assume that `pip install -e .` is used, and therefore the entrypoints file will be in the
136-
# .dist-info metadata directory
137-
raise NotImplementedError
127+
# we assume that `pip install -e .` is used, and therefore the entrypoints file used during local execution
128+
# will be in the .dist-info metadata directory in the sys path
129+
metadata_dir = f"{self.builder.artifact_project_id}.dist-info"
130+
131+
for path in sys.path:
132+
metadata_path = os.path.join(path, metadata_dir)
133+
if not os.path.exists(metadata_path):
134+
continue
135+
136+
return Path(metadata_path) / "entry_points.txt"
137+
138+
raise FileNotFoundError(f"No metadata found for {self.builder.artifact_project_id} in sys path")
139+
140+
def build_entrypoints(self):
141+
# TODO: currently this just replicates the manual build mode.
142+
path = os.path.join(os.getcwd(), self.config.entrypoint_static_file)
143+
print(f"discovering plugins and writing to {path} ...")
144+
builder = self.create_plugin_index_builder()
145+
with open(path, "w") as fd:
146+
builder.write(fd, output_format="ini")

plux/core/entrypoint.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def discover_entry_points(finder: PluginFinder) -> EntryPointDict:
3434
return to_entry_point_dict([spec_to_entry_point(spec) for spec in finder.find_plugins()])
3535

3636

37-
def to_entry_point_dict(eps: list[EntryPoint]) -> EntryPointDict:
37+
def to_entry_point_dict(eps: t.Iterable[EntryPoint]) -> EntryPointDict:
3838
"""
3939
Convert the list of EntryPoint objects to a dictionary that maps entry point groups to their respective list of
4040
``name=value`` entry points. Each pair is represented as a string.

tests/cli/test_hatch.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import os.path
22
import sys
3+
from pathlib import Path
34

45

56
def test_discover_with_ini_output(tmp_path):
@@ -27,7 +28,7 @@ def test_discover_with_ini_output(tmp_path):
2728
]
2829

2930

30-
def test_discover_with_ini_output_namespace_Package(tmp_path):
31+
def test_discover_with_ini_output_namespace_package(tmp_path):
3132
from plux.__main__ import main
3233

3334
project = os.path.join(os.path.dirname(__file__), "projects", "hatch", "namespace_package")
@@ -50,3 +51,31 @@ def test_discover_with_ini_output_namespace_Package(tmp_path):
5051
"mynestedplugin = test_project.subpkg.plugins:MyNestedPlugin",
5152
"myplugin = test_project.plugins:MyPlugin",
5253
]
54+
55+
56+
def test_entrypoints_manual_build_mode():
57+
from plux.__main__ import main
58+
59+
project = os.path.join(os.path.dirname(__file__), "projects", "hatch", "namespace_package")
60+
os.chdir(project)
61+
62+
index_file = Path(project, "plux.ini")
63+
index_file.unlink(missing_ok=True)
64+
65+
sys.path.append(project)
66+
try:
67+
try:
68+
main(["--workdir", project, "entrypoints"])
69+
except SystemExit:
70+
pass
71+
finally:
72+
sys.path.remove(project)
73+
74+
assert index_file.exists()
75+
76+
lines = index_file.read_text().strip().splitlines()
77+
assert lines == [
78+
"[plux.test.plugins]",
79+
"mynestedplugin = test_project.subpkg.plugins:MyNestedPlugin",
80+
"myplugin = test_project.plugins:MyPlugin",
81+
]

0 commit comments

Comments
 (0)