From 7447d743ba1afbc7e411d4915ea93f60e7846f93 Mon Sep 17 00:00:00 2001 From: Joachim Metz Date: Tue, 26 May 2026 13:20:39 +0200 Subject: [PATCH 1/3] Updated pylintrc to version 4.0 --- .github/workflows/test_tox.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/test_tox.yml b/.github/workflows/test_tox.yml index 457cee18..db1a615f 100644 --- a/.github/workflows/test_tox.yml +++ b/.github/workflows/test_tox.yml @@ -14,6 +14,10 @@ jobs: strategy: matrix: include: + - python-version: '3.10' + toxenv: 'py310' + - python-version: '3.11' + toxenv: 'py311' - python-version: '3.12' toxenv: 'py312' - python-version: '3.13' From 176b45ae37b852c1c95ddbf098574362825d3f12 Mon Sep 17 00:00:00 2001 From: Joachim Metz Date: Tue, 26 May 2026 15:03:36 +0200 Subject: [PATCH 2/3] Updated pylintrc to version 4.0 --- .github/workflows/test_tox.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/test_tox.yml b/.github/workflows/test_tox.yml index db1a615f..457cee18 100644 --- a/.github/workflows/test_tox.yml +++ b/.github/workflows/test_tox.yml @@ -14,10 +14,6 @@ jobs: strategy: matrix: include: - - python-version: '3.10' - toxenv: 'py310' - - python-version: '3.11' - toxenv: 'py311' - python-version: '3.12' toxenv: 'py312' - python-version: '3.13' From f85c207c854f414113dafcb8d359de0e967ac78c Mon Sep 17 00:00:00 2001 From: Joachim Metz Date: Tue, 26 May 2026 17:26:23 +0200 Subject: [PATCH 3/3] Changes for project configuration and deployment via CI --- data/templates/appveyor.yml/artifacts | 3 - data/templates/appveyor.yml/build | 4 - data/templates/appveyor.yml/deploy_script | 4 - data/templates/appveyor.yml/install | 1 - data/templates/appveyor.yml/matrix | 14 ---- data/templates/appveyor.yml/pypi_token | 2 - data/templates/github_actions/build_wheel.yml | 73 +++++++++++++++++++ data/templates/pyproject.toml/black.toml | 2 +- .../templates/pyproject.toml/l2tdevtools.toml | 7 ++ data/templates/pyproject.toml/project.toml | 2 +- .../dependency_writers/appveyor_yml.py | 28 +------ l2tdevtools/dependency_writers/dpkg.py | 16 ++-- .../dependency_writers/github_actions.py | 20 +++++ l2tdevtools/dependency_writers/pyproject.py | 50 +++++++++---- l2tdevtools/helpers/project.py | 49 ++++++++++++- l2tdevtools/project_config.py | 15 ++++ tools/update-dependencies.py | 1 + 17 files changed, 208 insertions(+), 83 deletions(-) delete mode 100644 data/templates/appveyor.yml/artifacts delete mode 100644 data/templates/appveyor.yml/build delete mode 100644 data/templates/appveyor.yml/deploy_script delete mode 100644 data/templates/appveyor.yml/pypi_token create mode 100644 data/templates/github_actions/build_wheel.yml create mode 100644 data/templates/pyproject.toml/l2tdevtools.toml diff --git a/data/templates/appveyor.yml/artifacts b/data/templates/appveyor.yml/artifacts deleted file mode 100644 index 71f9f8ce..00000000 --- a/data/templates/appveyor.yml/artifacts +++ /dev/null @@ -1,3 +0,0 @@ - -artifacts: -- path: dist\*.whl diff --git a/data/templates/appveyor.yml/build b/data/templates/appveyor.yml/build deleted file mode 100644 index 78494835..00000000 --- a/data/templates/appveyor.yml/build +++ /dev/null @@ -1,4 +0,0 @@ - -build_script: -- cmd: IF [%TARGET%]==[wheel] ( - "%PYTHON%\\python.exe" -m build --wheel ) diff --git a/data/templates/appveyor.yml/deploy_script b/data/templates/appveyor.yml/deploy_script deleted file mode 100644 index 1f3c23c4..00000000 --- a/data/templates/appveyor.yml/deploy_script +++ /dev/null @@ -1,4 +0,0 @@ - -deploy_script: -- ps: If ($$env:APPVEYOR_REPO_TAG -eq "true" -And $$env:MACHINE_TYPE -eq "x86") { - Invoke-Expression "$${env:PYTHON}\\python.exe -m twine upload dist/*.whl --username __token__ --password $${env:PYPI_TOKEN} --skip-existing" } diff --git a/data/templates/appveyor.yml/install b/data/templates/appveyor.yml/install index 89d359f1..0e528389 100644 --- a/data/templates/appveyor.yml/install +++ b/data/templates/appveyor.yml/install @@ -1,3 +1,2 @@ install: -- cmd: "%PYTHON%\\python.exe -m pip install -U build pip setuptools twine wheel" diff --git a/data/templates/appveyor.yml/matrix b/data/templates/appveyor.yml/matrix index e37e3573..2c1e9e72 100644 --- a/data/templates/appveyor.yml/matrix +++ b/data/templates/appveyor.yml/matrix @@ -13,17 +13,3 @@ PYTHON_VERSION: "3.14" L2TBINARIES_TRACK: "dev" TARGET: tests - - DESCRIPTION: "Build wheel on Windows with 32-bit Python 3.14" - MACHINE_TYPE: "amd64" - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 - PYTHON: "C:\\Python314-x64" - PYTHON_VERSION: "3.14" - L2TBINARIES_TRACK: "dev" - TARGET: wheel - - DESCRIPTION: "Build wheel on Windows with 64-bit Python 3.14" - MACHINE_TYPE: "amd64" - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 - PYTHON: "C:\\Python314-x64" - PYTHON_VERSION: "3.14" - L2TBINARIES_TRACK: "dev" - TARGET: wheel diff --git a/data/templates/appveyor.yml/pypi_token b/data/templates/appveyor.yml/pypi_token deleted file mode 100644 index 5a3f70f2..00000000 --- a/data/templates/appveyor.yml/pypi_token +++ /dev/null @@ -1,2 +0,0 @@ - PYPI_TOKEN: - secure: ${pypi_token} diff --git a/data/templates/github_actions/build_wheel.yml b/data/templates/github_actions/build_wheel.yml new file mode 100644 index 00000000..3ac7de1a --- /dev/null +++ b/data/templates/github_actions/build_wheel.yml @@ -0,0 +1,73 @@ +# Build Python sdist and wheel. +name: build_wheels +on: + pull_request: + push: + release: + types: [published] + # Allow trigger via API + workflow_dispatch: +permissions: read-all +jobs: + build_sdist: + name: Build sdist + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Install build dependencies + run: | + sudo apt update + sudo apt -y install python3-build + - name: Build sdist + run: | + python -m build --sdist + - uses: actions/upload-artifact@v7 + with: + name: cibw-sdist + path: dist/*.tar.gz + build_wheel: + name: Build wheel + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Install build dependencies + run: | + sudo apt update + sudo apt -y install python3-build python3-wheel + - name: Build wheel + run: | + python -m build --wheel + - uses: actions/upload-artifact@v7 + with: + name: cibw-wheels + path: dist/*.whl + upload_test_pypi: + needs: [build_sdist, build_wheel] + runs-on: ubuntu-latest + environment: pypi + permissions: + id-token: write + if: github.event_name == 'release' && github.event.action == 'published' + steps: + - uses: actions/download-artifact@v8 + with: + pattern: cibw-* + path: dist + merge-multiple: true + - uses: pypa/gh-action-pypi-publish@release/v1 + with: + repository-url: https://test.pypi.org/legacy/ + upload_pypi: + needs: [upload_test_pypi] + runs-on: ubuntu-latest + environment: pypi + permissions: + id-token: write + if: github.event_name == 'release' && github.event.action == 'published' + steps: + - uses: actions/download-artifact@v8 + with: + pattern: cibw-* + path: dist + merge-multiple: true + - uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/data/templates/pyproject.toml/black.toml b/data/templates/pyproject.toml/black.toml index b05f6de8..83c7e266 100644 --- a/data/templates/pyproject.toml/black.toml +++ b/data/templates/pyproject.toml/black.toml @@ -1,5 +1,5 @@ [tool.black] line-length = 88 -target-version = ["py310"] +target-version = ["${black_target_version}"] include = "\\.pyi?$$" diff --git a/data/templates/pyproject.toml/l2tdevtools.toml b/data/templates/pyproject.toml/l2tdevtools.toml new file mode 100644 index 00000000..1579f210 --- /dev/null +++ b/data/templates/pyproject.toml/l2tdevtools.toml @@ -0,0 +1,7 @@ + +[tool.l2tdevtools] +name = "${name_description}" +status = "${status}" +description = """ +${description_long} +""" diff --git a/data/templates/pyproject.toml/project.toml b/data/templates/pyproject.toml/project.toml index ebceca4b..783b6ffa 100644 --- a/data/templates/pyproject.toml/project.toml +++ b/data/templates/pyproject.toml/project.toml @@ -13,4 +13,4 @@ classifiers = [ "${development_status}", "Programming Language :: Python", ] -requires-python = ">=3.10" +requires-python = ">=${minimum_python_version}" diff --git a/l2tdevtools/dependency_writers/appveyor_yml.py b/l2tdevtools/dependency_writers/appveyor_yml.py index 3327a8c1..87ed2166 100644 --- a/l2tdevtools/dependency_writers/appveyor_yml.py +++ b/l2tdevtools/dependency_writers/appveyor_yml.py @@ -12,10 +12,6 @@ class AppveyorYmlWriter(interface.DependencyFileWriter): PATH = os.path.join("appveyor.yml") - _PROJECTS_WITHOUT_BUILD = frozenset( - ["dtformats", "esedbrc", "olecfrc", "vstools", "winevtrc", "winregrc"] - ) - def _GenerateFromTemplate(self, template_filename, template_mappings): """Generates file context based on a template file. @@ -44,13 +40,6 @@ def Write(self): template_data = self._GenerateFromTemplate("environment", template_mappings) file_content.append(template_data) - if self._project_definition.name not in self._PROJECTS_WITHOUT_BUILD: - if self._project_definition.pypi_token: - template_data = self._GenerateFromTemplate( - "pypi_token", template_mappings - ) - file_content.append(template_data) - template_data = self._GenerateFromTemplate("matrix", template_mappings) file_content.append(template_data) @@ -63,27 +52,12 @@ def Write(self): ) file_content.append(template_data) - if self._project_definition.name in self._PROJECTS_WITHOUT_BUILD: - template_filename = "build_off" - else: - template_filename = "build" - - template_data = self._GenerateFromTemplate(template_filename, template_mappings) + template_data = self._GenerateFromTemplate("build_off", template_mappings) file_content.append(template_data) template_data = self._GenerateFromTemplate("test_script", template_mappings) file_content.append(template_data) - if self._project_definition.name not in self._PROJECTS_WITHOUT_BUILD: - template_data = self._GenerateFromTemplate("artifacts", template_mappings) - file_content.append(template_data) - - if self._project_definition.pypi_token: - template_data = self._GenerateFromTemplate( - "deploy_script", template_mappings - ) - file_content.append(template_data) - file_content = "".join(file_content) with open(self.PATH, "w", encoding="utf-8") as file_object: diff --git a/l2tdevtools/dependency_writers/dpkg.py b/l2tdevtools/dependency_writers/dpkg.py index 9da17d30..c120b93b 100644 --- a/l2tdevtools/dependency_writers/dpkg.py +++ b/l2tdevtools/dependency_writers/dpkg.py @@ -1,6 +1,7 @@ """Writer for Debian packaging (dpkg) files.""" import os +import textwrap from l2tdevtools.dependency_writers import interface @@ -81,11 +82,9 @@ def Write(self): python_module_description = "".join( [python_module_description, f" resources ({python_module_name:s})"] ) - tools_description = ( f"Tools for {self._project_definition.name_description:s}" ) - tool_description_long, _, _ = ( self._project_definition.name_description.rpartition(" (") ) @@ -98,7 +97,6 @@ def Write(self): tool_description_long = "\n".join( [f" {line:s}" for line in tool_description_long.split("\n")] ) - file_content = [] file_content.extend(self._PYTHON3_FILE_HEADER) @@ -107,7 +105,6 @@ def Write(self): data_dependency = ( f"{self._project_definition.name:s}-data (>= ${{binary:Version}})" ) - file_content.extend(self._DATA_PACKAGE) file_content.extend(self._PYTHON3_PACKAGE) @@ -119,11 +116,14 @@ def Write(self): ): file_content.extend(self._TOOLS_PACKAGE) - description_long = self._project_definition.description_long description_long = "\n".join( - [f" {line:s}" for line in description_long.split("\n")] + [ + f" {line:s}" + for line in textwrap.wrap( + self._project_definition.description_long, width=79 + ) + ] ) - python3_dependencies = self._dependency_helper.GetDPKGDepends() if data_dependency: @@ -138,7 +138,6 @@ def Write(self): "python3-setuptools", "pybuild-plugin-pyproject", ] - build_dependencies = ", ".join(build_dependencies) template_mappings = { @@ -155,7 +154,6 @@ def Write(self): "tools_description": tools_description, "tool_description_long": tool_description_long, } - file_content = "\n".join(file_content) file_content = file_content.format(**template_mappings) diff --git a/l2tdevtools/dependency_writers/github_actions.py b/l2tdevtools/dependency_writers/github_actions.py index 5878ce33..fc3bd362 100644 --- a/l2tdevtools/dependency_writers/github_actions.py +++ b/l2tdevtools/dependency_writers/github_actions.py @@ -6,6 +6,26 @@ from l2tdevtools.dependency_writers import interface +class GitHubActionsBuildWheelYmlWriter(interface.DependencyFileWriter): + """build_wheel.yml GitHub actions workflow file writer.""" + + _TEMPLATE_FILE = os.path.join( + "data", "templates", "github_actions", "build_wheel.yml" + ) + + PATH = os.path.join(".github", "workflows", "build_wheel.yml") + + def Write(self): + """Writes a build_wheel.yml GitHub actions workflow file .""" + template_mappings = {} + + template_file = os.path.join(self._l2tdevtools_path, self._TEMPLATE_FILE) + file_content = self._GenerateFromTemplate(template_file, template_mappings) + + with open(self.PATH, "w", encoding="utf-8") as file_object: + file_object.write(file_content) + + class GitHubActionsLintYmlWriter(interface.DependencyFileWriter): """lint.yml GitHub actions workflow file writer.""" diff --git a/l2tdevtools/dependency_writers/pyproject.py b/l2tdevtools/dependency_writers/pyproject.py index 965966fa..4b62602a 100644 --- a/l2tdevtools/dependency_writers/pyproject.py +++ b/l2tdevtools/dependency_writers/pyproject.py @@ -4,6 +4,8 @@ import glob import logging import os +import textwrap +import tomllib from l2tdevtools.dependency_writers import interface @@ -49,16 +51,24 @@ def _GenerateFromTemplate(self, template_filename, template_mappings): def Write(self): """Writes a pyproject.toml file.""" - if self._project_definition.status == "experimental": - development_status = "Development Status :: 2 - Pre-Alpha" - elif self._project_definition.status == "alpha": - development_status = "Development Status :: 3 - Alpha" - elif self._project_definition.status == "beta": - development_status = "Development Status :: 4 - Beta" - elif self._project_definition.status == "stable": - development_status = "Development Status :: 5 - Production/Stable" - else: - development_status = "" + with open(self.PATH, "rb") as file_object: + pyproject_toml = tomllib.load(file_object) + + project = pyproject_toml.get("project", {}) + + minimum_python_version = "3.10" + requires_python = project.get("requires-python") + if requires_python and requires_python.startswith(">="): + minimum_python_version = requires_python[2:] + + tool = pyproject_toml.get("tool", {}) + tool_docformatter = tool.get("docformatter", {}) + + docformatter_non_cap = tool_docformatter.get("non-cap", []) + docformatter_non_cap = ", ".join( + [f'"{value:s}"' for value in docformatter_non_cap] + ) + docformatter_non_cap = f"[{docformatter_non_cap:s}]" # TODO: handle license-files @@ -115,19 +125,26 @@ def Write(self): date_time = datetime.datetime.now() version = date_time.strftime("%Y%m%d") - # TODO: configure in project.ini file - docformatter_non_cap = "[]" - + description_long = " \\\n".join( + textwrap.wrap(self._project_definition.description_long, width=78) + ) template_mappings = { + "black_target_version": f"py{minimum_python_version:s}".replace(".", ""), "description_short": ( self._project_definition.description_short.rstrip(".") ), - "development_status": development_status, + "development_status": ( + self._project_definition.GetPythonTroveDevelopmentStatus() + ), + "description_long": f"{description_long:s} \\", "docformatter_non_cap": docformatter_non_cap, "maintainer_email": maintainer_email, "maintainer_name": maintainer_name, + "name_description": self._project_definition.name_description, + "status": self._project_definition.status, "python_module_name": python_module_name, "readme_file": readme_file, + "minimum_python_version": minimum_python_version, "version": version, } file_content = [] @@ -177,6 +194,11 @@ def Write(self): ) file_content.append(template_data) + template_data = self._GenerateFromTemplate( + "l2tdevtools.toml", template_mappings + ) + file_content.append(template_data) + template_data = self._GenerateFromTemplate("setuptools.toml", template_mappings) file_content.append(template_data) diff --git a/l2tdevtools/helpers/project.py b/l2tdevtools/helpers/project.py index 91646c5f..0c4439e0 100644 --- a/l2tdevtools/helpers/project.py +++ b/l2tdevtools/helpers/project.py @@ -2,6 +2,7 @@ import logging import os +import tomllib from l2tdevtools import project_config from l2tdevtools.review_helpers import cli @@ -134,8 +135,50 @@ def ReadDefinitionFile(self): ProjectDefinition: project definition. """ if self._project_definition is None: - project_reader = project_config.ProjectDefinitionReader() - with open(f"{self.project_name:s}.ini", encoding="utf-8") as file_object: - self._project_definition = project_reader.Read(file_object) + project_ini = f"{self.project_name:s}.ini" + + if os.path.isfile(project_ini): + logging.warning( + f"{project_ini:s} has been deprecated consider migrating to " + f"pyproject.toml [tool.l2tdevtools]" + ) + project_reader = project_config.ProjectDefinitionReader() + with open( + f"{self.project_name:s}.ini", encoding="utf-8" + ) as file_object: + self._project_definition = project_reader.Read(file_object) + + else: + with open("pyproject.toml", "rb") as file_object: + pyproject_toml = tomllib.load(file_object) + + project = pyproject_toml.get("project", {}) + project_urls = project.get("urls", {}) + tool = pyproject_toml.get("tool", {}) + tool_l2tdevtools = tool.get("l2tdevtools", {}) + + maintainer = None + maintainers = project.get("maintainers", []) + if maintainers: + maintainer = maintainers[0] + maintainer = f'{maintainer["name"]:s} <{maintainer["email"]:s}>' + + git_url = project_urls.get("Repository") + if git_url: + git_url = f"{git_url:s}.git" + + project_definition = project_config.ProjectDefinition() + project_definition.description_long = tool_l2tdevtools.get( + "description" + ).rstrip() + project_definition.description_short = project.get("description") + project_definition.git_url = git_url + project_definition.homepage_url = project_urls.get("Homepage") + project_definition.maintainer = maintainer + project_definition.name = project.get("name") + project_definition.name_description = tool_l2tdevtools.get("name") + project_definition.status = tool_l2tdevtools.get("status") + + self._project_definition = project_definition return self._project_definition diff --git a/l2tdevtools/project_config.py b/l2tdevtools/project_config.py index 1ccc62b6..3f9c6848 100644 --- a/l2tdevtools/project_config.py +++ b/l2tdevtools/project_config.py @@ -18,6 +18,13 @@ class ProjectDefinition: status (str): development status of the projects, such as "alpha" or "beta". """ + _TROVE_DEVELOPMENT_CLASSIFIERS = { + "experimental": "Development Status :: 2 - Pre-Alpha", + "alpha": "Development Status :: 3 - Alpha", + "beta": "Development Status :: 4 - Beta", + "stable": "Development Status :: 5 - Production/Stable", + } + def __init__(self): """Initializes a project configuration.""" super().__init__() @@ -31,6 +38,14 @@ def __init__(self): self.pypi_token = None self.status = "alpha" + def GetPythonTroveDevelopmentStatus(self): + """Retrieves the project status as a Python Trove classifier. + + Returns: + str: Python Trove development status classifier. + """ + return self._TROVE_DEVELOPMENT_CLASSIFIERS.get(self.status, "") + class ProjectDefinitionReader: """Project definition reader.""" diff --git a/tools/update-dependencies.py b/tools/update-dependencies.py index 70c9521a..bf495ae1 100755 --- a/tools/update-dependencies.py +++ b/tools/update-dependencies.py @@ -47,6 +47,7 @@ def Main(): writer.Write() for writer_class in ( + github_actions.GitHubActionsBuildWheelYmlWriter, github_actions.GitHubActionsLintYmlWriter, github_actions.GitHubActionsTestDockerYmlWriter, github_actions.GitHubActionsTestDocsYmlWriter,