makepatch 0.0.1__py3-none-any.whl → 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
makepatch/__hooks__.py ADDED
@@ -0,0 +1,7 @@
1
+ from hatchling.plugin import hookimpl
2
+ from makepatch.hooks import PatcherBuildHook
3
+
4
+
5
+ @hookimpl
6
+ def hatch_register_build_hook():
7
+ return PatcherBuildHook
makepatch/__init__.py CHANGED
@@ -0,0 +1,2 @@
1
+ from .__hook__ import hatch_register_build_hook
2
+ from .hooks import PatcherBuildHook
makepatch/_config.py CHANGED
@@ -1,18 +1,59 @@
1
+ import os
1
2
  from collections.abc import Iterable
2
- from typing import final, Annotated
3
+ from pathlib import Path
4
+ from typing import final, Annotated, ClassVar
3
5
 
4
6
  from pydantic import BaseModel, Field
7
+ from pydantic.json_schema import SkipJsonSchema
5
8
 
9
+ if __debug__ and __import__("typing").TYPE_CHECKING:
10
+
11
+ from makepatch._types.hatch import HatchHookInterface
6
12
 
7
- __config = None
8
13
 
9
14
  @final
10
- class ClippyConfig(BaseModel):
15
+ class PatcherConfig(BaseModel):
16
+ __INTERNAL: ClassVar[object] = object()
17
+ __INSTANCE: ClassVar[PatcherConfig | None] = None
18
+
11
19
  module_source: Annotated[str, Field(alias="module-source")]
12
20
  excludes: Annotated[Iterable[str], Field(default_factory=frozenset)]
13
- dev_excludes: Annotated[Iterable[str], Field(alias="module-source", default_factory=frozenset)]
21
+ dev_excludes: Annotated[Iterable[str], Field(alias="dev-excludes", default_factory=frozenset)]
22
+
23
+ module_name: Annotated[SkipJsonSchema[str], Field(exclude=True)]
24
+ project_root: Annotated[SkipJsonSchema[Path], Field(exclude=True)]
25
+
26
+ def __init__(self, obj: object, /, **kwargs):
27
+ if obj is not self.__INTERNAL:
28
+ raise RuntimeError("Security error: External initialization is prohibited")
29
+ if "/" in kwargs["module-source"]:
30
+ _, kwargs["module_name"] = str(kwargs["module-source"]).rsplit(os.sep, 1)
31
+ else:
32
+ kwargs["module_name"] = kwargs["module-source"]
33
+ super().__init__(**kwargs)
34
+
35
+ @classmethod
36
+ def from_hatch(cls, hook: HatchHookInterface, /) -> PatcherConfig:
37
+ if PatcherConfig.__INSTANCE:
38
+ raise RuntimeError(f"{cls.__name__} already initialized")
39
+
40
+ config = hook.config["options"]
41
+ config["project_root"] = Path(hook.root).resolve()
42
+ PatcherConfig.__INSTANCE = PatcherConfig(PatcherConfig.__INTERNAL, **config)
43
+ assert PatcherConfig.__INSTANCE
44
+ return PatcherConfig.__INSTANCE
45
+
46
+ @classmethod
47
+ def from_pyproject(cls, root: Path, /):
48
+ pyproject_toml = (root / "pyproject.toml").resolve()
49
+ if not pyproject_toml.exists() or not pyproject_toml.is_file():
50
+ raise FileNotFoundError(f"{pyproject_toml} does not exist or is not a file")
14
51
 
15
- def __init__(self, /):
16
- global __config
17
- if __config:
18
- raise RuntimeError("Configuration already initialized")
52
+ import tomllib
53
+ try:
54
+ with open(pyproject_toml, 'rb') as f:
55
+ config = tomllib.load(f)["tool"]["hatch"]["build"]["hooks"]["makepatch"]["options"]
56
+ config["project_root"] = Path(root).resolve()
57
+ return PatcherConfig(PatcherConfig.__INTERNAL, **config)
58
+ except tomllib.TOMLDecodeError as e:
59
+ raise RuntimeError(f"Cannot parse pyproject.toml") from e
@@ -0,0 +1 @@
1
+ from .hatch import HatchHookInterface
@@ -0,0 +1,57 @@
1
+ from typing import Protocol, Any
2
+
3
+ from hatchling.bridge.app import Application
4
+ from hatchling.metadata.core import ProjectMetadata
5
+
6
+
7
+ class HatchHookInterface(Protocol):
8
+ @property
9
+ def app(self, /) -> Application:
10
+ """
11
+ An instance of [Application](../utilities.md#hatchling.bridge.app.Application).
12
+ """
13
+
14
+ @property
15
+ def root(self, /) -> str:
16
+ """
17
+ The root of the project tree.
18
+ """
19
+
20
+ @property
21
+ def config(self, /) -> dict[str, Any]:
22
+ """
23
+ The cumulative hook configuration.
24
+
25
+ ```toml config-example
26
+ [tool.hatch.build.hooks.<PLUGIN_NAME>]
27
+ [tool.hatch.build.targets.<TARGET_NAME>.hooks.<PLUGIN_NAME>]
28
+ ```
29
+ """
30
+
31
+ @property
32
+ def metadata(self, /) -> ProjectMetadata:
33
+ ...
34
+
35
+ @property
36
+ def directory(self, /) -> str:
37
+ """
38
+ The build directory.
39
+ """
40
+
41
+ @property
42
+ def target_name(self, /) -> str:
43
+ """
44
+ The plugin name of the build target.
45
+ """
46
+
47
+ def dependencies(self, /) -> list[str]: # noqa: PLR6301
48
+ """
49
+ A list of extra [dependencies](../../config/dependency.md) that must be installed
50
+ prior to builds.
51
+
52
+ !!! warning
53
+ - For this to have any effect the hook dependency itself cannot be dynamic and
54
+ must always be defined in `build-system.requires`.
55
+ - As the hook must be imported to call this method, imports that require these
56
+ dependencies must be evaluated lazily.
57
+ """
@@ -0,0 +1 @@
1
+ from ._patcher import PatchApplier
@@ -0,0 +1,125 @@
1
+ import os
2
+ import shutil
3
+ import subprocess
4
+ from typing import final
5
+
6
+ from makepatch._config import PatcherConfig
7
+
8
+
9
+ if __debug__ and __import__("typing").TYPE_CHECKING:
10
+ from pathlib import Path
11
+ from typing import Final
12
+
13
+
14
+ @final
15
+ class PatchApplier:
16
+ if __debug__ and __import__("typing").TYPE_CHECKING:
17
+ __slots__ = (
18
+ "__config",
19
+ "__excludes",
20
+ "__excludes_dev",
21
+ "__patches",
22
+ "__src",
23
+ "__sources",
24
+ )
25
+
26
+ def __init__(self, config: PatcherConfig, /) -> None:
27
+ self.__config: Final = config
28
+ self.__excludes: Final = set[str]()
29
+ self.__excludes_dev: Final = set[str]()
30
+
31
+ # resolve patches
32
+ self.__patches: Final = self.__resolve_path(config.project_root / "patches")
33
+
34
+ # INTENDED: required for later use
35
+ self.__src: Path | None = None
36
+ self.__sources: Path | None = None
37
+
38
+ def setup_required(self, /):
39
+ return not (self.__config.project_root / "work/patched").resolve().exists()
40
+
41
+ @staticmethod
42
+ def __resolve_path(path: Path, /):
43
+ path = path.resolve()
44
+ if not path.exists() or not path.is_dir():
45
+ raise RuntimeError(f"{path} does not exist or is not a directory")
46
+ return path
47
+
48
+ def resolve_src(self, /):
49
+ """
50
+ Resolves `module` src root, where the codes are placed
51
+ """
52
+ assert not self.__src
53
+ if not self.__sources:
54
+ self.__sources = self.__resolve_path(self.__config.project_root / "work/sources")
55
+ assert self.__sources
56
+ src = self.__src = self.__resolve_path(self.__sources / self.__config.module_source)
57
+
58
+ for glob in self.__config.excludes:
59
+ dirname = glob.split(os.sep, 1)[0]
60
+ for excluded in src.rglob(glob):
61
+ if (rel_str := str(excluded.relative_to(src))).startswith(dirname):
62
+ self.__excludes.add(rel_str)
63
+
64
+ def resolve_sources(self, /):
65
+ """
66
+ Resolves entire git sources
67
+ """
68
+ assert not self.__sources
69
+ sources = self.__sources = self.__resolve_path(self.__config.project_root / "work/sources")
70
+
71
+ for glob in self.__config.dev_excludes:
72
+ dirname = glob.split(os.sep, 1)[0]
73
+ for excluded in sources.rglob(glob):
74
+ if (rel_str := str(excluded.relative_to(sources))).startswith(dirname):
75
+ self.__excludes_dev.add(rel_str)
76
+
77
+ def copy(self, dst: Path, /):
78
+ if self.__excludes_dev:
79
+ src = self.__sources
80
+ excludes = self.__excludes_dev
81
+ filter_ = lambda path: path.is_relative_to(self.__src) and (str(path.relative_to(self.__src)) in self.__excludes)
82
+ else:
83
+ src = self.__src
84
+ excludes = self.__excludes
85
+ filter_ = None
86
+
87
+ assert src and excludes
88
+ for src_file in src.rglob("*"):
89
+ if not src_file.is_file():
90
+ assert src_file.exists()
91
+ continue
92
+
93
+ rel = src_file.relative_to(src)
94
+ if (str(rel) in excludes) or (filter_ and filter_(src_file)):
95
+ continue
96
+
97
+ dst_file = dst / rel
98
+ if dst_file.exists():
99
+ raise RuntimeError(f"Failed to copy file {rel}: file exists")
100
+ dst_file.parent.mkdir(parents=True, exist_ok=True)
101
+ shutil.copy2(src_file, dst_file)
102
+
103
+ def apply(self, dst: Path, /) -> bool:
104
+ total = []
105
+ for patch in self.__patches.rglob("*.patch"):
106
+ rel_patch = patch.relative_to(self.__patches)
107
+ total.append(rel_patch)
108
+
109
+ rel_dst = rel_patch.with_suffix("") # foo.py.patch -> foo.py
110
+ dst_file = dst / rel_dst
111
+ dst_file.parent.mkdir(parents=True, exist_ok=True)
112
+
113
+ res = subprocess.run(
114
+ ["patch", "--unified", str(dst_file), str(patch)],
115
+ capture_output=True,
116
+ text=True,
117
+ )
118
+ if res.returncode != 0:
119
+ print(f"Failed to apply {rel_patch}:\n\t"
120
+ + "\n\t".join(res.stdout.splitlines()))
121
+ break
122
+ else:
123
+ print(f"Successfully applied {len(total)} patches")
124
+ return True
125
+ return False
@@ -1 +1 @@
1
- from ._patch import ClippyPatchHook
1
+ from ._patch import PatcherBuildHook
makepatch/hooks/_patch.py CHANGED
@@ -1,88 +1,60 @@
1
- import os
2
- from pathlib import Path
3
1
  from typing import override
4
2
 
5
3
  from hatchling.builders.hooks.plugin.interface import BuildHookInterface
6
4
 
7
- import shutil, subprocess, tomllib
5
+ import shutil
8
6
 
9
- if __debug__ and __import__("typing").TYPE_CHECKING:
10
- from typing import Any
7
+ from makepatch._config import PatcherConfig
8
+ from makepatch.core import PatchApplier
11
9
 
10
+ if __debug__ and __import__("typing").TYPE_CHECKING:
11
+ from typing import Any, Final
12
12
 
13
- class ClippyPatchHook(BuildHookInterface):
14
- @property
15
- def __root(self):
16
- return Path(self.root)
17
13
 
18
- @property
19
- def __config(self):
20
- with open(self.__root / "pyproject.toml", "rb") as f:
21
- pyproject = tomllib.load(f)
14
+ class PatcherBuildHook(BuildHookInterface):
15
+ PLUGIN_NAME = "makepatch"
22
16
 
23
- try:
24
- config = pyproject["tool"]["patcher"]
25
- except KeyError as e:
26
- raise RuntimeError("[tool.patcher] not found in pyproject.toml") from e
27
- return config["module-source"], frozenset(config.get("excludes", set()))
17
+ def __init__(self, /, *args, **kwargs):
18
+ super().__init__(*args, **kwargs)
19
+ self.__config: Final = PatcherConfig.from_hatch(self)
20
+ self.__patcher: Final = PatchApplier(self.__config)
28
21
 
29
22
  @override
30
23
  def initialize(self, version: str, build_data: dict[str, Any]) -> None:
31
- module_name, exclude_globs = self.__config
24
+ config = self.__config
25
+ patcher = self.__patcher
26
+
27
+ if patcher.setup_required():
28
+ patcher.resolve_sources()
29
+ patcher.resolve_src()
32
30
 
33
- src = self.__root / "work" / "sources" / module_name
34
- if not src.exists(): raise RuntimeError(f"{src} not exists")
35
- elif not src.is_dir(): raise RuntimeError(f"{src} is not a directory")
31
+ dst = config.project_root / "work/patched"
32
+ if dst.exists():
33
+ if not dst.is_dir(): raise RuntimeError(f"{dst} is not a directory")
34
+ shutil.rmtree(dst)
35
+ dst.mkdir(parents=True)
36
36
 
37
- excludes = set()
38
- for glob in exclude_globs:
39
- excludes |= set(str(excluded.relative_to(src)) for excluded in src.rglob(glob) if str(excluded.relative_to(src)).startswith(glob.split(os.sep)[0]))
37
+ patcher.copy(dst)
38
+ patcher.apply(dst / config.module_source)
39
+ self.__success = False
40
+ return
40
41
 
41
- patches = self.__root / "patches"
42
- if not patches.exists(): raise RuntimeError(f"{patches} not exists")
43
- elif not patches.is_dir(): raise RuntimeError(f"{patches} is not a directory")
42
+ patcher.resolve_src()
44
43
 
45
- if str(module_name).startswith("src"):
46
- module_name = module_name[4:]
47
- dst = self.__root / "src" / module_name
44
+ dst = self.__dst = config.project_root / "src" / config.module_name
48
45
  if dst.exists():
49
46
  if not dst.is_dir(): raise RuntimeError(f"{dst} is not a directory")
50
47
  shutil.rmtree(dst)
51
48
  dst.mkdir(parents=True)
52
49
 
53
- # Copy sources, skipping excludes
54
- for src_file in src.rglob("*"):
55
- if not src_file.is_file(): continue
56
-
57
- rel = src_file.relative_to(src)
58
- if str(rel) in excludes: continue
59
-
60
- dst_file = dst / rel
61
- dst_file.parent.mkdir(parents=True, exist_ok=True)
62
- shutil.copy2(src_file, dst_file)
63
-
64
- # Apply patches
65
- for patch_file in sorted(patches.rglob("*.patch")):
66
- rel_patch = patch_file.relative_to(patches)
67
- # e.g. foo.py.patch → foo.py
68
- target_rel = rel_patch.with_suffix("")
69
- target = dst / target_rel
70
-
71
- if not target.parent.exists():
72
- target.parent.mkdir(parents=True, exist_ok=False)
73
-
74
- result = subprocess.run(
75
- ["patch", "--unified", str(target), str(patch_file)],
76
- capture_output=True,
77
- text=True,
78
- )
79
- if result.returncode != 0:
80
- raise RuntimeError(f"Failed to apply {rel_patch}:\n{result.stderr}")
50
+ patcher.copy(dst)
51
+ self.__success = patcher.apply(dst)
81
52
 
53
+ @override
82
54
  def finalize(self, version: str, build_data: dict[str, Any], artifact_path: str) -> None:
83
- module_source, _ = self.__config
84
- if str(module_source).startswith("src"):
85
- module_source = module_source[4:]
86
- dst = self.__root / "src" / module_source
87
- if dst.exists():
88
- shutil.rmtree(dst)
55
+ if not self.__success:
56
+ return
57
+
58
+ if not self.__dst.exists() or not self.__dst.is_dir():
59
+ raise RuntimeError(f"{self.__dst} does not exists or is not a directory")
60
+ shutil.rmtree(self.__dst)
@@ -0,0 +1,28 @@
1
+ import shutil
2
+ from pathlib import Path
3
+
4
+ from makepatch._config import PatcherConfig
5
+ from makepatch.core import PatchApplier
6
+
7
+ if __debug__ and __import__("typing").TYPE_CHECKING:
8
+ from typing import Final
9
+
10
+
11
+ from ._rebuild import rebuild_patches
12
+
13
+ def init_patches():
14
+ root: Final = Path(__import__("os").getcwd())
15
+ config: Final = PatcherConfig.from_pyproject(root)
16
+ patcher: Final = PatchApplier(config)
17
+
18
+ patcher.resolve_sources()
19
+ patcher.resolve_src()
20
+
21
+ dst = root / "work" / "patched"
22
+ if dst.exists():
23
+ if not dst.is_dir(): raise RuntimeError(f"{dst} is not a directory")
24
+ shutil.rmtree(dst)
25
+ dst.mkdir(parents=True)
26
+
27
+ patcher.copy(dst)
28
+ exit(not patcher.apply(dst / config.module_source))
@@ -0,0 +1,78 @@
1
+ import os
2
+ import re
3
+ import subprocess
4
+
5
+ from pathlib import Path
6
+
7
+ from makepatch._config import PatcherConfig
8
+
9
+ if __debug__ and __import__("typing").TYPE_CHECKING:
10
+ from typing import Final
11
+
12
+
13
+ def rebuild_patches():
14
+ TIMESTAMP = re.compile(r"\t\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{9} \+\d{4}")
15
+ ARGS = (
16
+ "--ignore-trailing-space",
17
+ "--strip-trailing-cr",
18
+ )
19
+
20
+ root: Final = Path(os.getcwd())
21
+ config: Final = PatcherConfig.from_pyproject(root)
22
+
23
+ ref = root / "work/sources" / config.module_source
24
+ if not ref.exists() or not ref.is_dir():
25
+ raise RuntimeError(f"{ref} does not exists or is not a directory")
26
+
27
+ src = root / "work/patched" / config.module_source
28
+ if not ref.exists() or not ref.is_dir():
29
+ raise RuntimeError(f"{ref} does not exists or is not a directory")
30
+
31
+ patches = root / "patches"
32
+ if patches.exists():
33
+ if not patches.is_dir(): raise RuntimeError(f"{patches} is not a directory")
34
+ __import__("shutil").rmtree(patches)
35
+ patches.mkdir(parents=True)
36
+
37
+ excludes = set()
38
+ for glob in config.excludes:
39
+ dirname = glob.split(os.sep, 1)[0]
40
+ for excluded in src.rglob(glob):
41
+ if (rel_str := str(excluded.relative_to(src))).startswith(dirname):
42
+ excludes.add(rel_str)
43
+
44
+ generated = []
45
+ for target_file in sorted(src.rglob("*")):
46
+ if not target_file.is_file(): continue
47
+ if target_file.suffix == "pyc": continue
48
+ if (target_file.parent.name.startswith("__") and
49
+ target_file.parent.name.endswith("__")):
50
+ continue
51
+
52
+ rel_file = target_file.relative_to(src)
53
+ if str(rel_file) in excludes: continue
54
+
55
+ edited = target_file.relative_to(root)
56
+ origin = (ref / rel_file).relative_to(root)
57
+
58
+ result = subprocess.run(
59
+ ("diff", "--unified", *ARGS, "--new-file", str(origin), str(edited)),
60
+ capture_output=True,
61
+ text=True,
62
+ )
63
+ if result.returncode == 0:
64
+ continue
65
+ elif result.returncode != 1:
66
+ raise RuntimeError(f"Failed to generate a patch file for {edited} (return code {result.returncode}):"
67
+ f"\n{result.stderr}")
68
+ else:
69
+ patch = patches / rel_file.with_suffix(rel_file.suffix + ".patch")
70
+ patch.parent.mkdir(parents=True, exist_ok=True)
71
+ with open(patch, "x") as f:
72
+ l = result.stdout.splitlines(keepends=True)
73
+ l[0] = TIMESTAMP.sub("", l[0])
74
+ l[1] = TIMESTAMP.sub("", l[1])
75
+ f.write(''.join(l))
76
+ generated.append(patch)
77
+ else:
78
+ print(f"Successfully generated {len(generated)} patches")
@@ -0,0 +1,23 @@
1
+ Metadata-Version: 2.4
2
+ Name: makepatch
3
+ Version: 0.2.0
4
+ Summary: Hatch build hook to easily hard-fork packages with patches!
5
+ Project-URL: source, https://github.com/AlphaKR93/packages/tree/main/python/makepatch
6
+ Project-URL: issues, https://github.com/AlphaKR93/packages/issues
7
+ Author-email: Alpha <dev@alpha93.kr>
8
+ License-Expression: MIT
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Environment :: Console
11
+ Classifier: Environment :: Plugins
12
+ Classifier: Framework :: Hatch
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Programming Language :: Python :: Implementation :: CPython
19
+ Classifier: Topic :: Software Development :: Build Tools
20
+ Requires-Python: >=3.12
21
+ Requires-Dist: hatchling>=1.29.0
22
+ Requires-Dist: pydantic>=2.13.0
23
+ Requires-Dist: typing-extensions>=4.15.0; python_version < '3.15'
@@ -0,0 +1,15 @@
1
+ makepatch/__hooks__.py,sha256=Twwvsdvf5-wFb4aqUD9ApiTj_hpf_kOh_nedyX4a5yE,163
2
+ makepatch/__init__.py,sha256=VGfjjIEpPnILrFlZqLzYhbUL6tqmhx1-e9uZOuVpQh4,86
3
+ makepatch/_config.py,sha256=OA0SPKb8R5Q00cs6DwDu79kN6gW8vnDrKGvp6DCUfG8,2492
4
+ makepatch/_types/__init__.pyi,sha256=SlDWSyq8IncNQKcgvjc9F7jyqKjTCSLW4HLTxgsSYcM,39
5
+ makepatch/_types/hatch.pyi,sha256=MWAwNXZx1RXrdZ8sVLY8pinoOleSajjDatylnFsLprQ,1604
6
+ makepatch/core/__init__.py,sha256=F943qvL-FwY0zkSuMxHuVzG55EVDICJYouAE1-3NrOU,36
7
+ makepatch/core/_patcher.py,sha256=m5IsO66AC40i7rpHH1y5PE5VFW390svgZxA4SIw1930,4403
8
+ makepatch/hooks/__init__.py,sha256=I7QNThkPOzPtvQiocyUoQKKdWNYWk5wiF-FVRz4LTjo,38
9
+ makepatch/hooks/_patch.py,sha256=kSuzOjyeYTWEO457e5ikZI5RN8KGn22n5BVQEj1GOL8,1971
10
+ makepatch/scripts/__init__.py,sha256=dggERiHtxr-wWJ6sHvfsfW3a9a3Io3CmNd7xvIC5AnY,769
11
+ makepatch/scripts/_rebuild.py,sha256=iisw0dza8cr4o_FBvzcrNGmxeeje98XW2g6dHlJqsJ0,2810
12
+ makepatch-0.2.0.dist-info/METADATA,sha256=Cp_Qi2UyIpNqoKsvTNebZNtXQT2XQrTIOgnmPZI_hRE,1003
13
+ makepatch-0.2.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
14
+ makepatch-0.2.0.dist-info/entry_points.txt,sha256=tDk_T8AJNcjy0KhBEXxf9wZBIAhv3l4pGekKOqXpNpY,159
15
+ makepatch-0.2.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.11.14
2
+ Generator: hatchling 1.31.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -0,0 +1,6 @@
1
+ [console_scripts]
2
+ apply-patches = makepatch.scripts:apply_patches
3
+ rebuild-patches = makepatch.scripts:rebuild_patches
4
+
5
+ [hatch]
6
+ makepatch = makepatch.__hooks__
makepatch/__hook__.py DELETED
@@ -1,7 +0,0 @@
1
- from hatchling.plugin import hookimpl as _hook
2
- from src.clippy.hooks import ClippyPatchHook
3
-
4
-
5
- @_hook
6
- def __register_hatch_build_hooks():
7
- return ClippyPatchHook
@@ -1,8 +0,0 @@
1
- Metadata-Version: 2.3
2
- Name: makepatch
3
- Version: 0.0.1
4
- Summary: Hatch build hook to easily hard-fork packages with patches!
5
- Classifier: Framework :: Hatch
6
- Requires-Dist: hatchling>=1.29.0
7
- Requires-Dist: pydantic>=2.13.4
8
- Requires-Python: >=3.12
@@ -1,10 +0,0 @@
1
- makepatch/__hook__.py,sha256=ov-ZExdRe2dhvnocD01_58Z37eBryz8sTaTAbFEHM-4,171
2
- makepatch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- makepatch/_config.py,sha256=GlyVVm9ZsolzDKSiupoTMTmOHVSNCMdyo-_AzArw3Wo,565
4
- makepatch/hooks/__init__.py,sha256=8JvonoGcrgihzhk7eUIwQPwr2oTL58IfF44tAk2vOYc,37
5
- makepatch/hooks/_patch.py,sha256=xjaKYDJkhekGfBomXOKH5pOiP209hNtt6G4kHv-kfQs,3335
6
- makepatch/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- makepatch-0.0.1.dist-info/WHEEL,sha256=fWriCkzqm-pffF5af4gJC9iI5FMFaJTuN9UxxxzOmdY,81
8
- makepatch-0.0.1.dist-info/entry_points.txt,sha256=8xpxWiGS9XHoBCcyoVcfkRfjW2WnvamePMfI3_WJsOs,141
9
- makepatch-0.0.1.dist-info/METADATA,sha256=2kQkR1g2KEOP011WEC73brgoEzEW_wM-HQsAhJbm6Q8,242
10
- makepatch-0.0.1.dist-info/RECORD,,
@@ -1,7 +0,0 @@
1
- [console_scripts]
2
- init = makepatch.scripts:init
3
- rebuild-patches = makepatch.scripts:rebuild_patches
4
-
5
- [hatch]
6
- makepatch = makepatch.__hook__
7
-