nmk-python 1.6.0__py3-none-any.whl → 1.8.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,56 @@
1
+ """
2
+ Python build backend definition module
3
+ """
4
+
5
+ from abc import ABC, abstractmethod
6
+ from pathlib import Path
7
+ from typing import cast
8
+
9
+ from nmk._internal.envbackend_legacy import EnvBackend # For type hinting
10
+ from nmk.model.model import NmkModel
11
+
12
+
13
+ class PythonBuildBackend(ABC):
14
+ """
15
+ Python build backend interface
16
+
17
+ :param model: NmkModel instance
18
+ """
19
+
20
+ def __init__(self, model: NmkModel):
21
+ self._model = model
22
+ self._env_backend = cast(EnvBackend, model.env_backend) # type: ignore
23
+
24
+ @abstractmethod
25
+ def install_editable(self): # pragma: no cover
26
+ """
27
+ Install current project as editable package
28
+ """
29
+ pass
30
+
31
+ @abstractmethod
32
+ def install_wheel(self, wheel_path: Path): # pragma: no cover
33
+ """
34
+ Install current project built wheel
35
+ """
36
+ pass
37
+
38
+ @abstractmethod
39
+ def build_wheel(self, build_dir: Path, built_wheel_name: str, wheel_version: str) -> Path: # pragma: no cover
40
+ """
41
+ Build current project wheel
42
+
43
+ :param build_dir: temporary build folder
44
+ :param built_wheel_name: name of the built wheel file
45
+ :return: path to built wheel
46
+ """
47
+ pass
48
+
49
+ @abstractmethod
50
+ def uninstall_wheels(self, wheel_names: list[str]): # pragma: no cover
51
+ """
52
+ Uninstall specified wheels
53
+
54
+ :param wheel_names: names of the wheels to uninstall
55
+ """
56
+ 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_wheels(self, wheel_names: list[str]):
49
+ # Prepare uninstall args
50
+ uninstall_args = ["uninstall", "--yes"] + wheel_names
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_wheels(self, wheel_names: list[str]):
55
+ # Same as setup
56
+ self._env_backend.upgrade(full=False, only_deps=True)
nmk_python/build.py CHANGED
@@ -2,35 +2,47 @@
2
2
  Python package build module
3
3
  """
4
4
 
5
+ import importlib.metadata
6
+ import json
5
7
  import shutil
6
- import sys
7
- from logging import Logger
8
+ from fnmatch import fnmatch
9
+ from itertools import product
8
10
  from pathlib import Path
11
+ from typing import cast
9
12
 
13
+ from nmk._internal.envbackend_legacy import EnvBackend # For type hinting
14
+ from nmk.logs import NmkLogWrapper
10
15
  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
16
+ from nmk.model.model import NmkModel
17
+ from nmk.model.resolver import NmkDictConfigResolver, NmkListConfigResolver, NmkStrConfigResolver
18
+ from nmk.utils import is_windows
19
+ from packaging.requirements import Requirement
20
+
21
+ from nmk_python.backends.factory import BuildBackendFactory
17
22
 
18
23
 
19
24
  # Install filter for Windows
20
- def _can_install(name: str, logger: Logger) -> bool:
25
+ def _can_install(name: str, logger: NmkLogWrapper) -> bool:
21
26
  # 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!")
27
+ if is_windows() and name in ["nmk", "buildenv"]:
28
+ logger.warning(f"Can't install {name} while running {name}!")
24
29
  return False
25
30
  return True
26
31
 
27
32
 
33
+ # Get python package from model
34
+ def _python_package(model: NmkModel) -> str:
35
+ cfg = model.config.get("pythonPackage")
36
+ assert cfg is not None and isinstance(cfg.value, str), "Can't read pythonPackage from model"
37
+ return cfg.value
38
+
39
+
28
40
  class PackageBuilder(NmkTaskBuilder):
29
41
  """
30
42
  Python package builder
31
43
  """
32
44
 
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]):
45
+ 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
46
  """
35
47
  Delegate to python build module, from a temporary build folder
36
48
 
@@ -49,21 +61,14 @@ class PackageBuilder(NmkTaskBuilder):
49
61
  build_path.mkdir(exist_ok=True, parents=True)
50
62
 
51
63
  # Copy source folders and various project files
52
- project_root = Path(self.model.config[NmkRootConfig.PROJECT_DIR].value)
64
+ project_path = Path(project_file)
65
+ project_root = project_path.parent
53
66
  for source_dir in map(Path, source_dirs):
54
67
  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)
68
+ for candidate in filter(lambda p: p.is_file(), map(Path, [project_path, version_file, project_root / "README.md", project_root / "LICENSE"])):
69
+ dest = build_path / candidate.relative_to(project_root)
70
+ dest.parent.mkdir(parents=True, exist_ok=True)
71
+ shutil.copyfile(candidate, dest)
67
72
 
68
73
  # Handle extra resources
69
74
  for src, dst in extra_resources.items():
@@ -90,18 +95,17 @@ class PackageBuilder(NmkTaskBuilder):
90
95
  for wheel in artifacts_path.glob("*.whl"):
91
96
  wheel.unlink()
92
97
 
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,
98
+ # Read version from file
99
+ wheel_version = Path(version_file).read_text().splitlines()[0]
100
+
101
+ # Delegate to build backend
102
+ built_wheel = BuildBackendFactory.create(self.model).build_wheel(
103
+ build_dir=build_path, built_wheel_name=self.main_output.name, wheel_version=wheel_version
98
104
  )
105
+ assert built_wheel.is_file(), f"Expected built wheel not found: {built_wheel}"
99
106
 
100
107
  # 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)
108
+ shutil.copyfile(built_wheel, self.main_output)
105
109
 
106
110
 
107
111
  class PythonModuleResolver(NmkStrConfigResolver):
@@ -113,27 +117,31 @@ class PythonModuleResolver(NmkStrConfigResolver):
113
117
  """
114
118
  Return module name from package (i.e. wheel) name
115
119
  """
116
- return self.model.config["pythonPackage"].value.replace("-", "_")
120
+ return _python_package(self.model).replace("-", "_")
117
121
 
118
122
 
119
- class Installer(VenvUpdateBuilder):
123
+ class Installer(NmkTaskBuilder):
120
124
  """
121
125
  Install built wheel in venv
122
126
  """
123
127
 
124
- def build(self, name: str, pip_args: list[str], to_remove: str):
128
+ def build(self, name: str, wheel: str, pip_args: list[str] | None = None, to_remove: str = ""): # pyright: ignore[reportIncompatibleMethodOverride]
125
129
  """
126
130
  Install wheel in venv
127
131
 
128
132
  :param name: wheel name to be installed
129
- :param pip_args: pip command line arguments
133
+ :param wheel: wheel path to be installed
134
+ :param pip_args: pip command line arguments; deprecated, not used anymore
130
135
  :param to_remove: stamp file to be removed
131
136
  """
132
137
 
133
138
  # Check if wheel can be installed
134
139
  if _can_install(name, self.logger):
135
- # Install wheel in current venv
136
- super().build(" ".join(pip_args))
140
+ # Delegate to build backend
141
+ eb = cast(EnvBackend, self.model.env_backend) # type: ignore
142
+ old_packages = eb.installed_packages
143
+ BuildBackendFactory.create(self.model).install_wheel(wheel_path=Path(wheel))
144
+ eb.print_updates(old_packages)
137
145
 
138
146
  # Remove stamp file
139
147
  Path(to_remove).unlink(missing_ok=True)
@@ -144,17 +152,28 @@ class Uninstaller(NmkTaskBuilder):
144
152
  Uninstall current project wheel from venv
145
153
  """
146
154
 
147
- def build(self, name: str):
155
+ def build(self, name: str, local_deps: list[str] | None = None): # pyright: ignore[reportIncompatibleMethodOverride]
148
156
  """
149
157
  Uninstall wheel from venv
150
158
 
151
159
  Note that task won't fail if the wheel is not installed
152
160
 
153
161
  :param name: wheel name to be uninstalled
162
+ :param local_deps: list of workspace local dependencies patterns, used to find additional wheels to uninstall
154
163
  """
155
164
 
156
- # Simply delegate to pip
157
- run_pip(["uninstall", "--yes", name], logger=self.logger)
165
+ # Find local deps to uninstall, from provided packages
166
+ wheel_names = set([name])
167
+ eb = cast(EnvBackend, self.model.env_backend) # type: ignore
168
+ old_packages = eb.installed_packages
169
+ for pattern, dep_name in product(local_deps if local_deps else [], old_packages.keys()):
170
+ if fnmatch(dep_name, pattern):
171
+ self.logger.debug(f"wheel name '{dep_name}' matches local dependency pattern '{pattern}', will be uninstalled")
172
+ wheel_names.add(dep_name)
173
+
174
+ # Delegate to build backend
175
+ BuildBackendFactory.create(self.model).uninstall_wheels(wheel_names=sorted(list(wheel_names)))
176
+ eb.print_updates(old_packages)
158
177
 
159
178
 
160
179
  class EditableBuilder(NmkTaskBuilder):
@@ -162,17 +181,20 @@ class EditableBuilder(NmkTaskBuilder):
162
181
  Install python project in editable mode
163
182
  """
164
183
 
165
- def build(self, pip_args: list[str]):
184
+ def build(self, pip_args: list[str] | None = None): # pyright: ignore[reportIncompatibleMethodOverride]
166
185
  """
167
186
  Install project in venv as editable package
168
187
 
169
- :param pip_args: pip command line arguments
188
+ :param pip_args: pip command line arguments; deprecated, not used anymore
170
189
  """
171
190
 
172
191
  # 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))
192
+ if _can_install(_python_package(self.model), self.logger):
193
+ # Delegate to build backend
194
+ eb = cast(EnvBackend, self.model.env_backend) # type: ignore
195
+ old_packages = eb.installed_packages
196
+ BuildBackendFactory.create(self.model).install_editable()
197
+ eb.print_updates(old_packages)
176
198
 
177
199
  # Touch stamp file
178
200
  self.main_output.touch()
@@ -183,8 +205,91 @@ class PythonOptionalDepsResolver(NmkListConfigResolver):
183
205
  Python optional deps resolver
184
206
  """
185
207
 
186
- def get_value(self, name: str, groups: dict[str, list[str]]) -> list[str]:
208
+ def get_value(self, name: str, groups: dict[str, list[str]]) -> list[str]: # pyright: ignore[reportIncompatibleMethodOverride]
187
209
  """
188
210
  Turn dependency options deps dict into a merged list of dependencies
189
211
  """
190
212
  return sorted(list(set(dependency for deps in groups.values() for dependency in deps)))
213
+
214
+
215
+ class PythonDevDepsResolver(NmkListConfigResolver):
216
+ """
217
+ Python development deps resolver
218
+ """
219
+
220
+ def get_value(self, name: str, package_deps: list[str], all_deps: list[str]) -> list[str]: # pyright: ignore[reportIncompatibleMethodOverride]
221
+ """
222
+ Return development dependencies (all deps minus package deps)
223
+ """
224
+ return sorted(list(set(all_deps) - set(package_deps)))
225
+
226
+
227
+ class PythonArchiveDepsResolver(NmkDictConfigResolver):
228
+ """
229
+ Python archive dependencies resolver
230
+ """
231
+
232
+ def get_value(self, name: str, archives: list[str]) -> dict[str, str]: # pyright: ignore[reportIncompatibleMethodOverride]
233
+ """
234
+ Return archive dependencies mapping (python package name -> wheel path)
235
+ """
236
+
237
+ # Iterate on existing archives paths
238
+ out: dict[str, str] = {}
239
+ for dep in filter(lambda p: p.is_file() and "-" in p.name, map(Path, archives)):
240
+ # Extract package name from wheel file name
241
+ dep_name = dep.name.split("-")[0].replace("_", "-")
242
+ out[dep_name] = str(dep.resolve()).replace("\\", "\\\\") # Escape backslashes for Windows paths
243
+ return out
244
+
245
+
246
+ class DepsMetadataBuilder(NmkTaskBuilder):
247
+ """
248
+ Generate python dependencies metadata file
249
+ """
250
+
251
+ def build(self, root_name: str, local_deps: list[str]): # pyright: ignore[reportIncompatibleMethodOverride]
252
+ """
253
+ Generate python dependencies metadata file
254
+
255
+ :param root_name: name of the root package
256
+ :param local_deps: list of workspace local dependencies patterns
257
+ """
258
+
259
+ # Normalization implementation
260
+ def normalize(name: str) -> str:
261
+ return name.lower().replace("_", "-")
262
+
263
+ # Prepare distributions map
264
+ distributions = {normalize(d.name): d for d in importlib.metadata.distributions()}
265
+ assert root_name in distributions, f"Root package '{root_name}' not found in installed distributions"
266
+ output_data: dict[str, str] = {}
267
+
268
+ # Visitor implementation
269
+ def visit(name: str):
270
+ # Already visited or unknown?
271
+ if (name in output_data) or (name not in distributions):
272
+ return
273
+
274
+ # If this is a local project, don't add metadata
275
+ if (name != root_name) and not any(fnmatch(name, pattern) for pattern in local_deps):
276
+ # Keep versions
277
+ output_data[name] = distributions[name].version
278
+
279
+ # Visit dependencies
280
+ for dep in distributions[name].requires or []:
281
+ # Get requirement
282
+ req = Requirement(dep)
283
+
284
+ # Check marker, if any
285
+ if req.marker is not None and not req.marker.evaluate():
286
+ continue
287
+
288
+ # Visit dependency if installed
289
+ visit(normalize(req.name))
290
+
291
+ # Visit from root
292
+ visit(root_name)
293
+
294
+ # Write output metadata file
295
+ self.main_output.write_text(json.dumps({k: output_data[k] for k in sorted(output_data)}, indent=4))
nmk_python/build.yml CHANGED
@@ -1,155 +1,192 @@
1
1
  config:
2
-
3
- # Python package name (to be configured at project level)
4
- pythonPackage: ${projectName}
5
- pythonModule:
6
- __resolver__: nmk_python.build.PythonModuleResolver
7
-
8
- # Python artifacts directory
9
- pythonArtifacts: ${outputDir}/artifacts
10
-
11
- # Python build directory
12
- pythonBuildDir: ${outputDir}/python
13
-
14
- # Target platform for python project
15
- pythonPackagePlatform: any
16
-
17
- # Python source distribution + wheel artifacts
18
- pythonWheel: ${pythonArtifacts}/${pythonModule}-${pythonVersion}-py3-none-${pythonPackagePlatform}.whl
19
-
20
- # Add project file fragment for build config
21
- pythonProjectFileFragments:
22
- - ${BASEDIR}/templates/build.toml.jinja
23
-
24
- # Python optional deps (named groups of python package optional dependencies)
25
- pythonPackageOptionalRequirements: {}
26
-
27
- # Derived list of python dependencies from the dependency groups
28
- pythonPackageOptionalRequirementsList:
29
- __resolver__: nmk_python.build.PythonOptionalDepsResolver
30
- params:
31
- groups: ${pythonPackageOptionalRequirements}
32
- pythonProjectFileItems:
33
- project:
34
- optional-dependencies: ${pythonPackageOptionalRequirements}
35
-
36
- # Python package dependencies
37
- pythonPackageRequirements: []
38
-
39
- # Also install in venv all dependencies
40
- venvPkgDeps:
41
- - ${pythonPackageRequirements}
42
- - ${pythonPackageOptionalRequirementsList}
43
-
44
- # Extra (external) resources to be bundled in built python wheel
45
- pythonExtraResources: {}
46
-
47
- # Python editable install stamp
48
- pythonEditableStamp: ${outputDir}/.pyeditable
49
-
50
- # Python files to be ignored
51
- gitIgnoredFiles:
52
- - .eggs/
53
- - "*.egg-info/"
54
- - "*.pyc"
55
- - __pycache__
56
-
57
- # Additional github publish step: publish on pypi.org
58
- githubPublishSteps:
59
- - name: Publish package on pypi.org
60
- uses: pypa/gh-action-pypi-publish@v1.13.0
61
- with:
62
- user: __token__
63
- password: ${{ secrets.pypi_password }}
64
- packages_dir: ${r!pythonArtifacts}
65
-
66
- # Wheel install arguments
67
- pythonWheelInstallArgs:
68
- - --force-reinstall # force reinstall because otherwise install will be skipped if version didn't changed
69
- - --no-deps # but skip dependencies to avoid reinstalling everything (deps are already installed by py.venv task)
70
-
71
- # Editable install arguments
72
- pythonEditableInstallArgs:
73
- - --no-deps # skip dependencies to avoid reinstalling everything
74
- - --no-build-isolation # disable dependencies install when building for "editable" mode
75
-
76
- # Type checking mode for Pyright; may be off, basic, standard, strict
77
- pythonTypeCheckingMode: strict
78
-
79
- # Excluded modules for wheel build
80
- pythonBuildExcludedModules: []
2
+ # Python package name (to be configured at project level)
3
+ pythonPackage: ${projectName}
4
+ pythonModule:
5
+ __resolver__: nmk_python.build.PythonModuleResolver
6
+
7
+ # Python artifacts directory
8
+ pythonArtifacts: ${outputDir}/artifacts
9
+
10
+ # Python build directory
11
+ pythonBuildDir: ${outputDir}/python
12
+
13
+ # Target platform for python project
14
+ pythonPackagePlatform: any
15
+
16
+ # Python source distribution + wheel artifacts
17
+ pythonWheel: ${pythonArtifacts}/${pythonModule}-${pythonVersion}-py3-none-${pythonPackagePlatform}.whl
18
+
19
+ # Add project file fragment for build config
20
+ pythonProjectFileFragments:
21
+ - ${BASEDIR}/templates/build.toml.jinja
22
+
23
+ # Python optional deps (named groups of python package optional dependencies)
24
+ pythonPackageOptionalRequirements: {}
25
+
26
+ # Derived list of python dependencies from the dependency groups
27
+ pythonPackageOptionalRequirementsList:
28
+ __resolver__: nmk_python.build.PythonOptionalDepsResolver
29
+ params:
30
+ groups: ${pythonPackageOptionalRequirements}
31
+ pythonProjectFileItems:
32
+ project:
33
+ optional-dependencies: ${pythonPackageOptionalRequirements}
34
+
35
+ # Python package dependencies
36
+ pythonPackageRequirements: []
37
+
38
+ # Python development dependencies
39
+ pythonDevRequirements:
40
+ __resolver__: nmk_python.build.PythonDevDepsResolver
41
+ params:
42
+ package_deps: ${pythonPackageRequirements}
43
+ all_deps: ${venvPkgDeps}
44
+
45
+ # Archive related requirements
46
+ pythonArchiveRequirements:
47
+ __resolver__: nmk_python.build.PythonArchiveDepsResolver
48
+ params:
49
+ archives: ${venvArchiveDeps}
50
+
51
+ # Also install in venv all dependencies
52
+ venvPkgDeps:
53
+ - ${pythonPackageRequirements}
54
+ - ${pythonPackageOptionalRequirementsList}
55
+
56
+ # Extra (external) resources to be bundled in built python wheel
57
+ pythonExtraResources: {}
58
+
59
+ # Python editable install stamp
60
+ pythonEditableStamp: ${outputDir}/.pyeditable
61
+
62
+ # Python files to be ignored
63
+ gitIgnoredFiles:
64
+ - .eggs/
65
+ - "*.egg-info/"
66
+ - "*.pyc"
67
+ - __pycache__
68
+
69
+ # Additional github publish step: publish on pypi.org
70
+ githubPublishSteps:
71
+ - name: Publish package on pypi.org
72
+ uses: pypa/gh-action-pypi-publish@v1.13.0
73
+ with:
74
+ user: __token__
75
+ password: ${{ secrets.pypi_password }}
76
+ packages_dir: ${r!pythonArtifacts}
77
+
78
+ # Wheel install arguments (for pip backend)
79
+ pythonWheelPipInstallArgs:
80
+ - --force-reinstall # force reinstall because otherwise install will be skipped if version didn't changed
81
+ - --no-deps # but skip dependencies to avoid reinstalling everything (deps are already installed by py.venv task)
82
+
83
+ # Editable install arguments (for pip backend)
84
+ pythonEditablePipInstallArgs:
85
+ - --no-deps # skip dependencies to avoid reinstalling everything
86
+ - --no-build-isolation # disable dependencies install when building for "editable" mode
87
+
88
+ # Wheel install arguments (for uv backend)
89
+ pythonWheelUvInstallArgs: []
90
+
91
+ # Editable install arguments (for uv backend)
92
+ pythonEditableUvInstallArgs: []
93
+
94
+ # Type checking mode for Pyright; may be off, basic, standard, strict
95
+ pythonTypeCheckingMode: strict
96
+
97
+ # Excluded modules for wheel build
98
+ pythonBuildExcludedModules: []
99
+
100
+ # Workspace local dependencies patterns
101
+ pythonLocalDepsPatterns: []
102
+
103
+ # Generated python dependencies metadata file
104
+ pythonDepsMetadata: ${outputDir}/python_deps.json
81
105
 
82
106
  tasks:
83
-
84
- # Python editable install
85
- py.editable:
86
- description: Install python editable package
87
- emoji: ":snake:.:house:"
88
- deps:
89
- - py.project
90
- - py.format
91
- - py.analyze
92
- appendToDeps: build
93
- if: ${pythonSrcFiles} # Only if python source files are found
94
- output:
95
- - ${pythonEditableStamp}
96
- input:
97
- - ${pythonProjectFile}
98
- builder: nmk_python.build.EditableBuilder
99
- params:
100
- pip_args:
101
- - ${pythonEditableInstallArgs}
102
-
103
- # Python wheel build
104
- py.build:
105
- description: Build python wheel
106
- emoji: ":snake:.:hammer:"
107
- deps:
108
- - py.project
109
- - py.format
110
- - py.analyze
111
- appendToDeps: package
112
- if: ${pythonSrcFiles} # Only if python source files are found
113
- output:
114
- - ${pythonWheel}
115
- input:
116
- - ${pythonSrcFiles}
117
- - ${pythonProjectFile}
118
- builder: nmk_python.build.PackageBuilder
119
- params:
120
- project_file: ${pythonProjectFile}
121
- version_file: ${pythonVersionStamp}
122
- source_dirs: ${pythonSrcFolders}
123
- artifacts_dir: ${pythonArtifacts}
124
- build_dir: ${pythonBuildDir}
125
- extra_resources: ${pythonExtraResources}
126
-
127
- # Python wheel install
128
- py.install:
129
- description: Install python wheel
130
- emoji: ":snake:.:inbox_tray:"
131
- deps:
132
- - py.build
133
- appendToDeps: install
134
- if: ${pythonSrcFiles} # Only if python source files are found
135
- input: ${pythonWheel}
136
- output:
137
- - ${ROOTDIR}/${venvName}
138
- - ${venvState}
139
- builder: nmk_python.build.Installer
140
- params:
141
- name: ${pythonPackage}
142
- pip_args:
143
- - ${pythonWheelInstallArgs}
144
- to_remove: ${pythonEditableStamp}
145
-
146
- # Python module clean
147
- py.uninstall:
148
- description: Uninstall python package
149
- emoji: ":broom:.:snake:"
150
- appendToDeps:
151
- - clean
152
- builder: nmk_python.build.Uninstaller
153
- params:
154
- name: ${pythonPackage}
155
- if: ${pythonSrcFiles} # Only if python source files are found
107
+ # Python editable install
108
+ py.editable:
109
+ description: Install python editable package
110
+ emoji: ":snake:.:house:"
111
+ deps:
112
+ - py.project
113
+ - py.format
114
+ - py.analyze
115
+ appendToDeps: build
116
+ if: ${pythonSrcFiles} # Only if python source files are found
117
+ output:
118
+ - ${pythonEditableStamp}
119
+ input:
120
+ - ${pythonProjectFile}
121
+ builder: nmk_python.build.EditableBuilder
122
+
123
+ # Python dependencies metadata
124
+ py.deps:
125
+ description: Gather python dependencies metadata
126
+ emoji: ":snake:.:link:"
127
+ deps:
128
+ - py.editable
129
+ appendToDeps: build
130
+ if: ${pythonSrcFiles} # Only if python source files are found
131
+ output:
132
+ - ${pythonDepsMetadata}
133
+ input:
134
+ - ${pythonProjectFile}
135
+ builder: nmk_python.build.DepsMetadataBuilder
136
+ params:
137
+ root_name: ${pythonPackage}
138
+ local_deps: ${pythonLocalDepsPatterns}
139
+
140
+ # Python wheel build
141
+ py.build:
142
+ description: Build python wheel
143
+ emoji: ":snake:.:hammer:"
144
+ deps:
145
+ - py.project
146
+ - py.format
147
+ - py.analyze
148
+ appendToDeps: package
149
+ if: ${pythonSrcFiles} # Only if python source files are found
150
+ output:
151
+ - ${pythonWheel}
152
+ input:
153
+ - ${pythonSrcFiles}
154
+ - ${pythonProjectFile}
155
+ builder: nmk_python.build.PackageBuilder
156
+ params:
157
+ project_file: ${pythonProjectFile}
158
+ version_file: ${pythonVersionStamp}
159
+ source_dirs: ${pythonSrcFolders}
160
+ artifacts_dir: ${pythonArtifacts}
161
+ build_dir: ${pythonBuildDir}
162
+ extra_resources: ${pythonExtraResources}
163
+
164
+ # Python wheel install
165
+ py.install:
166
+ description: Install python wheel
167
+ emoji: ":snake:.:inbox_tray:"
168
+ deps:
169
+ - py.build
170
+ appendToDeps: install
171
+ if: ${pythonSrcFiles} # Only if python source files are found
172
+ input: ${pythonWheel}
173
+ output:
174
+ - ${ROOTDIR}/${venvName}
175
+ - ${venvState}
176
+ builder: nmk_python.build.Installer
177
+ params:
178
+ name: ${pythonPackage}
179
+ wheel: ${pythonWheel}
180
+ to_remove: ${pythonEditableStamp}
181
+
182
+ # Python module clean
183
+ py.uninstall:
184
+ description: Uninstall python package and local dependencies
185
+ emoji: ":broom:.:snake:"
186
+ appendToDeps:
187
+ - clean
188
+ builder: nmk_python.build.Uninstaller
189
+ params:
190
+ name: ${pythonPackage}
191
+ local_deps: ${pythonLocalDepsPatterns}
192
+ if: ${pythonSrcFiles} # Only if python source files are found
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.8.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=bzpS1lUotsgdwBJH6Jgl2guAva1M0XDlKzwfj_lfZ1Y,11368
4
+ nmk_python/build.yml,sha256=8CWVacVv3r3pbvuRkaLQbH1aTO-oK4U0hAO9SgDTfFA,5726
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=NOAXMa6jmAIfUvA690ht-x_DwsGtvBvttPZLfFcX_b0,1437
18
+ nmk_python/backends/factory.py,sha256=NMYByORffwSSIjeNMLkOnwh6x1xhJQVhoV-0rSooq6Q,953
19
+ nmk_python/backends/pip_build_backend.py,sha256=4MGYghBeMjZhUlKTwE0svSHArYwOAaK1I6_gmeNnwaI,1698
20
+ nmk_python/backends/uv_build_backend.py,sha256=QpN5nMm_QhnKzoHQcX4769OOQG6NqOD2ctYXyH2MI7A,1880
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.8.0.dist-info/licenses/LICENSE,sha256=PROo9EW7wP6jmSr_90OluItAsIB411b_CAqEBioMKpI,1074
28
+ nmk_python-1.8.0.dist-info/METADATA,sha256=wW1y07Ek_meONzj-P7sZd9h_8d1tEsKMKN7xm-DD2pQ,3148
29
+ nmk_python-1.8.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
30
+ nmk_python-1.8.0.dist-info/top_level.txt,sha256=MCb5w_hoOZ0kk3Dajxrd9Pz6lZ3pyuS6TQCRA3vddTo,11
31
+ nmk_python-1.8.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,,