-
-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Hatch build hook for Polylith (#153)
* feat(hatch): add hatch build hook for parsing project bricks * fix(cli): correct create project input args * wip(hatch): add build hook project * refactor: extract into new toml brick * fix: check build system (i.e. poetry or hatch) by looking in the build-backend config * refactor: rename hatch and cli project folders * docs: add docs about the Hatch build hook * bump poetry plugin to 1.14.4 * bump cli to 0.5.0
- Loading branch information
1 parent
007a610
commit 29f49c0
Showing
32 changed files
with
592 additions
and
89 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
from hatchling.plugin import hookimpl | ||
|
||
from polylith.hatch.hooks.bricks import PolylithBricksHook | ||
|
||
|
||
@hookimpl | ||
def hatch_register_build_hook(): | ||
return PolylithBricksHook |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from polylith.hatch import hooks | ||
|
||
__all__ = ["hooks"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
from pathlib import Path | ||
from typing import List, Union | ||
|
||
from polylith import parsing | ||
|
||
|
||
def get_work_dir(config: dict) -> Path: | ||
work_dir = config.get("work-dir", ".polylith_tmp") | ||
|
||
return Path(work_dir) | ||
|
||
|
||
def parse_namespace(bricks: dict) -> str: | ||
namespaces = parsing.parse_brick_namespace_from_path(bricks) | ||
|
||
return next(namespace for namespace in namespaces) | ||
|
||
|
||
def copy_brick(source: str, brick: str, tmp_dir: Path) -> Path: | ||
destination = Path(tmp_dir / brick).as_posix() | ||
|
||
return parsing.copy_brick(source, destination) | ||
|
||
|
||
def rewrite_module(module: Path, ns: str, top_ns: str) -> Union[str, None]: | ||
was_rewritten = parsing.rewrite_module(module, ns, top_ns) | ||
|
||
return f"{module.parent.name}/{module.name}" if was_rewritten else None | ||
|
||
|
||
def rewrite_modules(path: Path, ns: str, top_ns: str) -> List[str]: | ||
"""Rewrite modules in bricks with new top namespace | ||
returns a list of bricks that was rewritten | ||
""" | ||
|
||
modules = path.glob("**/*.py") | ||
|
||
res = [rewrite_module(module, ns, top_ns) for module in modules] | ||
|
||
return [r for r in res if r] |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import shutil | ||
from pathlib import Path | ||
from typing import Any, Dict | ||
|
||
from hatchling.builders.hooks.plugin.interface import BuildHookInterface | ||
from polylith import repo, toml | ||
from polylith.hatch import core | ||
|
||
|
||
class PolylithBricksHook(BuildHookInterface): | ||
PLUGIN_NAME = "polylith-bricks" | ||
|
||
def initialize(self, _version: str, build_data: Dict[str, Any]) -> None: | ||
top_ns = self.config.get("top-namespace") | ||
work_dir = core.get_work_dir(self.config) | ||
pyproject = Path(f"{self.root}/{repo.default_toml}") | ||
|
||
print(f"Using {pyproject.as_posix()}.") | ||
|
||
data = toml.read_toml_document(pyproject) | ||
bricks = toml.get_project_packages_from_polylith_section(data) | ||
|
||
if not bricks: | ||
print("No bricks found.") | ||
return | ||
|
||
if not top_ns: | ||
build_data["force_include"] = bricks | ||
return | ||
|
||
ns = core.parse_namespace(bricks) | ||
|
||
for source, brick in bricks.items(): | ||
path = core.copy_brick(source, brick, work_dir) | ||
rewritten_bricks = core.rewrite_modules(path, ns, top_ns) | ||
|
||
for item in rewritten_bricks: | ||
print(f"Updated {item} with new top namespace for local imports.") | ||
|
||
key = work_dir.as_posix() | ||
build_data["force_include"][key] = top_ns | ||
|
||
def finalize(self, *args, **kwargs) -> None: | ||
work_dir = core.get_work_dir(self.config) | ||
|
||
if not work_dir.exists() or not work_dir.is_dir(): | ||
return | ||
|
||
shutil.rmtree(work_dir.as_posix()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from polylith.parsing.core import copy_brick, parse_brick_namespace_from_path | ||
from polylith.parsing.rewrite import rewrite_module | ||
|
||
__all__ = ["copy_brick", "parse_brick_namespace_from_path", "rewrite_module"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import shutil | ||
from pathlib import Path | ||
|
||
|
||
def copy_brick(source: str, destination: str) -> Path: | ||
ignore = shutil.ignore_patterns( | ||
"*.pyc", | ||
"__pycache__", | ||
".venv", | ||
".mypy_cache", | ||
".pytest_cache", | ||
"node_modules", | ||
".git", | ||
) | ||
|
||
res = shutil.copytree(source, destination, ignore=ignore, dirs_exist_ok=True) | ||
|
||
return Path(res) | ||
|
||
|
||
def parse_brick_namespace_from_path(bricks: dict) -> set: | ||
parts = {str.split(v, "/")[0] for v in bricks.values()} | ||
|
||
return parts |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import ast | ||
from pathlib import Path | ||
|
||
|
||
def create_namespace_path(top_ns: str, current: str) -> str: | ||
top_ns_module_path = top_ns.replace("/", ".") | ||
return f"{top_ns_module_path}.{current}" | ||
|
||
|
||
def mutate_import(node: ast.Import, ns: str, top_ns: str) -> bool: | ||
did_mutate = False | ||
|
||
for alias in node.names: | ||
if alias.name == ns: | ||
alias.name = create_namespace_path(top_ns, alias.name) | ||
did_mutate = True | ||
|
||
return did_mutate | ||
|
||
|
||
def mutate_import_from(node: ast.ImportFrom, ns: str, top_ns: str) -> bool: | ||
did_mutate = False | ||
|
||
if not node.module or node.level != 0: | ||
return did_mutate | ||
|
||
if node.module == ns or node.module.startswith(f"{ns}."): | ||
node.module = create_namespace_path(top_ns, node.module) | ||
did_mutate = True | ||
|
||
return did_mutate | ||
|
||
|
||
def mutate_imports(node: ast.AST, ns: str, top_ns: str) -> bool: | ||
if isinstance(node, ast.Import): | ||
return mutate_import(node, ns, top_ns) | ||
|
||
if isinstance(node, ast.ImportFrom): | ||
return mutate_import_from(node, ns, top_ns) | ||
|
||
return False | ||
|
||
|
||
def rewrite_module(source: Path, ns: str, top_ns: str) -> bool: | ||
file_path = source.as_posix() | ||
|
||
with open(file_path, "r", encoding="utf-8") as f: | ||
tree = ast.parse(f.read(), source.name) | ||
|
||
res = {mutate_imports(node, ns, top_ns) for node in ast.walk(tree)} | ||
|
||
if True in res: | ||
rewritten_source_code = ast.unparse(tree) # type: ignore[attr-defined] | ||
|
||
with open(file_path, "w", encoding="utf-8", newline="") as f: | ||
f.write(rewritten_source_code) | ||
|
||
return True | ||
|
||
return False |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from polylith.toml.core import ( | ||
get_project_dependencies, | ||
get_project_package_includes, | ||
get_project_packages_from_polylith_section, | ||
read_toml_document, | ||
) | ||
|
||
__all__ = [ | ||
"get_project_dependencies", | ||
"get_project_package_includes", | ||
"get_project_packages_from_polylith_section", | ||
"read_toml_document", | ||
] |
Oops, something went wrong.