nmk-python 1.6.0__py3-none-any.whl → 1.7.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.
File without changes
@@ -0,0 +1,54 @@
1
+ """
2
+ Python build backend definition module
3
+ """
4
+
5
+ from abc import ABC, abstractmethod
6
+ from pathlib import Path
7
+
8
+ from nmk.model.model import NmkModel
9
+
10
+
11
+ class PythonBuildBackend(ABC):
12
+ """
13
+ Python build backend interface
14
+
15
+ :param model: NmkModel instance
16
+ """
17
+
18
+ def __init__(self, model: NmkModel):
19
+ self._model = model
20
+ self._env_backend = model.env_backend
21
+
22
+ @abstractmethod
23
+ def install_editable(self): # pragma: no cover
24
+ """
25
+ Install current project as editable package
26
+ """
27
+ pass
28
+
29
+ @abstractmethod
30
+ def install_wheel(self, wheel_path: Path): # pragma: no cover
31
+ """
32
+ Install current project built wheel
33
+ """
34
+ pass
35
+
36
+ @abstractmethod
37
+ def build_wheel(self, build_dir: Path, built_wheel_name: str, wheel_version: str) -> Path: # pragma: no cover
38
+ """
39
+ Build current project wheel
40
+
41
+ :param build_dir: temporary build folder
42
+ :param built_wheel_name: name of the built wheel file
43
+ :return: path to built wheel
44
+ """
45
+ pass
46
+
47
+ @abstractmethod
48
+ def uninstall_wheel(self, wheel_name: str): # pragma: no cover
49
+ """
50
+ Uninstall specified wheel
51
+
52
+ :param wheel_name: name of the wheel to uninstall
53
+ """
54
+ pass
@@ -0,0 +1,32 @@
1
+ """
2
+ Build backend factory definition module
3
+ """
4
+
5
+ from nmk.model.model import NmkModel
6
+
7
+ from .build_backend import PythonBuildBackend
8
+ from .pip_build_backend import PipBuildBackend
9
+ from .uv_build_backend import UvBuildBackend
10
+
11
+ # Supported backends mapping
12
+ _SUPPORTED_BACKENDS = {"pip": PipBuildBackend, "legacy": PipBuildBackend, "uv": UvBuildBackend}
13
+
14
+
15
+ class BuildBackendFactory:
16
+ """
17
+ Python build backend factory
18
+ """
19
+
20
+ @staticmethod
21
+ def create(model: NmkModel) -> PythonBuildBackend:
22
+ """
23
+ Create a python build backend, from provided environment backend
24
+
25
+ :param model: NmkModel instance
26
+ :return: Python build backend instance
27
+ """
28
+
29
+ # Check for supported backend
30
+ env_backend_name = model.env_backend.name
31
+ assert env_backend_name in _SUPPORTED_BACKENDS, f"Unsupported backend for python build: {env_backend_name}"
32
+ return _SUPPORTED_BACKENDS[env_backend_name](model)
@@ -0,0 +1,53 @@
1
+ """
2
+ Pip build backend module.
3
+ """
4
+
5
+ import sys
6
+ from pathlib import Path
7
+ from typing import cast
8
+
9
+ from nmk.utils import run_with_logs
10
+
11
+ from .build_backend import PythonBuildBackend
12
+
13
+
14
+ class PipBuildBackend(PythonBuildBackend):
15
+ """
16
+ Pip build backend implementation
17
+
18
+ :param model: NmkModel instance
19
+ """
20
+
21
+ def install_editable(self):
22
+ # Prepare install args
23
+ install_args = ["install", "-e", "."]
24
+
25
+ # Add extra args from model config
26
+ install_args += cast(list[str], self._model.config["pythonEditablePipInstallArgs"].value)
27
+
28
+ # Delegate to subprocess
29
+ self._env_backend.subprocess(install_args, cwd=self._env_backend.project_path)
30
+
31
+ def install_wheel(self, wheel_path: Path):
32
+ # Prepare install args
33
+ install_args = ["install", str(wheel_path)]
34
+
35
+ # Add extra args from model config
36
+ install_args += cast(list[str], self._model.config["pythonWheelPipInstallArgs"].value)
37
+
38
+ # Delegate to subprocess
39
+ self._env_backend.subprocess(install_args, cwd=self._env_backend.project_path)
40
+
41
+ def build_wheel(self, build_dir: Path, built_wheel_name: str, wheel_version: str) -> Path:
42
+ # Delegate to build module
43
+ run_with_logs([sys.executable, "-m", "build", "--wheel", "--skip-dependency-check", "--no-isolation"], cwd=build_dir)
44
+
45
+ # Return built wheel path
46
+ return build_dir / "dist" / built_wheel_name
47
+
48
+ def uninstall_wheel(self, wheel_name: str):
49
+ # Prepare uninstall args
50
+ uninstall_args = ["uninstall", "--yes", wheel_name]
51
+
52
+ # Delegate to subprocess
53
+ self._env_backend.subprocess(uninstall_args, cwd=self._env_backend.project_path)
@@ -0,0 +1,56 @@
1
+ """
2
+ Uv build backend module.
3
+ """
4
+
5
+ from pathlib import Path
6
+ from typing import cast
7
+
8
+ from .build_backend import PythonBuildBackend
9
+
10
+
11
+ class UvBuildBackend(PythonBuildBackend):
12
+ """
13
+ Uv build backend implementation
14
+
15
+ :param model: NmkModel instance
16
+ """
17
+
18
+ def install_editable(self):
19
+ # Prepare install args
20
+ install_args = ["sync"]
21
+
22
+ # Add extra args from model config
23
+ install_args += cast(list[str], self._model.config["pythonEditableUvInstallArgs"].value)
24
+
25
+ # Delegate to subprocess
26
+ self._env_backend.subprocess(install_args, cwd=self._env_backend.project_path)
27
+
28
+ def install_wheel(self, wheel_path: Path):
29
+ # Prepare install args
30
+ install_args = ["pip", "install", str(wheel_path)]
31
+
32
+ # Add extra args from model config
33
+ install_args += cast(list[str], self._model.config["pythonWheelUvInstallArgs"].value)
34
+
35
+ # Delegate to subprocess
36
+ self._env_backend.subprocess(install_args, cwd=self._env_backend.project_path)
37
+
38
+ def build_wheel(self, build_dir: Path, built_wheel_name: str, wheel_version: str) -> Path:
39
+ # Name for wheel sub-directory
40
+ wheel_sub_dir = "wheel_dist"
41
+
42
+ # Check for (experimental) uv-build backend
43
+ if self._model.config["pythonUseUvBuildBackend"].value:
44
+ # Prepare project version (without updating the venv)
45
+ self._env_backend.subprocess(["version", "--active", "--no-sync", wheel_version], cwd=build_dir)
46
+
47
+ # Delegate to uv
48
+ build_args = ["build", "--wheel", "--out-dir", wheel_sub_dir]
49
+ self._env_backend.subprocess(build_args, cwd=build_dir)
50
+
51
+ # Return built wheel path
52
+ return build_dir / wheel_sub_dir / built_wheel_name
53
+
54
+ def uninstall_wheel(self, wheel_name: str):
55
+ # Same as setup
56
+ self._env_backend.upgrade(full=False, only_deps=True)
nmk_python/build.py CHANGED
@@ -3,34 +3,39 @@ Python package build module
3
3
  """
4
4
 
5
5
  import shutil
6
- import sys
7
- from logging import Logger
8
6
  from pathlib import Path
9
7
 
8
+ from nmk.logs import NmkLogWrapper
10
9
  from nmk.model.builder import NmkTaskBuilder
11
- from nmk.model.keys import NmkRootConfig
12
- from nmk.model.resolver import NmkListConfigResolver, NmkStrConfigResolver
13
- from nmk.utils import is_windows, run_pip, run_with_logs
14
- from nmk_base.venvbuilder import VenvUpdateBuilder
15
- from tomlkit import loads
16
- from tomlkit.toml_file import TOMLFile
10
+ from nmk.model.model import NmkModel
11
+ from nmk.model.resolver import NmkDictConfigResolver, NmkListConfigResolver, NmkStrConfigResolver
12
+ from nmk.utils import is_windows
13
+
14
+ from nmk_python.backends.factory import BuildBackendFactory
17
15
 
18
16
 
19
17
  # Install filter for Windows
20
- def _can_install(name: str, logger: Logger) -> bool:
18
+ def _can_install(name: str, logger: NmkLogWrapper) -> bool:
21
19
  # On Windows, refuse to install nmk package while running nmk (wont' work)
22
- if is_windows() and name == "nmk":
23
- logger.warning("Can't install nmk while running nmk!")
20
+ if is_windows() and name in ["nmk", "buildenv"]:
21
+ logger.warning(f"Can't install {name} while running {name}!")
24
22
  return False
25
23
  return True
26
24
 
27
25
 
26
+ # Get python package from model
27
+ def _python_package(model: NmkModel) -> str:
28
+ cfg = model.config.get("pythonPackage")
29
+ assert cfg is not None and isinstance(cfg.value, str), "Can't read pythonPackage from model"
30
+ return cfg.value
31
+
32
+
28
33
  class PackageBuilder(NmkTaskBuilder):
29
34
  """
30
35
  Python package builder
31
36
  """
32
37
 
33
- def build(self, project_file: str, version_file: str, source_dirs: list[str], artifacts_dir: str, build_dir: str, extra_resources: dict[str, str]):
38
+ def build(self, project_file: str, version_file: str, source_dirs: list[str], artifacts_dir: str, build_dir: str, extra_resources: dict[str, str]): # pyright: ignore[reportIncompatibleMethodOverride]
34
39
  """
35
40
  Delegate to python build module, from a temporary build folder
36
41
 
@@ -49,21 +54,14 @@ class PackageBuilder(NmkTaskBuilder):
49
54
  build_path.mkdir(exist_ok=True, parents=True)
50
55
 
51
56
  # Copy source folders and various project files
52
- project_root = Path(self.model.config[NmkRootConfig.PROJECT_DIR].value)
57
+ project_path = Path(project_file)
58
+ project_root = project_path.parent
53
59
  for source_dir in map(Path, source_dirs):
54
60
  shutil.copytree(source_dir, build_path / source_dir.relative_to(project_root))
55
- for candidate in filter(lambda p: p.is_file(), map(Path, [project_file, version_file, project_root / "README.md", project_root / "LICENSE"])):
56
- shutil.copyfile(candidate, build_path / candidate.name)
57
-
58
- # Update project file with version
59
- build_project = build_path / Path(project_file).name
60
- build_version = build_path / Path(version_file).name
61
- with build_project.open() as f:
62
- project_doc = loads(f.read())
63
- dyn_table = project_doc.get("tool").get("setuptools").get("dynamic")
64
- dyn_table["version"] = {"file": build_version.name}
65
- project_output = TOMLFile(build_project)
66
- project_output.write(project_doc)
61
+ for candidate in filter(lambda p: p.is_file(), map(Path, [project_path, version_file, project_root / "README.md", project_root / "LICENSE"])):
62
+ dest = build_path / candidate.relative_to(project_root)
63
+ dest.parent.mkdir(parents=True, exist_ok=True)
64
+ shutil.copyfile(candidate, dest)
67
65
 
68
66
  # Handle extra resources
69
67
  for src, dst in extra_resources.items():
@@ -90,18 +88,17 @@ class PackageBuilder(NmkTaskBuilder):
90
88
  for wheel in artifacts_path.glob("*.whl"):
91
89
  wheel.unlink()
92
90
 
93
- # Delegate to build module
94
- run_with_logs(
95
- [sys.executable, "-m", "build", "--wheel", "--skip-dependency-check", "--no-isolation"],
96
- self.logger,
97
- cwd=build_path,
91
+ # Read version from file
92
+ wheel_version = Path(version_file).read_text().splitlines()[0]
93
+
94
+ # Delegate to build backend
95
+ built_wheel = BuildBackendFactory.create(self.model).build_wheel(
96
+ build_dir=build_path, built_wheel_name=self.main_output.name, wheel_version=wheel_version
98
97
  )
98
+ assert built_wheel.is_file(), f"Expected built wheel not found: {built_wheel}"
99
99
 
100
100
  # Copy wheel to artifacts folder
101
- target_wheel = self.main_output
102
- built_wheel = build_path / "dist" / target_wheel.name
103
- assert built_wheel.is_file(), f"Expected built wheel not found: {built_wheel}"
104
- shutil.copyfile(built_wheel, target_wheel)
101
+ shutil.copyfile(built_wheel, self.main_output)
105
102
 
106
103
 
107
104
  class PythonModuleResolver(NmkStrConfigResolver):
@@ -113,27 +110,30 @@ class PythonModuleResolver(NmkStrConfigResolver):
113
110
  """
114
111
  Return module name from package (i.e. wheel) name
115
112
  """
116
- return self.model.config["pythonPackage"].value.replace("-", "_")
113
+ return _python_package(self.model).replace("-", "_")
117
114
 
118
115
 
119
- class Installer(VenvUpdateBuilder):
116
+ class Installer(NmkTaskBuilder):
120
117
  """
121
118
  Install built wheel in venv
122
119
  """
123
120
 
124
- def build(self, name: str, pip_args: list[str], to_remove: str):
121
+ def build(self, name: str, wheel: str, pip_args: list[str] | None = None, to_remove: str = ""): # pyright: ignore[reportIncompatibleMethodOverride]
125
122
  """
126
123
  Install wheel in venv
127
124
 
128
125
  :param name: wheel name to be installed
129
- :param pip_args: pip command line arguments
126
+ :param wheel: wheel path to be installed
127
+ :param pip_args: pip command line arguments; deprecated, not used anymore
130
128
  :param to_remove: stamp file to be removed
131
129
  """
132
130
 
133
131
  # Check if wheel can be installed
134
132
  if _can_install(name, self.logger):
135
- # Install wheel in current venv
136
- super().build(" ".join(pip_args))
133
+ # Delegate to build backend
134
+ old_packages = self.model.env_backend.installed_packages
135
+ BuildBackendFactory.create(self.model).install_wheel(wheel_path=Path(wheel))
136
+ self.model.env_backend.print_updates(old_packages)
137
137
 
138
138
  # Remove stamp file
139
139
  Path(to_remove).unlink(missing_ok=True)
@@ -144,7 +144,7 @@ class Uninstaller(NmkTaskBuilder):
144
144
  Uninstall current project wheel from venv
145
145
  """
146
146
 
147
- def build(self, name: str):
147
+ def build(self, name: str): # pyright: ignore[reportIncompatibleMethodOverride]
148
148
  """
149
149
  Uninstall wheel from venv
150
150
 
@@ -153,8 +153,10 @@ class Uninstaller(NmkTaskBuilder):
153
153
  :param name: wheel name to be uninstalled
154
154
  """
155
155
 
156
- # Simply delegate to pip
157
- run_pip(["uninstall", "--yes", name], logger=self.logger)
156
+ # Delegate to build backend
157
+ old_packages = self.model.env_backend.installed_packages
158
+ BuildBackendFactory.create(self.model).uninstall_wheel(wheel_name=name)
159
+ self.model.env_backend.print_updates(old_packages)
158
160
 
159
161
 
160
162
  class EditableBuilder(NmkTaskBuilder):
@@ -162,17 +164,19 @@ class EditableBuilder(NmkTaskBuilder):
162
164
  Install python project in editable mode
163
165
  """
164
166
 
165
- def build(self, pip_args: list[str]):
167
+ def build(self, pip_args: list[str] | None = None): # pyright: ignore[reportIncompatibleMethodOverride]
166
168
  """
167
169
  Install project in venv as editable package
168
170
 
169
- :param pip_args: pip command line arguments
171
+ :param pip_args: pip command line arguments; deprecated, not used anymore
170
172
  """
171
173
 
172
174
  # Check if project can be installed in editable mode
173
- if _can_install(self.model.config["pythonPackage"].value, self.logger):
174
- # Delegate to pip
175
- run_pip(["install", "-e", "."], logger=self.logger, extra_args=" ".join(pip_args))
175
+ if _can_install(_python_package(self.model), self.logger):
176
+ # Delegate to build backend
177
+ old_packages = self.model.env_backend.installed_packages
178
+ BuildBackendFactory.create(self.model).install_editable()
179
+ self.model.env_backend.print_updates(old_packages)
176
180
 
177
181
  # Touch stamp file
178
182
  self.main_output.touch()
@@ -183,8 +187,39 @@ class PythonOptionalDepsResolver(NmkListConfigResolver):
183
187
  Python optional deps resolver
184
188
  """
185
189
 
186
- def get_value(self, name: str, groups: dict[str, list[str]]) -> list[str]:
190
+ def get_value(self, name: str, groups: dict[str, list[str]]) -> list[str]: # pyright: ignore[reportIncompatibleMethodOverride]
187
191
  """
188
192
  Turn dependency options deps dict into a merged list of dependencies
189
193
  """
190
194
  return sorted(list(set(dependency for deps in groups.values() for dependency in deps)))
195
+
196
+
197
+ class PythonDevDepsResolver(NmkListConfigResolver):
198
+ """
199
+ Python development deps resolver
200
+ """
201
+
202
+ def get_value(self, name: str, package_deps: list[str], all_deps: list[str]) -> list[str]: # pyright: ignore[reportIncompatibleMethodOverride]
203
+ """
204
+ Return development dependencies (all deps minus package deps)
205
+ """
206
+ return sorted(list(set(all_deps) - set(package_deps)))
207
+
208
+
209
+ class PythonArchiveDepsResolver(NmkDictConfigResolver):
210
+ """
211
+ Python archive dependencies resolver
212
+ """
213
+
214
+ def get_value(self, name: str, archives: list[str]) -> dict[str, str]: # pyright: ignore[reportIncompatibleMethodOverride]
215
+ """
216
+ Return archive dependencies mapping (python package name -> wheel path)
217
+ """
218
+
219
+ # Iterate on existing archives paths
220
+ out: dict[str, str] = {}
221
+ for dep in filter(lambda p: p.is_file() and "-" in p.name, map(Path, archives)):
222
+ # Extract package name from wheel file name
223
+ dep_name = dep.name.split("-")[0].replace("_", "-")
224
+ out[dep_name] = str(dep.resolve()).replace("\\", "\\\\") # Escape backslashes for Windows paths
225
+ return out
nmk_python/build.yml CHANGED
@@ -36,6 +36,19 @@ config:
36
36
  # Python package dependencies
37
37
  pythonPackageRequirements: []
38
38
 
39
+ # Python development dependencies
40
+ pythonDevRequirements:
41
+ __resolver__: nmk_python.build.PythonDevDepsResolver
42
+ params:
43
+ package_deps: ${pythonPackageRequirements}
44
+ all_deps: ${venvPkgDeps}
45
+
46
+ # Archive related requirements
47
+ pythonArchiveRequirements:
48
+ __resolver__: nmk_python.build.PythonArchiveDepsResolver
49
+ params:
50
+ archives: ${venvArchiveDeps}
51
+
39
52
  # Also install in venv all dependencies
40
53
  venvPkgDeps:
41
54
  - ${pythonPackageRequirements}
@@ -63,16 +76,22 @@ config:
63
76
  password: ${{ secrets.pypi_password }}
64
77
  packages_dir: ${r!pythonArtifacts}
65
78
 
66
- # Wheel install arguments
67
- pythonWheelInstallArgs:
79
+ # Wheel install arguments (for pip backend)
80
+ pythonWheelPipInstallArgs:
68
81
  - --force-reinstall # force reinstall because otherwise install will be skipped if version didn't changed
69
82
  - --no-deps # but skip dependencies to avoid reinstalling everything (deps are already installed by py.venv task)
70
83
 
71
- # Editable install arguments
72
- pythonEditableInstallArgs:
84
+ # Editable install arguments (for pip backend)
85
+ pythonEditablePipInstallArgs:
73
86
  - --no-deps # skip dependencies to avoid reinstalling everything
74
87
  - --no-build-isolation # disable dependencies install when building for "editable" mode
75
88
 
89
+ # Wheel install arguments (for uv backend)
90
+ pythonWheelUvInstallArgs: []
91
+
92
+ # Editable install arguments (for uv backend)
93
+ pythonEditableUvInstallArgs: []
94
+
76
95
  # Type checking mode for Pyright; may be off, basic, standard, strict
77
96
  pythonTypeCheckingMode: strict
78
97
 
@@ -96,9 +115,7 @@ tasks:
96
115
  input:
97
116
  - ${pythonProjectFile}
98
117
  builder: nmk_python.build.EditableBuilder
99
- params:
100
- pip_args:
101
- - ${pythonEditableInstallArgs}
118
+
102
119
 
103
120
  # Python wheel build
104
121
  py.build:
@@ -139,8 +156,7 @@ tasks:
139
156
  builder: nmk_python.build.Installer
140
157
  params:
141
158
  name: ${pythonPackage}
142
- pip_args:
143
- - ${pythonWheelInstallArgs}
159
+ wheel: ${pythonWheel}
144
160
  to_remove: ${pythonEditableStamp}
145
161
 
146
162
  # Python module clean
nmk_python/files.py CHANGED
@@ -2,7 +2,9 @@
2
2
  Python files resolvers
3
3
  """
4
4
 
5
+ from abc import abstractmethod
5
6
  from pathlib import Path
7
+ from typing import cast
6
8
 
7
9
  from nmk.model.resolver import NmkListConfigResolver
8
10
 
@@ -12,6 +14,7 @@ class FilesFinder(NmkListConfigResolver):
12
14
  Shared logic for files resolution
13
15
  """
14
16
 
17
+ @abstractmethod
15
18
  def find_in_folders(self) -> list[str]: # pragma: no cover
16
19
  """
17
20
  Folders to be browsed (to be overridden)
@@ -38,7 +41,7 @@ class PythonFilesFinder(FilesFinder):
38
41
  """
39
42
  Python source folders
40
43
  """
41
- return self.model.config["pythonSrcFolders"].value
44
+ return cast(list[str], self.model.config["pythonSrcFolders"].value)
42
45
 
43
46
  def get_value(self, name: str) -> list[Path]:
44
47
  """
@@ -50,8 +53,8 @@ class PythonFilesFinder(FilesFinder):
50
53
  all_files = set(super().get_value(name))
51
54
 
52
55
  # Remove generated and test ones
53
- all_files -= {Path(p) for p in self.model.config["pythonTestSrcFiles"].value}
54
- all_files -= {Path(p) for p in self.model.config["pythonGeneratedSrcFiles"].value}
56
+ all_files -= {Path(p) for p in cast(list[str], self.model.config["pythonTestSrcFiles"].value)}
57
+ all_files -= {Path(p) for p in cast(list[str], self.model.config["pythonGeneratedSrcFiles"].value)}
55
58
 
56
59
  return list(all_files)
57
60
 
@@ -63,4 +66,4 @@ class PythonTestFilesFinder(FilesFinder):
63
66
  """
64
67
  Python tests source folders
65
68
  """
66
- return [self.model.config["pythonTestSources"].value]
69
+ return [cast(str, self.model.config["pythonTestSources"].value)]
nmk_python/files.yml CHANGED
@@ -1,27 +1,30 @@
1
1
  config:
2
- # Folder(s) for Python sources
3
- pythonSrcFolders:
4
- - ${sourceDir}
2
+ # Folder(s) for Python sources
3
+ pythonSrcFolders:
4
+ - ${sourceDir}
5
5
 
6
- # Package holding test code
7
- pythonTestPackage: tests
6
+ # Package holding test code
7
+ pythonTestPackage: tests
8
8
 
9
- # Root folder for test sources
10
- pythonTestSources: ${sourceDir}/${pythonTestPackage}
9
+ # Root folder for test sources
10
+ pythonTestSources: ${sourceDir}/${pythonTestPackage}
11
11
 
12
- # Found Python test source files
13
- pythonTestSrcFiles:
14
- __resolver__: nmk_python.files.PythonTestFilesFinder
12
+ # Found Python test source files
13
+ pythonTestSrcFiles:
14
+ __resolver__: nmk_python.files.PythonTestFilesFinder
15
15
 
16
- # Generated Python source files (to let other plugins contribute)
17
- pythonGeneratedSrcFiles: []
16
+ # Generated Python source files (to let other plugins contribute)
17
+ pythonGeneratedSrcFiles: []
18
18
 
19
- # Found Python source files (that are neither generated not test ones)
20
- pythonFoundSrcFiles:
21
- __resolver__: nmk_python.files.PythonFilesFinder
19
+ # Found Python source files (that are neither generated not test ones)
20
+ pythonFoundSrcFiles:
21
+ __resolver__: nmk_python.files.PythonFilesFinder
22
22
 
23
- # All Python source files
24
- pythonSrcFiles:
25
- - ${pythonFoundSrcFiles}
26
- - ${pythonTestSrcFiles}
27
- - ${pythonGeneratedSrcFiles}
23
+ # All Python source files
24
+ pythonSrcFiles:
25
+ - ${pythonFoundSrcFiles}
26
+ - ${pythonTestSrcFiles}
27
+ - ${pythonGeneratedSrcFiles}
28
+
29
+ # State if using uv build backend (experimental)
30
+ pythonUseUvBuildBackend: false
nmk_python/project.yml CHANGED
@@ -1,32 +1,34 @@
1
1
  config:
2
- # Generated project file
3
- pythonProjectFile: ${PROJECTDIR}/pyproject.toml
2
+ # Generated project file
3
+ pythonProjectFile: ${PROJECTDIR}/pyproject.toml
4
4
 
5
- # Paths to pyproject.toml fragments to be merged
6
- pythonProjectFileFragments: []
5
+ # Paths to pyproject.toml fragments to be merged
6
+ pythonProjectFileFragments: []
7
7
 
8
- # Contributed project items from yml files
9
- pythonProjectFileItems: {}
8
+ # Contributed project items from yml files
9
+ pythonProjectFileItems: {}
10
10
 
11
- # Require VSCode specific extension for toml support
12
- vscodeExtensionsNames:
13
- - tamasfe.even-better-toml
11
+ # Require VSCode specific extension for toml support
12
+ vscodeExtensionsNames:
13
+ - tamasfe.even-better-toml
14
+
15
+ # Input file for venv refresh is project file (overrides nmk-base default)
16
+ venvUpdateInput: ${pythonProjectFile}
14
17
 
15
18
  tasks:
16
- py.project:
17
- description: Generate python project file
18
- emoji: ":snake:.:toolbox:"
19
- input:
20
- - ${pythonProjectFileFragments}
21
- - ${pythonVersionStamp}
22
- output:
23
- - ${pythonProjectFile}
24
- deps:
25
- - py.version
26
- appendToDeps: setup
27
- if: ${pythonSrcFiles} # Only if python source files are found
28
- builder: nmk_base.common.TomlFileBuilder
29
- params:
30
- fragment_files: ${pythonProjectFileFragments}
31
- items: ${pythonProjectFileItems}
32
- plugin_name: nmk-python
19
+ py.project:
20
+ description: Generate python project file
21
+ emoji: ":snake:.:toolbox:"
22
+ input:
23
+ - ${pythonProjectFileFragments}
24
+ - ${pythonVersionStamp}
25
+ output:
26
+ - ${pythonProjectFile}
27
+ deps:
28
+ - py.version
29
+ appendToDeps: py.venv # Project file must be generated before venv is refreshed
30
+ builder: nmk_base.common.TomlFileBuilder
31
+ params:
32
+ fragment_files: ${pythonProjectFileFragments}
33
+ items: ${pythonProjectFileItems}
34
+ plugin_name: nmk-python
@@ -1,10 +1,10 @@
1
1
  [build-system]
2
- requires = ["setuptools"]
3
- build-backend = "setuptools.build_meta"
2
+ requires = ["{% if pythonUseUvBuildBackend %}uv_build>=0.9.18,<0.10.0{% else %}setuptools{% endif %}"]
3
+ build-backend = "{% if pythonUseUvBuildBackend %}uv_build{% else %}setuptools.build_meta{% endif %}"
4
4
 
5
5
  [project]
6
6
  name = "{{ pythonPackage }}"
7
- dynamic = ["version"]
7
+ {% if pythonUseUvBuildBackend %}version = "0.0.0"{% else %}dynamic = ["version"]{% endif %}
8
8
  authors = [
9
9
  {name="{{ projectAuthor }}"},
10
10
  ]
@@ -20,7 +20,17 @@ dependencies = [{% for req in pythonPackageRequirements %}
20
20
  "{{ req }}",{% endfor %}
21
21
  ]
22
22
 
23
- [tool.setuptools.packages.find]
23
+ [dependency-groups]
24
+ dev = [{% for req in pythonDevRequirements %}
25
+ "{{ req }}",{% endfor %}{% for req in pythonArchiveRequirements %}
26
+ "{{ req }}@{{ pythonArchiveRequirements[req] }}",{% endfor %}
27
+ ]
28
+
29
+ {% if pythonUseUvBuildBackend %}[tool.uv.build-backend]
30
+ module-root = "{{pythonSrcFolders[0]}}"
31
+ wheel-exclude = [{% for module in pythonBuildExcludedModules %}{% if loop.index > 1 %},{% endif %}"{{module}}"{% endfor %}]
32
+
33
+ {% else %}[tool.setuptools.packages.find]
24
34
  where = [{% for path in pythonSrcFolders %}"{{path}}",{% endfor %}]
25
35
  exclude = [{% for module in pythonBuildExcludedModules %}{% if loop.index > 1 %},{% endif %}"{{module}}"{% endfor %}]
26
36
  namespaces = false
@@ -31,7 +41,7 @@ namespaces = false
31
41
  [tool.setuptools.dynamic.version]
32
42
  file = "{{ pythonVersionStamp }}"
33
43
 
34
- [tool.distutils.bdist_wheel]
44
+ {% endif %}[tool.distutils.bdist_wheel]
35
45
  plat-name = "{{ pythonPackagePlatform }}"
36
46
 
37
47
  [tool.pyright]
nmk_python/version.yml CHANGED
@@ -26,4 +26,3 @@ tasks:
26
26
  deps:
27
27
  - git.version
28
28
  appendToDeps: setup
29
- if: ${pythonSrcFiles} # Only if python source files are found
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nmk-python
3
- Version: 1.6.0
3
+ Version: 1.7.0
4
4
  Summary: Python plugin for nmk build system
5
5
  Author: The dynod project
6
6
  Maintainer: The dynod project
@@ -0,0 +1,31 @@
1
+ nmk_python/__init__.py,sha256=EouCWXbAeObby1vOCx0Vx_WaGC2fEEFDMCen3JZt4TU,446
2
+ nmk_python/badges.yml,sha256=LzElZecCXKxdF-oPkvIwfnbQlOcAM_zUj6MelClmAug,749
3
+ nmk_python/build.py,sha256=gxyDjQdbwcxSk2INF7gnMqWRaa9G8gtrSsqWbgM-Uqg,8558
4
+ nmk_python/build.yml,sha256=CDGwrg2KKgdP1uDozQZIVaolxyEWKkb2ETbz0kBRkIQ,5544
5
+ nmk_python/doc.yml,sha256=12amAlYylH7JAHaqBIOoEZ_LrXY_bPNKuHrrwUkIj8w,932
6
+ nmk_python/files.py,sha256=K6Y-XKXuf8jh4x333Qgj6G1MyB-BgZ5Kd2-XCZhsfR4,2000
7
+ nmk_python/files.yml,sha256=jlp87gW9rTASUnsy7id7VWanaMUMN_py82bMa2adniM,813
8
+ nmk_python/plugin.yml,sha256=hi7osXyIwpHgJAsww5mhOA6emxhzgitDF8cOsvyY29Y,1068
9
+ nmk_python/project.yml,sha256=wBnB6KOglRvIpw1UHuXTYmuE_2BYGBzGWwIEbA5NDv8,985
10
+ nmk_python/ruff.py,sha256=TBmn5nFflcFRvjj36JtJGJ-GYHOK6XfhK9Jp-2dO5_E,1557
11
+ nmk_python/ruff.yml,sha256=FNmYjbv2l5vlqFXinY87LfgzpZ3PSiduB_xUiYw8aMw,3019
12
+ nmk_python/tests.py,sha256=8kRKP3dT2Thi5W5sCQSQOPXkV1D9C7tde3QYySo6G2c,1212
13
+ nmk_python/tests.yml,sha256=Sg6ZezkBVuvOzh-4QbJmZQymCOzXfenc0EpMdMZsEBc,1687
14
+ nmk_python/version.py,sha256=fLpOWdujYwlJB7XhWaOGCTeg3gQaClk2YHoRkkW64dE,3109
15
+ nmk_python/version.yml,sha256=WpZyQ2mChmvA_6eVb_Yp6ZaFIkX6oINxWBhKgb8h4GE,833
16
+ nmk_python/backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ nmk_python/backends/build_backend.py,sha256=yW5__VMb_dlHvQQ8svdrDSLHxWbGWHSxpbl_pJu9F8M,1292
18
+ nmk_python/backends/factory.py,sha256=NMYByORffwSSIjeNMLkOnwh6x1xhJQVhoV-0rSooq6Q,953
19
+ nmk_python/backends/pip_build_backend.py,sha256=amI0pf5pqQBDnCXhOc2ca_tiDm6v7XLLtW-Jhqd0w4U,1688
20
+ nmk_python/backends/uv_build_backend.py,sha256=3BTqirTrbAeFdxkSAp3ot2zcJIfApaX1GaMpd46yM1U,1872
21
+ nmk_python/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ nmk_python/templates/build.toml.jinja,sha256=6F1Kdb32JVBWEitxLC7AsUdhx2VTyQEyWhhSb7n3oBs,1770
23
+ nmk_python/templates/pytest.toml.jinja,sha256=zQ3SRWnF23Omxm7zInE54j0l3OyjXSOn5pQ5Pak8Boo,641
24
+ nmk_python/templates/python-settings.json.jinja,sha256=jYXSy96cX9X9GIHoF7XiZDzd4P2GOlMisAVWW626WF4,749
25
+ nmk_python/templates/python-test-debug-launch.json.jinja,sha256=J_xju2EpHNXvgvqBxlf99p7g-ZMh3xE-_2r7o_57gpU,364
26
+ nmk_python/templates/ruff.toml.jinja,sha256=DTHe1ixK1SBY8d2xBCfkGz89HzjAoMO7Ogs12AY1i4Y,346
27
+ nmk_python-1.7.0.dist-info/licenses/LICENSE,sha256=PROo9EW7wP6jmSr_90OluItAsIB411b_CAqEBioMKpI,1074
28
+ nmk_python-1.7.0.dist-info/METADATA,sha256=HSUvh9OSZ-iTijy1avmwcxLXMGevgRkMdtGNl0qEiLk,3148
29
+ nmk_python-1.7.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
30
+ nmk_python-1.7.0.dist-info/top_level.txt,sha256=MCb5w_hoOZ0kk3Dajxrd9Pz6lZ3pyuS6TQCRA3vddTo,11
31
+ nmk_python-1.7.0.dist-info/RECORD,,
@@ -1,26 +0,0 @@
1
- nmk_python/__init__.py,sha256=EouCWXbAeObby1vOCx0Vx_WaGC2fEEFDMCen3JZt4TU,446
2
- nmk_python/badges.yml,sha256=LzElZecCXKxdF-oPkvIwfnbQlOcAM_zUj6MelClmAug,749
3
- nmk_python/build.py,sha256=K8BowkROfrGwOPk7wKwOo9fsWJrrk8bDqJqliC876WI,6716
4
- nmk_python/build.yml,sha256=tbKjPRef7ZydEogsX3tAuQMR1B-vaMX5_q8q8tqzOnM,5046
5
- nmk_python/doc.yml,sha256=12amAlYylH7JAHaqBIOoEZ_LrXY_bPNKuHrrwUkIj8w,932
6
- nmk_python/files.py,sha256=gtPf6ldR5F4oGsAdzFnEIbI9MqqvF3z5JQnyONZmLmk,1863
7
- nmk_python/files.yml,sha256=-vGjogdulwsfdKVEUa5RARYDKxF3m9RmHKFnIhaT3_k,780
8
- nmk_python/plugin.yml,sha256=hi7osXyIwpHgJAsww5mhOA6emxhzgitDF8cOsvyY29Y,1068
9
- nmk_python/project.yml,sha256=sRIMvKkasrYlIvOo3DsVyNM_sgID_sHVgZg_bSIqtfw,975
10
- nmk_python/ruff.py,sha256=TBmn5nFflcFRvjj36JtJGJ-GYHOK6XfhK9Jp-2dO5_E,1557
11
- nmk_python/ruff.yml,sha256=FNmYjbv2l5vlqFXinY87LfgzpZ3PSiduB_xUiYw8aMw,3019
12
- nmk_python/tests.py,sha256=8kRKP3dT2Thi5W5sCQSQOPXkV1D9C7tde3QYySo6G2c,1212
13
- nmk_python/tests.yml,sha256=Sg6ZezkBVuvOzh-4QbJmZQymCOzXfenc0EpMdMZsEBc,1687
14
- nmk_python/version.py,sha256=fLpOWdujYwlJB7XhWaOGCTeg3gQaClk2YHoRkkW64dE,3109
15
- nmk_python/version.yml,sha256=amHNaA7WVnD6S1kL0IFD1KLKef9-2aEM3xU2ZkiZanc,905
16
- nmk_python/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- nmk_python/templates/build.toml.jinja,sha256=RJ-hCDc2SJG3K5rxK8tIIFcW-R7nt7Bu-x0wP9uPGGQ,1114
18
- nmk_python/templates/pytest.toml.jinja,sha256=zQ3SRWnF23Omxm7zInE54j0l3OyjXSOn5pQ5Pak8Boo,641
19
- nmk_python/templates/python-settings.json.jinja,sha256=jYXSy96cX9X9GIHoF7XiZDzd4P2GOlMisAVWW626WF4,749
20
- nmk_python/templates/python-test-debug-launch.json.jinja,sha256=J_xju2EpHNXvgvqBxlf99p7g-ZMh3xE-_2r7o_57gpU,364
21
- nmk_python/templates/ruff.toml.jinja,sha256=DTHe1ixK1SBY8d2xBCfkGz89HzjAoMO7Ogs12AY1i4Y,346
22
- nmk_python-1.6.0.dist-info/licenses/LICENSE,sha256=PROo9EW7wP6jmSr_90OluItAsIB411b_CAqEBioMKpI,1074
23
- nmk_python-1.6.0.dist-info/METADATA,sha256=0tKglaK0AkZMKrLPmsIuBmjKcN5sAeW-7r1zrKJuqHA,3148
24
- nmk_python-1.6.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
25
- nmk_python-1.6.0.dist-info/top_level.txt,sha256=MCb5w_hoOZ0kk3Dajxrd9Pz6lZ3pyuS6TQCRA3vddTo,11
26
- nmk_python-1.6.0.dist-info/RECORD,,