From 354ab63ffd6cb1d0d7d29f513700b00aa2b3419c Mon Sep 17 00:00:00 2001 From: Andreas Maier Date: Thu, 26 Sep 2024 13:32:02 +0200 Subject: [PATCH] Migrated to pyproject.toml Signed-off-by: Andreas Maier --- .gitignore | 2 + Makefile | 152 +++++++++++++++----------------- minimum-constraints-develop.txt | 9 -- minimum-constraints-install.txt | 11 +++ pyproject.toml | 83 +++++++++++++++++ requirements-base.txt | 13 +-- setup.py | 139 ----------------------------- zhmc_log_forwarder/version.py | 32 +++++-- 8 files changed, 196 insertions(+), 245 deletions(-) create mode 100644 pyproject.toml delete mode 100644 setup.py diff --git a/.gitignore b/.gitignore index 9ef5095..a5b6fdc 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,9 @@ /htmlcov/ /build/ /build_doc/ +/build_docs/ /dist/ +/zhmc_log_forwarder/_version_scm.py /AUTHORS /ChangeLog /MANIFEST.in diff --git a/Makefile b/Makefile index 42e7084..d3e126e 100644 --- a/Makefile +++ b/Makefile @@ -94,9 +94,12 @@ package_name_under := zhmc_log_forwarder # Name of top level Python namespace module_name := zhmc_log_forwarder -# Package version (full version, including any pre-release suffixes, e.g. "0.1.0.dev1") -# Note: The package version is defined in zhmc_log_forwarder/version.py. -package_version := $(shell $(PYTHON_CMD) setup.py --version) +# Package version (e.g. "1.0.0a1.dev10+gd013028e" during development, or "1.0.0" +# when releasing). +# Note: The package version is automatically calculated by setuptools_scm based +# on the most recent tag in the commit history, increasing the least significant +# version indicator by 1. +package_version := $(shell $(PYTHON_CMD) -m setuptools_scm) # Python versions python_version := $(shell $(PYTHON_CMD) -c "import sys; sys.stdout.write('{}.{}.{}'.format(*sys.version_info[0:3]))") @@ -104,44 +107,69 @@ python_mn_version := $(shell $(PYTHON_CMD) -c "import sys; sys.stdout.write('{}. python_m_version := $(shell $(PYTHON_CMD) -c "import sys; sys.stdout.write('{}'.format(sys.version_info[0]))") pymn := py$(python_mn_version) +package_dir := $(package_name_under) + +# The version file is recreated by setuptools-scm on every build, so it is +# excluded from git, and also from some dependency lists. +version_file := $(package_dir)/_version_scm.py + +# Source files in the package +package_py_files := \ + $(filter-out $(version_file), $(wildcard $(package_dir)/*.py)) \ + $(wildcard $(package_dir)/*/*.py) \ + +# Directory with test source files +test_dir := tests + +# Source files with test code +test_py_files := \ + $(wildcard $(test_dir)/*.py) \ + $(wildcard $(test_dir)/*/*.py) \ + $(wildcard $(test_dir)/*/*/*.py) \ + # Directory for the generated distribution files dist_dir := dist # Distribution archives (as built by 'build' tool) -bdist_file := $(dist_dir)/$(package_name_under)-$(package_version)-py2.py3-none-any.whl +bdist_file := $(dist_dir)/$(package_name_under)-$(package_version)-py3-none-any.whl sdist_file := $(dist_dir)/$(package_name_under)-$(package_version).tar.gz -dist_files := $(bdist_file) $(sdist_file) +# Dependencies of the distribution archives. Since the $(version_file) is +# created when building the distribution archives, this must not contain +# the $(version_file). +dist_dependent_files := \ + pyproject.toml \ + LICENSE \ + README.md \ + AUTHORS.md \ + requirements.txt \ + $(wildcard $(package_dir)/zhmc_log_messages.yml) \ + $(package_py_files) \ -# Source files in the package -package_py_files := \ - $(wildcard $(module_name)/*.py) \ - $(wildcard $(module_name)/*/*.py) \ +# Directory where docs files and conf.py are located +doc_dir := docs # Directory for generated API documentation -doc_build_dir := build_doc - -# Directory where Sphinx conf.py is located -doc_conf_dir := docs - -# Documentation generator command -doc_cmd := sphinx-build -doc_opts := -v -d $(doc_build_dir)/doctrees -c $(doc_conf_dir) . +doc_build_dir := build_docs +doc_build_file := $(doc_build_dir)/html/docs/index.html # Dependents for Sphinx documentation build doc_dependent_files := \ - $(doc_conf_dir)/conf.py \ - $(wildcard $(doc_conf_dir)/*.rst) \ + $(doc_dir)/conf.py \ + $(wildcard $(doc_dir)/*.rst) \ $(package_py_files) \ + $(version_file) \ -# Directory with test source files -test_dir := tests +# Source files for checks (with PyLint and Flake8, etc.) +check_py_files := \ + $(package_py_files) \ + $(test_py_files) \ +# TODO: Add conf.py once docs are created +# $(doc_dir)/conf.py \ -# Source files with test code -test_py_files := \ - $(wildcard $(test_dir)/*.py) \ - $(wildcard $(test_dir)/*/*.py) \ - $(wildcard $(test_dir)/*/*/*.py) \ +# Documentation generator command +doc_cmd := sphinx-build +doc_opts := -v -d $(doc_build_dir)/doctrees -c $(doc_dir) . # Directory for .done files done_dir := done @@ -159,12 +187,6 @@ flake8_rc_file := .flake8 # PyLint config file pylint_rc_file := .pylintrc -# Source files for check (with PyLint and Flake8) -check_py_files := \ - setup.py \ - $(package_py_files) \ - $(test_py_files) \ - # Packages whose dependencies are checked using pip-missing-reqs check_reqs_packages := pip_check_reqs pipdeptree build pytest coverage coveralls flake8 pylint safety twine @@ -174,19 +196,6 @@ else pytest_opts := $(TESTOPTS) endif -# Files to be built -build_files := $(bdist_file) $(sdist_file) - -# Files the distribution archive depends upon. -# This is also used for 'include' statements in MANIFEST.in. -# Wildcards can be used directly (i.e. without wildcard function). -dist_included_files := \ - setup.py \ - LICENSE \ - README.md \ - requirements.txt \ - $(package_py_files) \ - # No built-in rules needed: .SUFFIXES: @@ -244,11 +253,11 @@ safety: $(done_dir)/safety_develop_$(pymn)_$(PACKAGE_LEVEL).done $(done_dir)/saf @echo '$@ done.' .PHONY: build -build: $(build_files) +build: $(bdist_file) $(sdist_file) @echo '$@ done.' .PHONY: builddoc -builddoc: $(doc_build_dir)/html/docs/index.html +builddoc: $(doc_build_file) @echo '$@ done.' .PHONY: all @@ -256,12 +265,12 @@ all: install develop check_reqs check test build builddoc authors @echo '$@ done.' .PHONY: upload -upload: _check_version uninstall $(dist_files) +upload: _check_version uninstall $(bdist_file) $(sdist_file) ifeq (,$(findstring .dev,$(package_version))) @echo '==> This will upload $(package_name) version $(package_version) to PyPI!' @echo -n '==> Continue? [yN] ' @bash -c 'read answer; if [ "$$answer" != "y" ]; then echo "Aborted."; false; fi' - twine upload $(dist_files) + twine upload $(bdist_file) $(sdist_file) @echo 'Done: Uploaded $(package_name) version to PyPI: $(package_version)' @echo '$@ done.' else @@ -320,61 +329,44 @@ ifeq (,$(package_version)) $(error Package version could not be determined) endif -$(done_dir)/base_$(pymn)_$(PACKAGE_LEVEL).done: requirements-base.txt minimum-constraints-install.txt minimum-constraints-develop.txt +$(done_dir)/base_$(pymn)_$(PACKAGE_LEVEL).done: Makefile requirements-base.txt minimum-constraints-install.txt minimum-constraints-develop.txt -$(call RM_FUNC,$@) @echo 'Installing/upgrading base packages with PACKAGE_LEVEL=$(PACKAGE_LEVEL)' $(PYTHON_CMD) -m pip install $(pip_level_opts) -r requirements-base.txt touch $@ @echo 'Done: Installed/upgraded base packages' -$(done_dir)/develop_$(pymn)_$(PACKAGE_LEVEL).done: $(done_dir)/base_$(pymn)_$(PACKAGE_LEVEL).done requirements-develop.txt minimum-constraints-install.txt minimum-constraints-develop.txt +$(done_dir)/develop_$(pymn)_$(PACKAGE_LEVEL).done: $(done_dir)/base_$(pymn)_$(PACKAGE_LEVEL).done Makefile requirements-develop.txt minimum-constraints-install.txt minimum-constraints-develop.txt -$(call RM_FUNC,$@) @echo 'Installing/upgrading development packages with PACKAGE_LEVEL=$(PACKAGE_LEVEL)' $(PIP_CMD) install $(pip_level_opts) -r requirements-develop.txt touch $@ @echo 'Done: Installed/upgraded development packages' -$(done_dir)/install_$(pymn)_$(PACKAGE_LEVEL).done: $(done_dir)/base_$(pymn)_$(PACKAGE_LEVEL).done requirements.txt minimum-constraints-install.txt minimum-constraints-develop.txt setup.py $(package_py_files) +$(done_dir)/install_$(pymn)_$(PACKAGE_LEVEL).done: $(done_dir)/base_$(pymn)_$(PACKAGE_LEVEL).done Makefile requirements.txt minimum-constraints-install.txt minimum-constraints-develop.txt pyproject.toml $(dist_dependent_files) -$(call RM_FUNC,$@) @echo 'Installing $(package_name) (non-editable) with PACKAGE_LEVEL=$(PACKAGE_LEVEL)' - $(PIP_CMD) install $(pip_level_opts) -r requirements.txt - $(PIP_CMD) install . + $(PIP_CMD) install $(pip_level_opts) . which zhmc_log_forwarder zhmc_log_forwarder --version touch $@ @echo 'Done: Installed $(package_name)' -$(doc_build_dir)/html/docs/index.html: Makefile $(doc_dependent_files) +$(doc_build_file): $(done_dir)/develop_$(pymn)_$(PACKAGE_LEVEL).done Makefile $(doc_dependent_files) @echo "Makefile: Creating the HTML pages with top level file: $@" -$(call RM_FUNC,$@) $(doc_cmd) -b html $(doc_opts) $(doc_build_dir)/html @echo "Done: Created the HTML pages with top level file: $@" -# Note: distutils depends on the right files specified in MANIFEST.in, even when -# they are already specified e.g. in 'package_data' in setup.py. -# We generate the MANIFEST.in file automatically, to have a single point of -# control (this Makefile) for what gets into the distribution archive. -MANIFEST.in: Makefile $(dist_included_files) - @echo "Makefile: Creating the manifest input file" - echo "# MANIFEST.in file generated by Makefile - DO NOT EDIT!!" >$@ -ifeq ($(PLATFORM),Windows_native) - for %%f in ($(dist_included_files)) do (echo include %%f >>$@) -else - echo "$(dist_included_files)" | xargs -n 1 echo include >>$@ -endif - @echo "Makefile: Done creating the manifest input file: $@" - -# Distribution archives. -# Note: Deleting MANIFEST causes distutils (setup.py) to read MANIFEST.in and to -# regenerate MANIFEST. Otherwise, changes in MANIFEST.in will not be used. -# Note: Deleting build is a safeguard against picking up partial build products -# which can lead to incorrect hashbangs in scripts in wheel archives. -$(bdist_file) $(sdist_file): Makefile MANIFEST.in $(dist_included_files) - @echo 'Makefile: Creating distribution archives: $@' - -$(call RM_FUNC,MANIFEST) - -$(call RMDIR_FUNC,$(package_name).egg-info .eggs build) - $(PYTHON_CMD) -m build --outdir $(dist_dir) - @echo 'Makefile: Done creating distribution archives: $@' +$(sdist_file): $(done_dir)/develop_$(pymn)_$(PACKAGE_LEVEL).done Makefile $(dist_dependent_files) + @echo "Makefile: Building the source distribution archive: $(sdist_file)" + $(PYTHON_CMD) -m build --sdist --outdir $(dist_dir) . + @echo "Makefile: Done building the source distribution archive: $(sdist_file)" + +$(bdist_file) $(version_file): $(done_dir)/develop_$(pymn)_$(PACKAGE_LEVEL).done Makefile $(dist_dependent_files) + @echo "Makefile: Building the wheel distribution archive: $(bdist_file)" + $(PYTHON_CMD) -m build --wheel --outdir $(dist_dir) -C--universal . + @echo "Makefile: Done building the wheel distribution archive: $(bdist_file)" $(done_dir)/pylint_$(pymn)_$(PACKAGE_LEVEL).done: $(done_dir)/develop_$(pymn)_$(PACKAGE_LEVEL).done Makefile $(pylint_rc_file) $(check_py_files) @echo "Makefile: Running Pylint" diff --git a/minimum-constraints-develop.txt b/minimum-constraints-develop.txt index b4c5f45..8fd5399 100644 --- a/minimum-constraints-develop.txt +++ b/minimum-constraints-develop.txt @@ -6,15 +6,6 @@ # mainly to bring pip to a defined level. -# ------------------------------------------------------------------------------ -# Base packages. -# Must be consistent with requirements-base.txt. - -pip==23.3 -setuptools==70.0.0 -wheel==0.38.1 - - # ------------------------------------------------------------------------------ # Direct dependencies for development. # Must be consistent with requirements-develop.txt. diff --git a/minimum-constraints-install.txt b/minimum-constraints-install.txt index 2bbc94f..f6c053f 100644 --- a/minimum-constraints-install.txt +++ b/minimum-constraints-install.txt @@ -2,6 +2,17 @@ # Pip constraints file for minimum versions of packages needed for installation. +# ------------------------------------------------------------------------------ +# Base packages. +# Must be consistent with requirements-base.txt. + +pip==23.3 +setuptools==70.0.0 +# Note on not specifying 'setuptools-scm[toml]': Extras cannot be in constraints files +setuptools-scm==8.1.0 +wheel==0.38.1 + + # ------------------------------------------------------------------------------ # Direct dependencies for installation. # Must be consistent with requirements.txt. diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..45bbd50 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,83 @@ +# pyproject.toml file for zhmc-log-forwarder +# Documentation: +# pyproject.toml: https://packaging.python.org/en/latest/guides/writing-pyproject-toml/ +# setuptools items: https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html + +[build-system] +requires = [ + # Keep in sync with base-requirements.txt and the base dependencies in + # minimum-constraints-install.txt + "setuptools>=70.0.0", + "setuptools-scm[toml]>=8.1.0", + "wheel>=0.38.1" +] +build-backend = "setuptools.build_meta" + +[tool.setuptools] +platforms = ["any"] +script-files = [] +zip-safe = true + +[tool.setuptools.packages.find] +# setuptools needs all sub-packages to be specified as well to avoid the +# ambiguity warning. That can be done by specifyng 'packages' with a full list +# of packages including sub-packages, or by specifying 'packages.find' with +# wildcards. The strings in 'packages.find' are matched using glob pattern +# matching against the package path. +include = [ + "zhmc_log_forwarder", + "zhmc_log_forwarder.*", +] + +[tool.setuptools.package-data] +zhmc_log_forwarder = [ + "zhmc_log_messages.yml", +] + +[project.scripts] +zhmc_log_forwarder = "zhmc_log_forwarder.zhmc_log_forwarder:main" + +[project] +name = "zhmc_log_forwarder" +description = "A log forwarder for the IBM Z HMC" +authors = [ + {name = "Andreas Maier", email = "maiera@de.ibm.com"} +] +maintainers = [ + {name = "Andreas Maier", email = "maiera@de.ibm.com"} +] + +readme = "README.md" +license = {text = "Apache License, Version 2.0"} +keywords = ["hmc", "prometheus", "monitoring"] +classifiers = [ + "License :: OSI Approved :: Apache Software License", + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Information Technology", + 'Intended Audience :: System Administrators', + "Topic :: System :: Systems Administration", + "Environment :: Console", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", +] +requires-python = ">=3.8" +dynamic = ["version", "dependencies"] + +[project.urls] +Homepage = "https://github.com/zhmcclient/zhmc-log-forwarder" +"Bug Tracker" = "https://github.com/zhmcclient/zhmc-log-forwarder/issues" +Documentation = "https://zhmc-log-forwarder.readthedocs.io/en/latest/" +"Source Code" = "https://github.com/zhmcclient/zhmc-log-forwarder" +Changelog = "https://zhmc-log-forwarder.readthedocs.io/en/latest/changes.html" + +[tool.setuptools.dynamic] +dependencies = {file = ["requirements.txt"]} + +[tool.setuptools_scm] +# Get the version from the Git tag, and write a version file: +version_file = "zhmc_log_forwarder/_version_scm.py" diff --git a/requirements-base.txt b/requirements-base.txt index 97a2484..1af6661 100644 --- a/requirements-base.txt +++ b/requirements-base.txt @@ -1,20 +1,15 @@ # ------------------------------------------------------------------------------ # Pip requirements file for base packages for a Python env. # -# Note: Base packages are those needed for setting up an initial environment, -# mainly to bring pip to a defined level. +# Base packages are those needed for pip and automatic package version detection. # ------------------------------------------------------------------------------ # Base packages. -# Must be consistent with minimum-constraints-develop.txt. +# Base dependencies (must be consistent with minimum-constraints-install.txt +# and build-system.requires in pyproject.toml) -# pip 10.0.0 introduced the --exclude-editable option. -# pip 18.0 is needed on pypy3 (py36) to support constraints like cffi!=1.11.3,>=1.8. -# Pip 20.2 introduced a new resolver whose backtracking had issues that were resolved only in 21.2.2. -# pip>=21.0 is needed for the cryptography package on Windows on GitHub Actions. pip>=23.3 - setuptools>=70.0.0 - +setuptools-scm[toml]>=8.1.0 wheel>=0.38.1 diff --git a/setup.py b/setup.py deleted file mode 100644 index 2cc4132..0000000 --- a/setup.py +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/env python -# Copyright 2019-2019 IBM Corp. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -Setup script for zhmc-log-forwarder project. -""" - -import os -import re -import setuptools - - -def get_version(version_file): - """ - Execute the specified version file and return the value of the __version__ - global variable that is set in the version file. - - Note: Make sure the version file does not depend on any packages in the - requirements list of this package (otherwise it cannot be executed in - a fresh Python environment). - """ - # pylint: disable=unspecified-encoding - with open(version_file, 'r') as fp: - version_source = fp.read() - _globals = {} - exec(version_source, _globals) # pylint: disable=exec-used - return _globals['__version__'] - - -def get_requirements(requirements_file): - """ - Parse the specified requirements file and return a list of its non-empty, - non-comment lines. The returned lines are without any trailing newline - characters. - """ - # pylint: disable=unspecified-encoding - with open(requirements_file, 'r') as fp: - lines = fp.readlines() - reqs = [] - for line in lines: - line = line.strip('\n') - if not line.startswith('#') and line != '': - reqs.append(line) - return reqs - - -def read_file(a_file): - """ - Read the specified file and return its content as one string. - """ - # pylint: disable=unspecified-encoding - with open(a_file, 'r') as fp: - content = fp.read() - return content - - -# pylint: disable=invalid-name -requirements = get_requirements('requirements.txt') -install_requires = [req for req in requirements - if req and not re.match(r'[^:]+://', req)] -dependency_links = [req for req in requirements - if req and re.match(r'[^:]+://', req)] -package_version = get_version(os.path.join('zhmc_log_forwarder', 'version.py')) - -# Docs on setup(): -# * https://docs.python.org/2.7/distutils/apiref.html? -# highlight=setup#distutils.core.setup -# * https://setuptools.readthedocs.io/en/latest/setuptools.html# -# new-and-changed-setup-keywords - -setuptools.setup( - name='zhmc-log-forwarder', - version=package_version, - packages=[ - 'zhmc_log_forwarder', - ], - package_data={ - 'zhmc_log_forwarder': [ - 'zhmc_log_messages.yml', - ], - }, - entry_points=dict( - console_scripts=[ - 'zhmc_log_forwarder=zhmc_log_forwarder.zhmc_log_forwarder:main', - ] - ), - install_requires=install_requires, - dependency_links=dependency_links, - description='A log forwarder for the IBM Z HMC', - long_description=read_file('README.md'), - long_description_content_type='text/markdown', - license='Apache License, Version 2.0', - author='Andreas Maier', - author_email='maiera@de.ibm.com', - maintainer='Andreas Maier', - maintainer_email='maiera@de.ibm.com', - url='https://github.com/zhmcclient/zhmc-log-forwarder', - project_urls={ - 'Bug Tracker': - 'https://github.com/zhmcclient/zhmc-log-forwarder/issues', - 'Documentation': 'https://zhmc-log-forwarder.readthedocs.io/en/latest/', - 'Source Code': 'https://github.com/zhmcclient/zhmc-log-forwarder', - }, - - options={'bdist_wheel': {'universal': True}}, - zip_safe=True, # This package can safely be installed from a zip file - platforms='any', - - python_requires='>=3.8', - classifiers=[ - 'Development Status :: 4 - Beta', - 'Environment :: Console', - 'Intended Audience :: Developers', - 'Intended Audience :: System Administrators', - 'Intended Audience :: Information Technology', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.12', - 'Topic :: Software Development :: Libraries :: Python Modules', - 'Topic :: System :: Systems Administration', - ] -) diff --git a/zhmc_log_forwarder/version.py b/zhmc_log_forwarder/version.py index 11218c7..e2a0862 100644 --- a/zhmc_log_forwarder/version.py +++ b/zhmc_log_forwarder/version.py @@ -16,11 +16,27 @@ Version of the zhmc-log-forwarder project. """ -#: The full version of this package including any development levels, as a -#: :term:`string`. -#: -#: Possible formats for this version string are: -#: -#: * "M.N.P.devD": Development level D of a not yet released version M.N.P -#: * "M.N.P": A released version M.N.P -__version__ = '1.0.0.dev1' +# In the RTD docs build, _version_scm.py does not exist: +try: + from ._version_scm import version, version_tuple +except ImportError: + version = "unknown" + version_tuple = tuple("unknown") + +__all__ = ['__version__', '__version_tuple__'] + +# The full version of this package including any development levels, as a +# string. +# +# Possible formats for this version string are: +# * "M.N.Pa1.dev7+g1234567": A not yet released version M.N.P +# * "M.N.P": A released version M.N.P +__version__ = version + +# The full version of this package including any development levels, as a +# tuple of version items, converted to integer where possible. +# +# Possible formats for this version tuple are: +# * (M, N, P, 'a1', 'dev7', 'g1234567'): A not yet released version M.N.P +# * (M, N, P): A released version M.N.P +__version_tuple__ = version_tuple