diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b982719653..a9ae9a7dd4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -28,8 +28,10 @@ Added Changed ~~~~~~~ -Replaced deprecated `pkg_resources` library with `importlib` & `packaging` -for plugins discovery and loading. +* Replaced deprecated `pkg_resources` library with `importlib` & `packaging` + for plugins discovery and loading. +* Implements pep-517 (pyproject.toml) and replaces pbr dependency + with setuptools-scm Removed ~~~~~~~ diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000..2ebcb6286f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,73 @@ +[project] +name = "rally" +description = "Generic Testing Framework & Tool that unifies all types of testing" +authors = [ + {name = "OpenStack", email = "openstack-discuss@lists.openstack.org"}, +] +readme = "README.rst" +license = { text = "Apache License, Version 2.0"} +classifiers = [ + "Environment :: OpenStack", + "Intended Audience :: Developers", + "Intended Audience :: Information Technology", + "License :: OSI Approved :: Apache Software License", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", +] +dynamic = ["version", "dependencies"] +requires-python = ">=3.9" + +[project.urls] +Homepage = "https://rally.readthedocs.io/" +Issues = "https://bugs.launchpad.net/rally" +Source = "https://opendev.org/openstack/rally" +Github = "https://github.com/openstack/rally" +Changelog = "https://github.com/openstack/rally/blob/master/CHANGELOG.rst" + +[build-system] +requires = [ + "setuptools>=64", + "setuptools_scm>=8" +] +build-backend = "setuptools.build_meta" + +[tool.setuptools] +include-package-data = true + +[tool.setuptools.data-files] +"etc/bash_completion.d" = ["etc/rally.bash_completion"] + +[tool.setuptools.dynamic] +dependencies = {file = ["requirements.txt"]} + +[project.optional-dependencies] +mysql = [ + "PyMySQL>=0.7.6" # MIT +] +postgres = [ + "psycopg2>=2.5" # LGPL/ZPL +] + +[tool.setuptools_scm] +local_scheme = "no-local-version" + +[tool.setuptools.packages.find] +where = ["."] +include = ["rally*"] +exclude = ["tests"] + +[project.scripts] +rally = "rally.cli.main:main" + +[project.entry-points."oslo.config.opts"] +rally = "rally.common.opts:list_opts" + +[project.entry-points."oslo.config.opts.defaults"] +rally = "rally.common.opts:update_opt_defaults" diff --git a/rally/cli/cliutils.py b/rally/cli/cliutils.py index 47769d649b..97d0a6ee4c 100644 --- a/rally/cli/cliutils.py +++ b/rally/cli/cliutils.py @@ -116,7 +116,11 @@ def print_list(objs, fields, formatters=None, sortby_index=0, if print_border and print_row_border: headers_horizontal_char = "=" - kwargs["hrules"] = prettytable.ALL + try: + kwargs["hrules"] = prettytable.HRuleStyle.ALL + except AttributeError: # pragma: no cover + # old prettytable + kwargs["hrules"] = prettytable.ALL else: headers_horizontal_char = "-" pt = prettytable.PrettyTable(field_labels) @@ -143,7 +147,11 @@ def print_list(objs, fields, formatters=None, sortby_index=0, pt.add_row(row) if not print_border or not print_header: - pt.set_style(prettytable.PLAIN_COLUMNS) + try: + pt.set_style(prettytable.TableStyle.PLAIN_COLUMNS) + except AttributeError: # pragma: no cover + # old prettytable + pt.set_style(prettytable.PLAIN_COLUMNS) pt.left_padding_width = 0 pt.right_padding_width = 1 @@ -506,7 +514,7 @@ def _compose_action_description(action_fn): def _print_version(): from rally.common import version - print("Rally version: %s" % version.version_string()) + print("Rally version: %s" % version.__version__) packages = version.plugins_versions() if packages: print("\nInstalled Plugins:") diff --git a/rally/common/version.py b/rally/common/version.py index 2ebe2dc588..e478e28583 100644 --- a/rally/common/version.py +++ b/rally/common/version.py @@ -13,20 +13,32 @@ # License for the specific language governing permissions and limitations # under the License. -from pbr import version as pbr_version + +from importlib.metadata import version as _version from rally.common.plugin import discover as rally_discover RALLY_VENDOR = "OpenStack Foundation" RALLY_PRODUCT = "OpenStack Rally" -RALLY_PACKAGE = None # OS distro package version suffix -loaded = False -version_info = pbr_version.VersionInfo("rally") +try: + # Try to get version from installed package metadata + __version__ = _version("rally") +except Exception: + # Fallback to setuptools_scm for development installs + try: + from setuptools_scm import get_version + + __version__ = get_version() + except Exception: + # Final fallback - this should rarely happen + __version__ = "0.0.0" -def version_string(): - return version_info.semantic_version().debian_string() +__version_tuple__ = tuple( + int(p) if p.isdigit() else p + for p in __version__.replace("-", ".").split(".") +) def database_revision(): @@ -42,3 +54,19 @@ def plugins_versions(): (ep.dist.name, ep.dist.version) for ep in rally_discover.iter_entry_points() ) + + +# backward compatibility +class version_info: + + @classmethod + def semantic_version(cls): + return cls + + @classmethod + def version_tuple(cls): + return __version_tuple__ + + +def version_string(): + return __version__ diff --git a/requirements.txt b/requirements.txt index cacacec739..bfb8fb9aad 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,24 +1,21 @@ -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. # Rally core dependencies -alembic!=1.2.0,!=1.6.3 # MIT +alembic!=1.2.0,!=1.6.3 Jinja2 # BSD -jsonschema # MIT +jsonschema markupsafe oslo.config!=4.3.0,!=4.4.0 # Apache Software License # do not forget to remove `testresources` from test-requirements. it is a # dependency of oslo.db for tests oslo.db # Apache Software License oslo.log!=3.44.2,!=4.1.2,!=4.2.0,!=5.0.1,!=5.0.2,!=5.1.0 # Apache Software License +packaging!=20.5,!=20.6,!=20.7 # Apache Software License/BSD License paramiko!=2.9.0,!=2.9.1 # LGPL -pbr!=2.1.0 # Apache Software License -PrettyTable!=3.4.0 # BSD +PrettyTable!=3.4.0 pyOpenSSL # Apache License, Version 2.0 PyYAML # MIT python-subunit # Apache-2.0 or BSD requests!=2.20.0,!=2.24.0 # Apache-2.0 +setuptools_scm SQLAlchemy>=2 # MIT virtualenv!=16.3.0 # MIT -packaging!=20.5,!=20.6,!=20.7 # Apache Software License/BSD License diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 110910adf0..0000000000 --- a/setup.cfg +++ /dev/null @@ -1,45 +0,0 @@ -[metadata] -name = rally -summary = Generic Testing Framework & Tool that unifies all types of testing. -description_file = - README.rst -author = OpenStack -author_email = openstack-discuss@lists.openstack.org -home_page = https://rally.readthedocs.io/ -license = Apache License, Version 2.0 -requires_python = >=3.9 -classifier = - Environment :: OpenStack - Intended Audience :: Developers - Intended Audience :: Information Technology - License :: OSI Approved :: Apache Software License - Operating System :: POSIX :: Linux - Programming Language :: Python - Programming Language :: Python :: Implementation :: CPython - Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - Programming Language :: Python :: 3.12 - -[files] -packages = - rally - -data_files = - etc/bash_completion.d = - etc/rally.bash_completion - -[entry_points] -console_scripts = - rally = rally.cli.main:main -oslo.config.opts = - rally = rally.common.opts:list_opts -oslo.config.opts.defaults = - rally = rally.common.opts:update_opt_defaults - -[extras] -mysql = - PyMySQL>=0.7.6 # MIT -postgres = - psycopg2>=2.5 # LGPL/ZPL diff --git a/setup.py b/setup.py deleted file mode 100644 index 6a55493096..0000000000 --- a/setup.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. -# -# 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 -# -# http://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. - -import setuptools - - -setuptools.setup( - setup_requires=['pbr>=1.8'], - pbr=True) diff --git a/test-requirements.txt b/test-requirements.txt index 67f03fd9d0..6e7b36d962 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -4,7 +4,7 @@ hacking>=4.0 # Apache Software License -fixtures # Apache Software License/BSD License +fixtures # Apache-2.0 or BSD pytest # MIT # py.test plugin for measuring coverage. pytest-cov # MIT diff --git a/tests/unit/common/test_version.py b/tests/unit/common/test_version.py index 63e20ce94f..2ef3a1b4ad 100644 --- a/tests/unit/common/test_version.py +++ b/tests/unit/common/test_version.py @@ -11,6 +11,7 @@ # License for the specific language governing permissions and limitations # under the License. +import importlib from unittest import mock from rally.common import version @@ -19,18 +20,55 @@ from tests.unit import test class ModuleTestCase(test.TestCase): - VERSION_REGEX = r"^\d+\.\d+\.\d+(~dev\d+)?$" + VERSION_REGEX = r"^\d+\.\d+\.\d+(\.dev\d+)?$" def test_version_info(self): - version_str = version.version_info.semantic_version().debian_string() + version_str = version.version_string() self.assertRegex(version_str, self.VERSION_REGEX) + self.assertNotEqual("0.0.0", version_str) + + self.assertEqual( + version.__version_tuple__, + version.version_info.semantic_version().version_tuple() + ) + + @mock.patch("setuptools_scm.get_version") + @mock.patch("importlib.metadata.version") + def test_version_string(self, mock_version, mock_get_version): + self.addCleanup(lambda: importlib.reload(version)) + + mock_version.return_value = "foo_version" + mock_get_version.return_value = "bar_version" + + # reload module, so it can rediscover version + importlib.reload(version) + # ensure that we reload version after the test + self.addCleanup(lambda: importlib.reload(version)) - @mock.patch("rally.common.version.version_info") - def test_version_string(self, mock_version_info): - mock_sv = mock.Mock() - mock_sv.debian_string.return_value = "foo_version" - mock_version_info.semantic_version.return_value = mock_sv self.assertEqual("foo_version", version.version_string()) + self.assertEqual("foo_version", version.__version__) + + mock_version.assert_called_once_with("rally") + self.assertFalse(mock_get_version.called) + + # check fallback + mock_version.reset_mock() + mock_version.side_effect = Exception("oops") + + importlib.reload(version) + self.assertEqual("bar_version", version.__version__) + mock_version.assert_called_once_with("rally") + mock_get_version.assert_called_once_with() + + # check fallback 2 + mock_version.reset_mock() + mock_get_version.reset_mock() + mock_get_version.side_effect = Exception("oops2") + + importlib.reload(version) + self.assertEqual("0.0.0", version.__version__) + mock_version.assert_called_once_with("rally") + mock_get_version.assert_called_once_with() @mock.patch("rally.common.db.schema.schema_revision", return_value="foo") def test_database_revision(self, mock_schema_revision): diff --git a/tox.ini b/tox.ini index 2f50607da4..a2b428802e 100644 --- a/tox.ini +++ b/tox.ini @@ -24,11 +24,11 @@ deps = -r{toxinidir}/requirements.txt usedevelop = True pre_commands = find . -type f -name "*.pyc" -delete commands = - pytest -vv \ # base command + pytest -vv \ # base command --html={env:PYTEST_REPORT} \ # html report - --self-contained-html \ # embedded css - --durations=10 \ # get a list of the slowest 10 tests - -n auto \ # launch tests in parallel + --self-contained-html \ # embedded css + --durations=10 \ # get a list of the slowest 10 tests + -n auto \ # launch tests in parallel {posargs:{env:TESTS_DIR}} distribute = false basepython = python3 diff --git a/upper-constraints.txt b/upper-constraints.txt index 4db86b150e..c0a9cd6fb6 100644 --- a/upper-constraints.txt +++ b/upper-constraints.txt @@ -1,61 +1,59 @@ -alembic===1.14.0 -attrs===24.2.0 +alembic===1.16.4 +attrs===25.3.0 bcrypt===4.0.1 -certifi===2024.8.30 +certifi===2025.7.14 cffi===1.17.1 -charset-normalizer===3.4.0 -cryptography===42.0.8 +charset-normalizer===3.4.2 +cryptography===43.0.3 debtcollector===3.0.0 -distlib===0.3.9 -filelock===3.16.1 -greenlet===3.1.1 +distlib===0.4.0 +filelock===3.18.0 +greenlet===3.2.3 idna===3.10 -importlib_metadata===8.5.0 iso8601===2.1.0 -Jinja2===3.1.4 -jsonschema===4.19.2 -jsonschema-specifications===2024.10.1 -Mako===1.3.6 +Jinja2===3.1.6 +jsonschema===4.25.0 +jsonschema-specifications===2025.4.1 +Mako===1.3.10 MarkupSafe===3.0.2 -msgpack===1.1.0 -netaddr===0.10.1 -oslo.config===9.7.0 -oslo.context===5.7.0 -oslo.db===17.0.0 -oslo.i18n===6.5.0 -oslo.log===6.1.2 -oslo.serialization===5.6.0 -oslo.utils===7.4.0 -packaging===24.2 -paramiko===3.5.0 -pbr===6.1.0 -pip===24.2 -platformdirs===4.3.6 -prettytable===3.11.0 -psutil===6.1.0 +msgpack===1.1.1 +netaddr===1.3.0 +oslo.config===10.0.0 +oslo.context===6.0.0 +oslo.db===17.3.0 +oslo.i18n===6.5.1 +oslo.log===7.2.0 +oslo.serialization===5.7.0 +oslo.utils===9.0.0 +packaging===25.0 +paramiko===3.5.1 +pbr===6.1.1 +pip===25.1.1 +platformdirs===4.3.8 +prettytable===3.16.0 +psutil===7.0.0 pycparser===2.22 PyNaCl===1.5.0 pyOpenSSL===24.2.1 -pyparsing===3.2.0 +pyparsing===3.2.3 python-dateutil===2.9.0.post0 python-subunit===1.4.4 PyYAML===6.0.2 -referencing===0.35.1 -requests===2.32.3 +referencing===0.36.2 +requests===2.32.4 rfc3986===2.0.0 -rpds-py===0.21.0 -setuptools===75.1.0 -six===1.16.0 -SQLAlchemy===2.0.36 -stevedore===5.4.0 -testresources===2.0.1 +rpds-py===0.26.0 +setuptools===80.9.0 +setuptools-scm===8.3.1 +six===1.17.0 +SQLAlchemy===2.0.41 +stevedore===5.4.1 +testresources===2.0.2 testscenarios===0.5.0 testtools===2.7.2 -typing_extensions===4.12.2 -tzdata===2024.2 +typing_extensions===4.14.1 +tzdata===2025.2 urllib3===1.26.20 -virtualenv===20.27.1 +virtualenv===20.31.2 wcwidth===0.2.13 -wheel===0.44.0 -wrapt===1.16.0 -zipp===3.21.0 +wrapt===1.17.2