nmk-python 0.14.0__py3-none-any.whl → 0.15.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.
nmk_python/__init__.py CHANGED
@@ -1,23 +1,21 @@
1
- from configparser import ConfigParser
2
- from pathlib import Path
1
+ """
2
+ Python support for nmk
3
+ """
4
+
5
+ from importlib.metadata import version
3
6
 
4
7
  from nmk_base.version import VersionResolver
5
- from pkg_resources import DistributionNotFound, get_distribution
6
8
 
7
9
  __title__ = "nmk-python"
8
10
  try:
9
- __version__ = get_distribution(__title__).version
10
- except DistributionNotFound: # pragma: no cover
11
- # For debug
12
- try:
13
- with (Path(__file__).parent.parent.parent / "setup.cfg").open("r") as f:
14
- c = ConfigParser()
15
- c.read_file(f.readlines())
16
- __version__ = c.get("metadata", "version")
17
- except Exception:
18
- __version__ = "unknown"
11
+ __version__ = version(__title__)
12
+ except Exception: # pragma: no cover
13
+ __version__ = "unknown"
19
14
 
20
15
 
21
16
  class NmkPythonVersionResolver(VersionResolver):
17
+ """Plugin version resolver"""
18
+
22
19
  def get_version(self) -> str:
20
+ """Returns nmk-python plugin version"""
23
21
  return __version__
nmk_python/badges.yml CHANGED
@@ -7,13 +7,13 @@ config:
7
7
  - alt: PyPI
8
8
  img: https://img.shields.io/pypi/v/${pythonPackage}
9
9
  url: https://pypi.org/project/${pythonPackage}/
10
- - alt: "Code style: black"
11
- img: https://img.shields.io/badge/code%20style-black-000000.svg
12
- url: https://github.com/psf/black
10
+ - alt: "Code style: ruff"
11
+ img: https://img.shields.io/badge/code%20style-ruff-000000.svg
12
+ url: https://astral.sh/ruff
13
13
  if: ${pythonFoundSrcFiles}
14
- - alt: "Flake8 analysis result"
15
- img: https://img.shields.io/badge/flake8-0-green
16
- url: https://flake8.pycqa.org/
14
+ - alt: "Ruff analysis result"
15
+ img: https://img.shields.io/badge/ruff-0-green
16
+ url: https://astral.sh/ruff
17
17
  if: ${pythonFoundSrcFiles}
18
18
  - alt: "Code coverage"
19
19
  img: https://img.shields.io/codecov/c/github/${githubUser}/${githubRepo}
nmk_python/build.py CHANGED
@@ -1,29 +1,125 @@
1
+ """
2
+ Python package build module
3
+ """
4
+
5
+ import shutil
1
6
  import sys
7
+ from pathlib import Path
8
+ from typing import Dict, List
2
9
 
3
10
  from nmk.model.builder import NmkTaskBuilder
4
11
  from nmk.model.keys import NmkRootConfig
5
12
  from nmk.model.resolver import NmkStrConfigResolver
6
13
  from nmk.utils import is_windows, run_with_logs
7
14
  from nmk_base.venvbuilder import VenvUpdateBuilder
15
+ from tomlkit import loads
16
+ from tomlkit.toml_file import TOMLFile
8
17
 
9
18
 
10
19
  class PackageBuilder(NmkTaskBuilder):
11
- def build(self, artifacts: str, build_dir: str):
12
- # Delegate to setup
20
+ """
21
+ Python package builder
22
+ """
23
+
24
+ def build(self, project_file: str, version_file: str, source_dirs: List[str], artifacts_dir: str, build_dir: str, extra_resources: Dict[str, str]):
25
+ """
26
+ Delegate to python build module, from a temporary build folder
27
+
28
+ :param project_file: path to python project file
29
+ :param version_file: path to generated version file
30
+ :param source_dirs: list of source folders for this wheel
31
+ :param artifacts_dir: output folder for built wheel
32
+ :param build_dir: temporary build folder
33
+ :param extra_resources: dictionary of extra resources mapping (original path -> target path)
34
+ """
35
+
36
+ # Prepare build folder
37
+ build_path = Path(build_dir)
38
+ if build_path.is_dir():
39
+ shutil.rmtree(build_path)
40
+ build_path.mkdir(exist_ok=True, parents=True)
41
+
42
+ # Copy source folders and various project files
43
+ project_root = Path(self.model.config[NmkRootConfig.PROJECT_DIR].value)
44
+ for source_dir in map(Path, source_dirs):
45
+ shutil.copytree(source_dir, build_path / source_dir.relative_to(project_root))
46
+ for candidate in filter(lambda p: p.is_file(), map(Path, [project_file, version_file, project_root / "README.md", project_root / "LICENSE"])):
47
+ shutil.copyfile(candidate, build_path / candidate.name)
48
+
49
+ # Update project file with version
50
+ build_project = build_path / Path(project_file).name
51
+ build_version = build_path / Path(version_file).name
52
+ with build_project.open() as f:
53
+ project_doc = loads(f.read())
54
+ dyn_table = project_doc.get("tool").get("setuptools").get("dynamic")
55
+ dyn_table["version"] = {"file": build_version.name}
56
+ project_output = TOMLFile(build_project)
57
+ project_output.write(project_doc)
58
+
59
+ # Handle extra resources
60
+ for src, dst in extra_resources.items():
61
+ src_path, dst_path = Path(src), Path(dst)
62
+ if not src_path.is_absolute(): # pragma: no branch
63
+ src_path = project_root / src_path
64
+ if not dst_path.is_absolute(): # pragma: no branch
65
+ dst_path = project_root / dst_path
66
+ assert src_path.exists(), f"Required extra resource path not found: {src_path}"
67
+ dst_path = build_path / dst_path.relative_to(project_root)
68
+ dst_path.mkdir(exist_ok=True, parents=True)
69
+ if src_path.is_file():
70
+ # Single file copy
71
+ self.logger.debug(f"Copy extra resource file: {src_path} --> {dst_path}")
72
+ shutil.copyfile(src_path, dst_path / src_path.name)
73
+ else:
74
+ # Directory copy
75
+ self.logger.debug(f"Copy extra resource tree: {src_path} --> {dst_path}")
76
+ shutil.copytree(src_path, dst_path, dirs_exist_ok=True)
77
+
78
+ # Prepare artifacts folder
79
+ artifacts_path = Path(artifacts_dir)
80
+ artifacts_path.mkdir(exist_ok=True, parents=True)
81
+ for wheel in artifacts_path.glob("*.whl"):
82
+ wheel.unlink()
83
+
84
+ # Delegate to build module
13
85
  run_with_logs(
14
- [sys.executable, "-c", "from setuptools import setup; setup()", "build", "-b", build_dir, "bdist_wheel", "-d", artifacts],
86
+ [sys.executable, "-m", "build", "--wheel", "--skip-dependency-check", "--no-isolation"],
15
87
  self.logger,
16
- cwd=self.model.config[NmkRootConfig.PROJECT_DIR].value,
88
+ cwd=build_path,
17
89
  )
18
90
 
91
+ # Copy wheel to artifacts folder
92
+ target_wheel = self.main_output
93
+ built_wheel = build_path / "dist" / target_wheel.name
94
+ assert built_wheel.is_file(), f"Expected built wheel not found: {built_wheel}"
95
+ shutil.copyfile(built_wheel, target_wheel)
96
+
97
+
98
+ class PythonModuleResolver(NmkStrConfigResolver):
99
+ """
100
+ Python module name resolver
101
+ """
19
102
 
20
- class PythonPackageForWheel(NmkStrConfigResolver):
21
103
  def get_value(self, name: str) -> str:
104
+ """
105
+ Return module name from package (i.e. wheel) name
106
+ """
22
107
  return self.model.config["pythonPackage"].value.replace("-", "_")
23
108
 
24
109
 
25
110
  class Installer(VenvUpdateBuilder):
111
+ """
112
+ Install built wheel in venv
113
+ """
114
+
26
115
  def build(self, name: str, pip_args: str):
116
+ """
117
+ Install wheel in venv
118
+
119
+ :param name: wheel name to be installed
120
+ :param pip_args: pip command line arguments
121
+ """
122
+
27
123
  # On Windows, refuse to install nmk package while running nmk (wont' work)
28
124
  if is_windows() and name == "nmk":
29
125
  self.logger.warning("Can't install nmk while running nmk!")
nmk_python/build.yml CHANGED
@@ -2,8 +2,8 @@ config:
2
2
 
3
3
  # Python package name (to be configured at project level)
4
4
  pythonPackage: ${projectName}
5
- pythonPackageForWheel:
6
- __resolver__: nmk_python.build.PythonPackageForWheel
5
+ pythonModule:
6
+ __resolver__: nmk_python.build.PythonModuleResolver
7
7
 
8
8
  # Python artifacts directory
9
9
  pythonArtifacts: ${outputDir}/artifacts
@@ -11,21 +11,27 @@ config:
11
11
  # Python build directory
12
12
  pythonBuildDir: ${outputDir}/python
13
13
 
14
+ # Target platform for python project
15
+ pythonPackagePlatform: any
16
+
14
17
  # Python source distribution + wheel artifacts
15
- pythonDist: ${pythonArtifacts}/${pythonPackage}-${pythonVersion}.tar.gz
16
- pythonWheel: ${pythonArtifacts}/${pythonPackageForWheel}-${pythonVersion}-py3-none-any.whl
18
+ pythonWheel: ${pythonArtifacts}/${pythonModule}-${pythonVersion}-py3-none-${pythonPackagePlatform}.whl
17
19
 
18
- # Add setup file for build config
19
- pythonSetupCfgFiles:
20
- - ${BASEDIR}/templates/build.cfg
20
+ # Add project file fragment for build config
21
+ pythonProjectFileFragments:
22
+ - ${BASEDIR}/templates/build.toml.jinja
21
23
 
22
24
  # Python package dependencies (also contributed to venv)
23
25
  pythonPackageRequirements: []
24
26
  venvPkgDeps:
25
27
  - setuptools
26
28
  - wheel
29
+ - build
27
30
  - ${pythonPackageRequirements}
28
31
 
32
+ # Extra (external) resources to be bundled in built python wheel
33
+ pythonExtraResources: {}
34
+
29
35
  # Python files to be ignored
30
36
  gitIgnoredFiles:
31
37
  - .eggs/
@@ -49,20 +55,24 @@ tasks:
49
55
  description: Build python package
50
56
  emoji: ":snake:.:hammer:"
51
57
  deps:
52
- - py.setup
58
+ - py.project
59
+ - py.format
53
60
  - py.analyze
54
61
  appendToDeps: build
55
62
  if: ${pythonSrcFiles} # Only if python source files are found
56
63
  output:
57
- - ${pythonDist}
58
64
  - ${pythonWheel}
59
65
  input:
60
66
  - ${pythonSrcFiles}
61
- - ${pythonSetupCfg}
67
+ - ${pythonProjectFile}
62
68
  builder: nmk_python.build.PackageBuilder
63
69
  params:
64
- artifacts: ${pythonArtifacts}
70
+ project_file: ${pythonProjectFile}
71
+ version_file: ${pythonVersionStamp}
72
+ source_dirs: ${pythonSrcFolders}
73
+ artifacts_dir: ${pythonArtifacts}
65
74
  build_dir: ${pythonBuildDir}
75
+ extra_resources: ${pythonExtraResources}
66
76
 
67
77
  # Python module install
68
78
  py.install:
nmk_python/files.py CHANGED
@@ -1,3 +1,7 @@
1
+ """
2
+ Python files resolvers
3
+ """
4
+
1
5
  from pathlib import Path
2
6
  from typing import List
3
7
 
@@ -5,19 +9,44 @@ from nmk.model.resolver import NmkListConfigResolver
5
9
 
6
10
 
7
11
  class FilesFinder(NmkListConfigResolver):
12
+ """
13
+ Shared logic for files resolution
14
+ """
15
+
8
16
  def find_in_folders(self) -> List[str]: # pragma: no cover
17
+ """
18
+ Folders to be browsed (to be overridden)
19
+
20
+ :return: List of folders to be browsed for python files
21
+ """
9
22
  pass
10
23
 
11
24
  def get_value(self, name: str) -> List[Path]:
12
- # Iterate on source paths, and find all python files
13
- return [src_file for src_path in map(Path, self.find_in_folders()) for src_file in filter(lambda f: f.is_file(), src_path.rglob("*.py"))]
25
+ """
26
+ Browse for source files in specified folders
27
+
28
+ :param name: Config item name
29
+ :return: List of found source files
30
+ """
31
+ # Iterate on source paths, and find all source files
32
+ return [src_file for src_path in map(Path, self.find_in_folders()) for src_file in filter(lambda f: f.is_file(), src_path.rglob("*"))]
14
33
 
15
34
 
16
35
  class PythonFilesFinder(FilesFinder):
36
+ """Regular python files resolver"""
37
+
17
38
  def find_in_folders(self) -> List[str]:
39
+ """
40
+ Python source folders
41
+ """
18
42
  return self.model.config["pythonSrcFolders"].value
19
43
 
20
44
  def get_value(self, name: str) -> List[Path]:
45
+ """
46
+ Browse for python files in specified folders
47
+
48
+ Also and make sure they don't overlap with generated and test source code
49
+ """
21
50
  # All found files
22
51
  all_files = set(super().get_value(name))
23
52
 
@@ -29,5 +58,10 @@ class PythonFilesFinder(FilesFinder):
29
58
 
30
59
 
31
60
  class PythonTestFilesFinder(FilesFinder):
61
+ """Test python files resolver"""
62
+
32
63
  def find_in_folders(self) -> List[str]:
64
+ """
65
+ Python tests source folders
66
+ """
33
67
  return [self.model.config["pythonTestSources"].value]
nmk_python/files.yml CHANGED
@@ -3,8 +3,11 @@ config:
3
3
  pythonSrcFolders:
4
4
  - ${sourceDir}
5
5
 
6
+ # Package holding test code
7
+ pythonTestPackage: tests
8
+
6
9
  # Root folder for test sources
7
- pythonTestSources: ${sourceDir}/tests
10
+ pythonTestSources: ${sourceDir}/${pythonTestPackage}
8
11
 
9
12
  # Found Python test source files
10
13
  pythonTestSrcFiles:
nmk_python/plugin.yml CHANGED
@@ -3,10 +3,9 @@ refs:
3
3
  - pip://nmk-base!plugin.yml
4
4
  - files.yml # Look for python source files
5
5
  - version.yml # Python version contribution
6
- - setup.yml # Python setup files generation
7
- - format.yml # Python code format (with black/isort)
8
- - flake.yml # Python code analysis (with flake8)
6
+ - project.yml # Python project file generation
9
7
  - build.yml # Python module build/install
8
+ - ruff.yml # Python code format/analysis (with ruff)
10
9
  - tests.yml # Python automated tests
11
10
  - badges.yml # Python badges
12
11
  - doc.yml # Python doc
@@ -22,7 +21,7 @@ config:
22
21
 
23
22
  # Plugin help links dict
24
23
  nmkPluginsDocs:
25
- nmk-python: https://github.com/dynod/nmk/wiki/nmk-python-plugin
24
+ nmk-python: https://nmk-python.readthedocs.io/
26
25
 
27
26
  # Contribution to VSCode settings/launch/extensions (if plugin is used)
28
27
  vscodeSettingsFiles:
nmk_python/project.py ADDED
@@ -0,0 +1,74 @@
1
+ """
2
+ Python project file builder
3
+ """
4
+
5
+ from pathlib import Path
6
+ from typing import List
7
+
8
+ from nmk_base.common import TemplateBuilder
9
+ from tomlkit import TOMLDocument, comment, loads
10
+ from tomlkit.toml_file import TOMLFile
11
+
12
+
13
+ class PythonProjectBuilder(TemplateBuilder):
14
+ """
15
+ Python project file builder
16
+ """
17
+
18
+ def _contribute(self, main: dict, update: dict):
19
+ """
20
+ Merge items from **update** dictionary into **main** one.
21
+ Merge logic for existing items is:
22
+
23
+ * for lists: new items are appended after existing ones
24
+ * for dictionaries: existing dictionary is updated with new one content
25
+ * for other types: existing values are replaces with new ones
26
+
27
+ :param main: Settings dictionary to be updated
28
+ :param update: Update dictionary to be merged into the existing one
29
+ """
30
+
31
+ for k, v in update.items():
32
+ # Already exists in target model?
33
+ if k in main:
34
+ # List: extend
35
+ if isinstance(v, list):
36
+ main[k].extend(v)
37
+ # Map: recursive contribution
38
+ elif isinstance(v, dict):
39
+ self._contribute(main[k], v)
40
+ # Otherwise: replace
41
+ else:
42
+ main[k] = v
43
+ else:
44
+ # New key
45
+ main[k] = v
46
+
47
+ def build(self, fragment_files: List[str], items: dict):
48
+ """
49
+ Generates python project file from fragments and items
50
+
51
+ :param fragment_files: List of fragment files to be merged
52
+ :param items: Dict of project items to be merged
53
+ """
54
+
55
+ # Merge project fragments to generate final project
56
+ project = {}
57
+ for f_path in map(Path, fragment_files):
58
+ try:
59
+ # Update document with rendered template
60
+ fragment_doc = loads(self.render_template(f_path, {}))
61
+ except Exception as e:
62
+ # Propagate error with file name
63
+ raise ValueError(f"While loading project file template ({f_path}): {e}") from e
64
+ self._contribute(project, fragment_doc.unwrap())
65
+
66
+ # Iterate on items contributed through yml project files (only ones contributing maps)
67
+ self._contribute(project, {k: v for k, v in items.items() if isinstance(v, dict)})
68
+
69
+ # Finally write config to output file
70
+ doc = TOMLDocument()
71
+ doc.add(comment("Please don't edit: generated by nmk-python plugin"))
72
+ doc.update(project)
73
+ project_output = TOMLFile(self.main_output)
74
+ project_output.write(doc)
nmk_python/project.yml ADDED
@@ -0,0 +1,31 @@
1
+ config:
2
+ # Generated project file
3
+ pythonProjectFile: ${PROJECTDIR}/pyproject.toml
4
+
5
+ # Paths to pyproject.toml fragments to be merged
6
+ pythonProjectFileFragments: []
7
+
8
+ # Contributed project items from yml files
9
+ pythonProjectFileItems: {}
10
+
11
+ # Require VSCode specific extension for toml support
12
+ vscodeExtensionsNames:
13
+ - tamasfe.even-better-toml
14
+
15
+ 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_python.project.PythonProjectBuilder
29
+ params:
30
+ fragment_files: ${pythonProjectFileFragments}
31
+ items: ${pythonProjectFileItems}
@@ -1,4 +1,8 @@
1
- import sys
1
+ """
2
+ Ruff tool handling
3
+ """
4
+
5
+ import contextlib
2
6
  from pathlib import Path
3
7
  from typing import List
4
8
 
@@ -7,8 +11,19 @@ from nmk.model.keys import NmkRootConfig
7
11
  from nmk.utils import run_with_logs
8
12
 
9
13
 
10
- class FlakeBuilder(NmkTaskBuilder):
11
- def build(self, src_folders: List[str]):
14
+ class RuffBuilder(NmkTaskBuilder):
15
+ """
16
+ Ruff tool builder
17
+ """
18
+
19
+ def build(self, src_folders: List[str], command: str):
20
+ """
21
+ Invoke ruff
22
+
23
+ :param src_folders: source folders to be analyzed
24
+ :param command: ruff command to be executed
25
+ """
26
+
12
27
  # Project folder
13
28
  project_folder = Path(self.model.config[NmkRootConfig.PROJECT_DIR].value)
14
29
 
@@ -16,14 +31,12 @@ class FlakeBuilder(NmkTaskBuilder):
16
31
  relative_src_folders = []
17
32
  for f in src_folders:
18
33
  p = Path(f)
19
- try:
34
+ with contextlib.suppress(ValueError):
20
35
  p = p.relative_to(project_folder)
21
- except ValueError: # pragma: no cover
22
- pass
23
36
  relative_src_folders.append(p)
24
37
 
25
- # Delegate to flake8
26
- run_with_logs([sys.executable, "-m", "flake8"] + relative_src_folders, self.logger, cwd=project_folder)
38
+ # Delegate to ruff
39
+ run_with_logs(["ruff", command] + relative_src_folders, self.logger, cwd=project_folder)
27
40
 
28
41
  # Touch output file
29
42
  self.main_output.touch()
nmk_python/ruff.yml ADDED
@@ -0,0 +1,64 @@
1
+ config:
2
+
3
+ # Just needs ruff
4
+ venvPkgDeps:
5
+ - ruff
6
+
7
+ # Max line length
8
+ pythonLineLength: 160
9
+
10
+ # Add project file fragment for ruff config
11
+ pythonProjectFileFragments:
12
+ - ${BASEDIR}/templates/ruff.toml.jinja
13
+
14
+ # Stamp files for ruff execution
15
+ pythonRuffFormatStamp: ${outputDir}/.ruff-format
16
+ pythonRuffCheckStamp: ${outputDir}/.ruff-check
17
+
18
+ # Ignored rules
19
+ pythonIgnoredRules:
20
+ - E203 # space before ":"; sometimes formatting with black introduces this kind of space in slice expressions
21
+ - E501 # line length; we're working with lines wider than the recommended 79 length
22
+
23
+ # Require VSCode specific extension for ruff analysis
24
+ vscodeExtensionsNames:
25
+ - charliermarsh.ruff
26
+
27
+ tasks:
28
+
29
+ # Python source code format with ruff
30
+ py.format:
31
+ description: Format python code
32
+ emoji: ":snake:.:lipstick:"
33
+ deps:
34
+ - out
35
+ - py.project
36
+ appendToDeps: build
37
+ if: ${pythonSrcFiles} # Only if python source files are found
38
+ output:
39
+ - ${pythonRuffFormatStamp}
40
+ input:
41
+ - ${pythonProjectFile}
42
+ - ${pythonSrcFiles}
43
+ builder: nmk_python.ruff.RuffBuilder
44
+ params:
45
+ src_folders: ${pythonSrcFolders}
46
+ command: format
47
+
48
+ # Python source code analysis with ruff
49
+ py.analyze:
50
+ description: Analyze python code
51
+ emoji: ":snake:.:eye: "
52
+ deps:
53
+ - py.format
54
+ appendToDeps: build
55
+ if: ${pythonSrcFiles} # Only if python source files are found
56
+ output:
57
+ - ${pythonRuffCheckStamp}
58
+ input:
59
+ - ${pythonProjectFile}
60
+ - ${pythonSrcFiles}
61
+ builder: nmk_python.ruff.RuffBuilder
62
+ params:
63
+ src_folders: ${pythonSrcFolders}
64
+ command: check
@@ -0,0 +1,35 @@
1
+ [build-system]
2
+ requires = ["setuptools"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "{{ pythonPackage }}"
7
+ dynamic = ["version"]
8
+ authors = [
9
+ {name="{{ projectAuthor }}"},
10
+ ]
11
+ maintainers = [
12
+ {name="{{ projectAuthor }}"},
13
+ ]
14
+ readme = {file = "README.md", content-type = "text/markdown"}
15
+ requires-python = ">={{ pythonMinVersion }}"
16
+ classifiers = [{% for ver in pythonSupportedVersions %}
17
+ "Programming Language :: Python :: {{ ver }}",{% endfor %}
18
+ ]
19
+ dependencies = [{% for req in pythonPackageRequirements %}
20
+ "{{ req }}",{% endfor %}
21
+ ]
22
+
23
+ [tool.setuptools.packages.find]
24
+ where = [{% for path in pythonSrcFolders %}"{{path}}",{% endfor %}]
25
+ exclude = ["{{pythonTestPackage}}"]
26
+ namespaces = false
27
+
28
+ [tool.setuptools.package-data]
29
+ "*" = ["*"]
30
+
31
+ [tool.setuptools.dynamic.version]
32
+ file = "{{ pythonVersionStamp }}"
33
+
34
+ [tool.distutils.bdist_wheel]
35
+ plat-name = "{{ pythonPackagePlatform }}"
@@ -0,0 +1,20 @@
1
+ [tool.pytest.ini_options]
2
+ testpaths = ["{{pythonTestSources}}"]
3
+ junit_family = "xunit2"
4
+ filterwarnings = ["ignore::DeprecationWarning"]
5
+ addopts = [
6
+ "--capture=sys",
7
+ "--junit-xml={{pythonTestOutput}}/report.xml{% for folder in pythonSrcFolders %}",
8
+ "--cov={{folder}}{% endfor %}",
9
+ "--cov-report=html:{{pythonCoverageHtmlReport}}",
10
+ "--cov-report=xml:{{pythonCoverageXmlReport}}",
11
+ "--cov-report=term",
12
+ "--cov-config={{pythonProjectFile}}"
13
+ ]
14
+
15
+ [tool.coverage.run]
16
+ data_file = "{{pythonCoverageFile}}"
17
+ concurrency = ["thread"]
18
+ disable_warnings = ["no-data-collected"]
19
+ branch = true
20
+ omit = [ "{{pythonTestSources}}/*" ]
@@ -3,17 +3,10 @@
3
3
  "editor.codeActionsOnSave": {
4
4
  "source.organizeImports": "always"
5
5
  },
6
- "editor.defaultFormatter": "ms-python.black-formatter",
6
+ "editor.defaultFormatter": "charliermarsh.ruff",
7
7
  "editor.formatOnSave": true
8
8
  },
9
- "black-formatter.args": [
10
- "-l",
11
- "{{ pythonLineLength }}"
12
- ],
13
- "flake8.args": [
14
- "--ignore={% for rule in pythonIgnoredRules %}{% if loop.index > 1 %},{% endif %}{{rule}}{% endfor %}"
15
- ],
16
- "python.defaultInterpreterPath": "venv/bin/python",
9
+ "python.defaultInterpreterPath": "{{ venvName }}/bin/python",
17
10
  "python.autoComplete.extraPaths": [{% for folder in pythonSrcFolders %}{% if loop.index > 1 %},{% endif %}
18
11
  "{{folder}}"{% endfor %}
19
12
  ],
@@ -26,7 +19,7 @@
26
19
  "pyvenv",
27
20
  "sdist",
28
21
  "venv",
29
- "loadme"
22
+ "pyproject"
30
23
  ],
31
24
  "python.analysis.extraPaths": [{% for folder in pythonSrcFolders %}{% if loop.index > 1 %},{% endif %}
32
25
  "{{folder}}"{% endfor %}
@@ -0,0 +1,19 @@
1
+ [tool.ruff]
2
+ line-length = {{ pythonLineLength }}
3
+
4
+ [tool.ruff.lint]
5
+ select = [
6
+ # pycodestyle
7
+ "E",
8
+ # Pyflakes
9
+ "F",
10
+ # pyupgrade
11
+ "UP",
12
+ # flake8-bugbear
13
+ "B",
14
+ # flake8-simplify
15
+ "SIM",
16
+ # isort
17
+ "I",
18
+ ]
19
+ ignore = [{% for rule in pythonIgnoredRules %}{% if loop.index > 1 %},{% endif %}"{{rule}}"{% endfor %}]
nmk_python/tests.py CHANGED
@@ -1,3 +1,7 @@
1
+ """
2
+ Python tests builder
3
+ """
4
+
1
5
  import shutil
2
6
  import subprocess
3
7
  import sys
@@ -8,7 +12,17 @@ from nmk.model.keys import NmkRootConfig
8
12
 
9
13
 
10
14
  class PytestBuilder(NmkTaskBuilder):
15
+ """
16
+ Python tests builder
17
+ """
18
+
11
19
  def build(self, pytest_args: Dict[str, str]):
20
+ """
21
+ Invoke pytest with specified options
22
+
23
+ :param pytest_args: extra pytest command line args
24
+ """
25
+
12
26
  # Clean outputs
13
27
  for p in self.outputs:
14
28
  if p.is_dir():
@@ -28,4 +42,6 @@ class PytestBuilder(NmkTaskBuilder):
28
42
  args.append(f"--{opt_k}={opt_v}")
29
43
 
30
44
  # Invoke pytest
31
- subprocess.run([sys.executable, "-m", "pytest"] + args, check=True, cwd=self.model.config[NmkRootConfig.PROJECT_DIR].value)
45
+ all_args = [sys.executable, "-m", "pytest"] + args
46
+ self.logger.debug(f"Running subprocess: {' '.join(all_args)}")
47
+ subprocess.run(all_args, check=True, cwd=self.model.config[NmkRootConfig.PROJECT_DIR].value)
nmk_python/tests.yml CHANGED
@@ -1,13 +1,7 @@
1
1
  config:
2
- # Tests related setup config
3
- pythonSetupCfgFiles:
4
- - ${BASEDIR}/templates/pytest.cfg
5
-
6
- # Extra config to get an extendable list of source paths to be ignored for coverage
7
- pythonSetupItems:
8
- run:
9
- omit:
10
- - ${pythonTestSources}/*
2
+ # Tests related project config
3
+ pythonProjectFileFragments:
4
+ - ${BASEDIR}/templates/pytest.toml.jinja
11
5
 
12
6
  # Tests related venv dependencies
13
7
  venvPkgDeps:
@@ -23,7 +17,7 @@ config:
23
17
  # Python test output folder
24
18
  pythonTestOutput: ${outputDir}/tests
25
19
 
26
- # Pytest extra args (not generated in setup.cfg)
20
+ # Pytest extra args (not generated in project file)
27
21
  pytestExtraArgs:
28
22
  numprocesses: auto
29
23
  cov-fail-under: 100
@@ -50,7 +44,7 @@ tasks:
50
44
  description: Run python tests
51
45
  emoji: ":snake:.:crossed_fingers:"
52
46
  deps:
53
- - py.build
47
+ - py.install
54
48
  appendToDeps: tests
55
49
  output:
56
50
  - ${pythonCoverageHtmlReport}
nmk_python/version.py CHANGED
@@ -1,18 +1,29 @@
1
+ """
2
+ Python version handling
3
+ """
4
+
5
+ import platform
1
6
  import re
7
+ from typing import List
2
8
 
3
9
  from nmk.model.builder import NmkTaskBuilder
4
- from nmk.model.resolver import NmkStrConfigResolver
10
+ from nmk.model.resolver import NmkListConfigResolver, NmkStrConfigResolver
5
11
 
6
12
  # Git version pattern
7
- GIT_VERSION_PATTERN = re.compile("([^-]+)(?:-([0-9]+))?(?:-(.+))?")
13
+ _GIT_VERSION_PATTERN = re.compile("([^-]+)(?:-([0-9]+))?(?:-(.+))?")
8
14
 
9
15
 
10
16
  class PythonVersionResolver(NmkStrConfigResolver):
17
+ """
18
+ Python version resolver
19
+ """
20
+
11
21
  def get_value(self, name: str) -> str:
12
- # Turn the git version in the Python way
13
- # See https://www.python.org/dev/peps/pep-0440/
22
+ """
23
+ Turn the git version in the Python way
24
+ """
14
25
  git_version = self.model.config["gitVersion"].value
15
- m = GIT_VERSION_PATTERN.match(git_version)
26
+ m = _GIT_VERSION_PATTERN.match(git_version)
16
27
  if m is not None:
17
28
  # Build python version from git version segments
18
29
  out = m.group(1)
@@ -30,8 +41,48 @@ class PythonVersionResolver(NmkStrConfigResolver):
30
41
 
31
42
 
32
43
  class PythonVersionRefresh(NmkTaskBuilder):
44
+ """
45
+ Python version builder
46
+ """
47
+
33
48
  def build(self, version: str):
34
- # Simple version dump
49
+ """
50
+ Simple python version dump
51
+ """
35
52
  self.logger.info(self.task.emoji, self.task.description)
36
53
  with self.main_output.open("w") as f:
37
54
  f.write(version)
55
+
56
+
57
+ class PythonSupportedVersionsResolver(NmkListConfigResolver):
58
+ """
59
+ Supported python versions range resolver
60
+ """
61
+
62
+ def get_value(self, name: str) -> List[str]:
63
+ """
64
+ Returns supported python versions range
65
+
66
+ :return: list of all python versions between min and max supported versions
67
+ """
68
+
69
+ def _split_version(v: str) -> List[int]:
70
+ return list(map(int, v.split(".")))
71
+
72
+ # Get min/max values, and verify consistency
73
+ min_ver, max_ver = self.model.config["pythonMinVersion"].value, self.model.config["pythonMaxVersion"].value
74
+ min_split, max_split = _split_version(min_ver), _split_version(max_ver)
75
+ prefix = "Inconsistency in python min/max supported versions: "
76
+ assert len(min_split) == len(max_split), prefix + "not the same segments count"
77
+ assert len(min_split) == 2, prefix + "can only deal with X.Y versions (2 segments expected)"
78
+ assert min_split[0] == max_split[0], prefix + "can't deal with different major versions"
79
+ assert max_split[1] > min_split[1], prefix + "max isn't greater than min"
80
+
81
+ # Also verifies current runtime is in range
82
+ p_ver = platform.python_version()
83
+ cur_split = _split_version(p_ver)
84
+ assert cur_split[0] == max_split[0], prefix + f"current python major version ({p_ver}) doesn't match with supported versions range"
85
+ assert min_split[1] <= cur_split[1] <= max_split[1], prefix + f"current python version ({p_ver}) is out of supported versions range"
86
+
87
+ # Iterate and return versions range
88
+ return [f"{min_split[0]}.{sub}" for sub in range(min_split[1], max_split[1] + 1)]
nmk_python/version.yml CHANGED
@@ -6,6 +6,12 @@ config:
6
6
  # Python version stamp file
7
7
  pythonVersionStamp: ${outputDir}/.pythonversion
8
8
 
9
+ # Python supported versions range (min-max)
10
+ pythonMinVersion: "3.8"
11
+ pythonMaxVersion: "3.12"
12
+ pythonSupportedVersions:
13
+ __resolver__: nmk_python.version.PythonSupportedVersionsResolver
14
+
9
15
  tasks:
10
16
  # To refresh python version stamp file
11
17
  py.version:
@@ -1,20 +1,20 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nmk-python
3
- Version: 0.14.0
3
+ Version: 0.15.0
4
4
  Summary: Python plugin for nmk build system
5
- Home-page: https://github.com/dynod/nmk-python
6
5
  Author: The dynod project
7
6
  Maintainer: The dynod project
8
- License: Mozilla Public License Version 2.0
9
7
  Classifier: Programming Language :: Python :: 3.8
10
8
  Classifier: Programming Language :: Python :: 3.9
11
9
  Classifier: Programming Language :: Python :: 3.10
12
10
  Classifier: Programming Language :: Python :: 3.11
13
11
  Classifier: Programming Language :: Python :: 3.12
12
+ Requires-Python: >=3.8
14
13
  Description-Content-Type: text/markdown
15
14
  License-File: LICENSE
16
15
  Requires-Dist: nmk-base
17
16
  Requires-Dist: Jinja2
17
+ Requires-Dist: tomlkit
18
18
 
19
19
  # nmk-python
20
20
  Python plugin for nmk build system
@@ -25,16 +25,15 @@ Python plugin for nmk build system
25
25
  [![Issues](https://img.shields.io/github/issues-search/dynod/nmk?label=issues&query=is%3Aopen+is%3Aissue+label%3Aplugin%3Apython)](https://github.com/dynod/nmk/issues?q=is%3Aopen+is%3Aissue+label%3Aplugin%3Apython)
26
26
  [![Supported python versions](https://img.shields.io/badge/python-3.8%20--%203.12-blue)](https://www.python.org/)
27
27
  [![PyPI](https://img.shields.io/pypi/v/nmk-python)](https://pypi.org/project/nmk-python/)
28
- [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
29
- [![Flake8 analysis result](https://img.shields.io/badge/flake8-0-green)](https://flake8.pycqa.org/)
28
+ [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](https://astral.sh/ruff)
29
+ [![Ruff analysis result](https://img.shields.io/badge/ruff-0-green)](https://astral.sh/ruff)
30
30
  [![Code coverage](https://img.shields.io/codecov/c/github/dynod/nmk-python)](https://app.codecov.io/gh/dynod/nmk-python)
31
+ [![Documentation Status](https://readthedocs.org/projects/nmk-python/badge/?version=stable)](https://nmk-python.readthedocs.io/)
31
32
  <!-- NMK-BADGES-END -->
32
33
 
33
34
  This plugin adds support for Python development in an **`nmk`** project:
34
- * setup file generation
35
- * code format with [black](https://github.com/psf/black)
36
- * import sorting with [isort](https://github.com/PyCQA/isort)
37
- * code analysis with [flake8](https://flake8.pycqa.org/)
35
+ * project file generation
36
+ * code format and analysis with [ruff](https://astral.sh/ruff)
38
37
  * python wheel build
39
38
  * test with [pytest](https://pytest.org)
40
39
  * VSCode settings generation (if [**`nmk-vscode`**](https://github.com/dynod/nmk-vscode) plugin is also used)
@@ -54,7 +53,7 @@ refs:
54
53
 
55
54
  ## Documentation
56
55
 
57
- This plugin documentation is available [here](https://github.com/dynod/nmk/wiki/nmk-python-plugin)
56
+ This plugin documentation is available [here](https://nmk-python.readthedocs.io/)
58
57
 
59
58
  ## Issues
60
59
 
@@ -0,0 +1,27 @@
1
+ nmk_python/__init__.py,sha256=EouCWXbAeObby1vOCx0Vx_WaGC2fEEFDMCen3JZt4TU,446
2
+ nmk_python/badges.yml,sha256=RAn_0A7NdHb0leF-MdXsVPtS6kOyfsUGb61GBXqDc-8,911
3
+ nmk_python/build.py,sha256=8PgpaWqx6jTSc7eZsvNC4h8WhfM9DoB0SqhCK6_BSpA,4898
4
+ nmk_python/build.yml,sha256=kgG-_6NyAX25B45plaDEHUMk2RYgaSYyM8y7c952JlQ,2989
5
+ nmk_python/doc.yml,sha256=12amAlYylH7JAHaqBIOoEZ_LrXY_bPNKuHrrwUkIj8w,932
6
+ nmk_python/files.py,sha256=T2-MBoc-KEmnhCC78IbGsi6Q-ntk_IcOtWVylLEhSAc,1887
7
+ nmk_python/files.yml,sha256=-vGjogdulwsfdKVEUa5RARYDKxF3m9RmHKFnIhaT3_k,780
8
+ nmk_python/plugin.yml,sha256=hi7osXyIwpHgJAsww5mhOA6emxhzgitDF8cOsvyY29Y,1068
9
+ nmk_python/project.py,sha256=GvFoJfyUv8G565gAHvTeoIHEz5SR5MxnIBOOnulh_nY,2612
10
+ nmk_python/project.yml,sha256=Uq8yKoqOgY2sIQweUJhrLALLFuc76uNmUZsz7hrb9pI,947
11
+ nmk_python/ruff.py,sha256=T0o9i_ZZosJDWRuLNkgYIzL9mb_KmNMsmQ1kDq08gBU,1082
12
+ nmk_python/ruff.yml,sha256=VIlH52dc4m-dgsm-SFuD4-AK6knke1-GuJSJQvVsXWE,1863
13
+ nmk_python/tests.py,sha256=nOgi6-Jh0rMQnl0vKqgAFuitiOpQVZmsjdhQZz_vuhc,1236
14
+ nmk_python/tests.yml,sha256=8th_7_7nII84URse1dmbhNJ1_l0ZvsRD-C9uhNWJ7GA,1663
15
+ nmk_python/version.py,sha256=HFZcZ_ZMxwVOR2OjUFF2cFc2Og3qPo5Bph3PiTDkhHU,3133
16
+ nmk_python/version.yml,sha256=YGvVUnJ0-HjWfyXOzDWqQzLSBF68Fd8cNT6ESHAr6oE,904
17
+ nmk_python/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ nmk_python/templates/build.toml.jinja,sha256=EBuBZpWkphpVj3X1j9s3WIL11-OcLj0u7e19CqxJEDU,902
19
+ nmk_python/templates/pytest.toml.jinja,sha256=zQ3SRWnF23Omxm7zInE54j0l3OyjXSOn5pQ5Pak8Boo,641
20
+ nmk_python/templates/python-settings.json.jinja,sha256=-6J1tybWEzChxlDqu4tp9BMWxbYD8HpztGs9G8LkC_w,877
21
+ nmk_python/templates/python-test-debug-launch.json.jinja,sha256=J_xju2EpHNXvgvqBxlf99p7g-ZMh3xE-_2r7o_57gpU,364
22
+ nmk_python/templates/ruff.toml.jinja,sha256=DTHe1ixK1SBY8d2xBCfkGz89HzjAoMO7Ogs12AY1i4Y,346
23
+ nmk_python-0.15.0.dist-info/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
24
+ nmk_python-0.15.0.dist-info/METADATA,sha256=fLeDmZBvEeeUciDAVZ1iYKzv_JQZjYC2zDnJhyO3KDc,2790
25
+ nmk_python-0.15.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
26
+ nmk_python-0.15.0.dist-info/top_level.txt,sha256=MCb5w_hoOZ0kk3Dajxrd9Pz6lZ3pyuS6TQCRA3vddTo,11
27
+ nmk_python-0.15.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: setuptools (75.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
nmk_python/flake.yml DELETED
@@ -1,56 +0,0 @@
1
- config:
2
-
3
- # Needs flake8 and plugins
4
- venvPkgDeps:
5
- - flake8
6
- - pep8-naming
7
- - flake8-builtins
8
- - flake8-blind-except
9
- - flake8-comprehensions
10
- - flake8-string-format
11
- - flake8-pep3101
12
- - flake8-bugbear
13
-
14
- # Add setup file for flake8 config
15
- pythonSetupCfgFiles:
16
- - ${BASEDIR}/templates/flake.cfg
17
-
18
- # Also contribute to setup items for extendable flake8 exclusion paths list
19
- pythonSetupItems:
20
- flake8:
21
- exclude:
22
- - __pycache__
23
-
24
- # Stamp file for flake8 execution
25
- pythonFlakeStamp: ${outputDir}/.flake
26
-
27
- # Ignored rules
28
- pythonIgnoredRules:
29
- - E203 # space before ":"; sometimes formatting with black introduces this kind of space in slice expressions
30
- - E501 # line length; we're working with lines wider than the recommended 79 length
31
- - B902 # allow blind Exception catch; typically usefull for fallback behaviors
32
- - W503 # line break before operator; incompatible with black formatting
33
-
34
- # Require VSCode specific extension for flake8 analysis
35
- vscodeExtensionsNames:
36
- - ms-python.flake8
37
-
38
- tasks:
39
-
40
- # Python source code analysis with flake8
41
- py.analyze:
42
- description: Analyze python code
43
- emoji: ":snake:.:eye: "
44
- deps:
45
- - py.setup
46
- - py.format
47
- appendToDeps: setup
48
- if: ${pythonSrcFiles} # Only if python source files are found
49
- output:
50
- - ${pythonFlakeStamp}
51
- input:
52
- - ${pythonSetupCfg}
53
- - ${pythonSrcFiles}
54
- builder: nmk_python.flake.FlakeBuilder
55
- params:
56
- src_folders: ${pythonSrcFolders}
nmk_python/format.py DELETED
@@ -1,17 +0,0 @@
1
- import sys
2
- from typing import List
3
-
4
- from nmk.model.builder import NmkTaskBuilder
5
- from nmk.utils import run_with_logs
6
-
7
-
8
- class FormatBuilder(NmkTaskBuilder):
9
- def build(self, src_files: List[str], src_folders: List[str], line_length: int):
10
- # Delegate to black
11
- run_with_logs([sys.executable, "-m", "black", "-l", str(line_length)] + src_files, self.logger)
12
-
13
- # Delegate to isort
14
- run_with_logs([sys.executable, "-m", "isort"] + src_folders, self.logger)
15
-
16
- # Touch output file
17
- self.main_output.touch()
nmk_python/format.yml DELETED
@@ -1,41 +0,0 @@
1
- config:
2
-
3
- # Needs black and isort
4
- venvPkgDeps:
5
- - black
6
- - isort
7
-
8
- # Max line length
9
- pythonLineLength: 160
10
-
11
- # Stamp file for format tools execution
12
- pythonFormatStamp: ${outputDir}/.format
13
-
14
- # Add setup file for isort config
15
- pythonSetupCfgFiles:
16
- - ${BASEDIR}/templates/isort.cfg
17
-
18
- # Require VSCode specific extension for black formatting
19
- vscodeExtensionsNames:
20
- - ms-python.black-formatter
21
-
22
- tasks:
23
- # Code format tools execution
24
- py.format:
25
- description: Format python code
26
- emoji: ":snake:.:lipstick:"
27
- deps:
28
- - out
29
- - py.setup
30
- appendToDeps: setup
31
- if: ${pythonSrcFiles} # Only if python source files are found
32
- output:
33
- - ${pythonFormatStamp}
34
- input:
35
- - ${pythonSrcFiles}
36
- - ${pythonSetupCfg}
37
- builder: nmk_python.format.FormatBuilder
38
- params:
39
- src_files: ${pythonSrcFiles}
40
- src_folders: ${pythonSrcFolders}
41
- line_length: ${pythonLineLength}
nmk_python/setup.py DELETED
@@ -1,63 +0,0 @@
1
- import platform
2
- from configparser import ConfigParser
3
- from pathlib import Path
4
- from typing import List, Union
5
-
6
- from nmk.model.resolver import NmkListConfigResolver
7
- from nmk_base.common import TemplateBuilder
8
-
9
- LIST_SEPARATOR = "\n"
10
-
11
-
12
- class PythonSupportedVersionsResolver(NmkListConfigResolver):
13
- def get_value(self, name: str) -> List[str]:
14
- def split_version(v: str) -> List[int]:
15
- return list(map(int, v.split(".")))
16
-
17
- # Get min/max values, and verify consistency
18
- min_ver, max_ver = self.model.config["pythonMinVersion"].value, self.model.config["pythonMaxVersion"].value
19
- min_split, max_split = split_version(min_ver), split_version(max_ver)
20
- prefix = "Inconsistency in python min/max supported versions: "
21
- assert len(min_split) == len(max_split), prefix + "not the same segments count"
22
- assert len(min_split) == 2, prefix + "can only deal with X.Y versions (2 segments expected)"
23
- assert min_split[0] == max_split[0], prefix + "can't deal with different major versions"
24
- assert max_split[1] > min_split[1], prefix + "max isn't greater than min"
25
-
26
- # Also verifies current runtime is in range
27
- p_ver = platform.python_version()
28
- cur_split = split_version(p_ver)
29
- assert cur_split[0] == max_split[0], prefix + f"current python major version ({p_ver}) doesn't match with supported versions range"
30
- assert min_split[1] <= cur_split[1] <= max_split[1], prefix + f"current python version ({p_ver}) is out of supported versions range"
31
-
32
- # Iterate and return versions range
33
- return [f"{min_split[0]}.{sub}" for sub in range(min_split[1], max_split[1] + 1)]
34
-
35
-
36
- class PythonSetupBuilder(TemplateBuilder):
37
- def handle_ini_values(self, values: Union[str, List[str]]):
38
- # Turn list into a single block of text values
39
- return (LIST_SEPARATOR + LIST_SEPARATOR.join([self.relative_path(str(v)) for v in values])) if isinstance(values, list) else values
40
-
41
- def build(self, setup_cfg_files: List[str], setup_items: dict):
42
- # Merge setup fragments to generate final setup
43
- setup_cfg_output = self.main_output
44
- c = ConfigParser()
45
- for f_path in map(Path, setup_cfg_files):
46
- # Update config with rendered template
47
- c.read_string(self.render_template(f_path, {}))
48
-
49
- # Iterate on items contributed through yml project files (only ones contributing maps)
50
- for section, values in filter(lambda t: isinstance(t[1], dict), setup_items.items()):
51
- # Create new section is not done yet
52
- if not c.has_section(section):
53
- c.add_section(section)
54
-
55
- # Handle list of values
56
- prepared_values = {k: self.handle_ini_values(v) for k, v in values.items()}
57
-
58
- # Finally update section values
59
- c[section].update(prepared_values)
60
-
61
- # Finally write config to output file
62
- with setup_cfg_output.open("w") as f:
63
- c.write(f)
nmk_python/setup.yml DELETED
@@ -1,37 +0,0 @@
1
- config:
2
- # Generated setup config files
3
- pythonSetupCfg: ${PROJECTDIR}/setup.cfg
4
-
5
- # Paths to setup.cfg fragments to be merged
6
- pythonSetupCfgFiles: []
7
-
8
- # Contributed setup items from yml files
9
- pythonSetupItems: {}
10
-
11
- # Python supported versions range (min-max)
12
- pythonMinVersion: "3.8"
13
- pythonMaxVersion: "3.12"
14
- pythonSupportedVersions:
15
- __resolver__: nmk_python.setup.PythonSupportedVersionsResolver
16
-
17
- # Contribute to gitignore
18
- gitIgnoredFiles:
19
- - ${pythonSetupCfg}
20
-
21
- tasks:
22
- py.setup:
23
- description: Generate python setup files
24
- emoji: ":snake:.:toolbox:"
25
- input:
26
- - ${pythonSetupCfgFiles}
27
- - ${pythonVersionStamp}
28
- output:
29
- - ${pythonSetupCfg}
30
- deps:
31
- - py.version
32
- appendToDeps: setup
33
- if: ${pythonSrcFiles} # Only if python source files are found
34
- builder: nmk_python.setup.PythonSetupBuilder
35
- params:
36
- setup_cfg_files: ${pythonSetupCfgFiles}
37
- setup_items: ${pythonSetupItems}
@@ -1,20 +0,0 @@
1
- [metadata]
2
- name = {{ pythonPackage }}
3
- author = {{ projectAuthor }}
4
- maintainer = {{ projectAuthor }}
5
- version = {{ pythonVersion }}
6
- long_description = file: README.md
7
- long_description_content_type = text/markdown
8
- license = Mozilla Public License Version 2.0
9
- classifiers ={% for ver in pythonSupportedVersions %}
10
- Programming Language :: Python :: {{ ver }}{% endfor %}
11
-
12
- [options]
13
- package_dir = ={{ pythonSrcFolders[0] }}
14
- packages = find:
15
- install_requires ={% for req in pythonPackageRequirements %}
16
- {{ req }}{% endfor %}
17
-
18
- [options.packages.find]
19
- where = {{ pythonSrcFolders[0] }}
20
- exclude = tests
@@ -1,6 +0,0 @@
1
- [flake8]
2
- max-line-length = {{ pythonLineLength }}
3
- enable-extensions = G
4
- select = E,F,W,A,B,C,D,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,X,Y,Z
5
- ignore = {% for rule in pythonIgnoredRules %}{% if loop.index > 1 %},{% endif %}{{rule}}{% endfor %}
6
- jobs = auto
@@ -1,3 +0,0 @@
1
- [isort]
2
- line_length = {{ pythonLineLength }}
3
- multi_line_output = 3
@@ -1,20 +0,0 @@
1
- [tool:pytest]
2
- testpaths = {{pythonTestSources}}
3
- junit_family = xunit2
4
- filterwarnings =
5
- ignore::DeprecationWarning
6
- addopts =
7
- --capture=sys
8
- --junit-xml={{pythonTestOutput}}/report.xml{% for folder in pythonSrcFolders %}
9
- --cov={{folder}}{% endfor %}
10
- --cov-report=html:{{pythonCoverageHtmlReport}}
11
- --cov-report=xml:{{pythonCoverageXmlReport}}
12
- --cov-report=term
13
- --cov-config={{pythonSetupCfg}}
14
-
15
- ; coverage configuration
16
- [run]
17
- data_file = {{pythonCoverageFile}}
18
- concurrency = thread
19
- disable_warnings = no-data-collected
20
- branch = True
@@ -1,30 +0,0 @@
1
- nmk_python/__init__.py,sha256=pVT-8I5dH_SkG5OQBtnOyIrWTtBh16mdua0Rah5ADPw,705
2
- nmk_python/badges.yml,sha256=TaauzvFP9KIiudSeq-YihcaZACKis6wwF526miVOguw,926
3
- nmk_python/build.py,sha256=y5W5mcn0ZYSvFC28cxxxOtpm2qodiVFAANxMtBBVKpg,1134
4
- nmk_python/build.yml,sha256=_a4uEFIYa5wuP6lJhn5sKYfp5sdzrVpy7hgq-j7Uhyw,2652
5
- nmk_python/doc.yml,sha256=12amAlYylH7JAHaqBIOoEZ_LrXY_bPNKuHrrwUkIj8w,932
6
- nmk_python/files.py,sha256=2loPwmbZdqYNod6ML85s0dpBY4jx6--AB5M2zQLzi5s,1152
7
- nmk_python/files.yml,sha256=fg8oPpTdjOSquIWw_cqcn5UyXfQHZzCVNSyHc95wfvE,703
8
- nmk_python/flake.py,sha256=5EmeBuTlT4HiUj5KMMnELyQ1eaMd3j3DnEax6m4gzn0,895
9
- nmk_python/flake.yml,sha256=FaoIzqVuD_YxbW6YruuO-Sfm0YAiwpztOdZv_evJvNA,1701
10
- nmk_python/format.py,sha256=JQCtKrWB8IsGzcqRTBZ7AbsVe6eG50sFibucj8ig4Ls,546
11
- nmk_python/format.yml,sha256=3R2Bhbg3AZRV8YgKVBjGY8WnnNJA-28GS867RDeW2AY,1068
12
- nmk_python/plugin.yml,sha256=ZXP1G7n6zrEu9H50_H_1XqDG-vE09x8SMJJpai7niQ8,1139
13
- nmk_python/setup.py,sha256=URmntiw4rCe999CgRoO7EhnvvYyO1NAeeO42qplzY2M,2995
14
- nmk_python/setup.yml,sha256=glSyp98_OwoZbJSgoopgkCFiUeKkbkCntLQTSl4S8sw,1068
15
- nmk_python/tests.py,sha256=rKKJKKx4g3C6TAVR_aNhsgPlMN3bU61dUqKx3seqq4s,935
16
- nmk_python/tests.yml,sha256=-rMWZC5PeadjOQu82d_OpmupFDs4Q1mzU3XxZBBlLTM,1825
17
- nmk_python/version.py,sha256=f-b--EYQMw2G2I8gCeMHDwOM8OQC8nu_a2cEtkAS6rQ,1278
18
- nmk_python/version.yml,sha256=1l-PnAgRA1WRsjtxTOBmA_iP6LJDSWUu9Jr7bF_dVGM,696
19
- nmk_python/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- nmk_python/templates/build.cfg,sha256=QxElw9uo30FkBkb-fNRYrdF1lWI5IoGg6c5EDSFMjQA,601
21
- nmk_python/templates/flake.cfg,sha256=2XufwS9-_MPcGRwDvGHcd0dUAxdLnV4D3IEi69yqRhw,246
22
- nmk_python/templates/isort.cfg,sha256=7ZGCSLYAm1ZPmREwzDQS718e8Zp3Aa7ETtX5uuVxvR4,67
23
- nmk_python/templates/pytest.cfg,sha256=m1oCLagZkiwimF4qES5d6BEMJRZm_SiqZMllJK5fj9E,561
24
- nmk_python/templates/python-settings.json.jinja,sha256=nwuuBJwj-nY6q5d1bBhnCV4nVa1YZvhm7QGBM2fubCM,1094
25
- nmk_python/templates/python-test-debug-launch.json.jinja,sha256=J_xju2EpHNXvgvqBxlf99p7g-ZMh3xE-_2r7o_57gpU,364
26
- nmk_python-0.14.0.dist-info/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
27
- nmk_python-0.14.0.dist-info/METADATA,sha256=vjQjoXs_pVJU6jhe7oD2iAn5TDKf7MNSQaXAivLxkuU,2849
28
- nmk_python-0.14.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
29
- nmk_python-0.14.0.dist-info/top_level.txt,sha256=MCb5w_hoOZ0kk3Dajxrd9Pz6lZ3pyuS6TQCRA3vddTo,11
30
- nmk_python-0.14.0.dist-info/RECORD,,