diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 32f98c2..177e9a5 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -65,7 +65,7 @@ jobs: strategy: fail-fast: false matrix: - python-versions: ["3.10", "3.11", "3.12", "3.13"] + python-versions: ["3.10", "3.11", "3.12", "3.13", "3.14"] steps: - name: Check out Repository id: check-out-repository @@ -102,7 +102,7 @@ jobs: strategy: fail-fast: false matrix: - python-versions: ["3.10", "3.11", "3.12", "3.13"] + python-versions: ["3.10", "3.11", "3.12", "3.13", "3.14"] steps: - name: Check out Repository @@ -130,7 +130,7 @@ jobs: strategy: fail-fast: false matrix: - python-versions: ["3.10", "3.11", "3.12", "3.13"] + python-versions: ["3.10", "3.11", "3.12", "3.13", "3.14"] steps: - name: Check out Repository diff --git a/.github/workflows/fast-tests.yml b/.github/workflows/fast-tests.yml index a31b9dd..629a725 100644 --- a/.github/workflows/fast-tests.yml +++ b/.github/workflows/fast-tests.yml @@ -14,7 +14,7 @@ jobs: strategy: fail-fast: false matrix: - python-versions: ["3.10", "3.11", "3.12", "3.13"] + python-versions: ["3.10", "3.11", "3.12", "3.13", "3.14"] steps: - name: Check out Repository diff --git a/doc/changes/changelog.md b/doc/changes/changelog.md index dc68d80..ff7a87d 100644 --- a/doc/changes/changelog.md +++ b/doc/changes/changelog.md @@ -1,6 +1,7 @@ # 📝 Changes * [unreleased](unreleased.md) +* [1.0.0](changes_1.0.0.md) * [0.16.0](changes_0.16.0.md) * [0.15.0](changes_0.15.0.md) * [0.14.0](changes_0.14.0.md) @@ -26,6 +27,7 @@ hidden: --- unreleased +changes_1.0.0 changes_0.16.0 changes_0.15.0 changes_0.14.0 diff --git a/doc/changes/changes_1.0.0.md b/doc/changes/changes_1.0.0.md new file mode 100644 index 0000000..fcb64eb --- /dev/null +++ b/doc/changes/changes_1.0.0.md @@ -0,0 +1,19 @@ +# 1.0.0 - 2026-07-29 + +## Summary + +Added support for Python 3.14 + +## Dependencies + +* #163: Added support for Python 3.14 + +## Dependency Updates + +### `dev` + +* Updated dependency `pytest-exasol-backend:1.4.1` to `1.5.0` + +## Refactoring + +* #152: Re-enabled `check-workflows` in `checks.yml` and updated to `exasol-toolbox` 10.0.0 diff --git a/doc/changes/unreleased.md b/doc/changes/unreleased.md index 34979f2..fb47370 100644 --- a/doc/changes/unreleased.md +++ b/doc/changes/unreleased.md @@ -1,7 +1,3 @@ # Unreleased ## Summary - -## Refactoring - -* #152: Re-enabled `check-workflows` in `checks.yml` and updated to `exasol-toolbox` 10.0.0 \ No newline at end of file diff --git a/noxconfig.py b/noxconfig.py index 07b8efd..5f8960d 100644 --- a/noxconfig.py +++ b/noxconfig.py @@ -5,5 +5,5 @@ PROJECT_CONFIG = BaseConfig( root_path=Path(__file__).parent, project_name="python_extension_common", - python_versions=("3.10", "3.11", "3.12", "3.13"), + python_versions=("3.10", "3.11", "3.12", "3.13", "3.14"), ) diff --git a/poetry.lock b/poetry.lock index 60a5d07..62db137 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1179,6 +1179,25 @@ files = [ dev = ["coverage", "flake8", "lxml", "lxml-stubs", "memray ; platform_python_implementation != \"PyPy\"", "mypy", "psutil", "sphinx", "tox (>=4.0)", "xmlschema (>=4.0.1)"] docs = ["readthedocs-sphinx-search", "sphinx"] +[[package]] +name = "exasol-bucketfs" +version = "2.1.0" +description = "BucketFS utilities for the Python programming language" +optional = false +python-versions = "<4.0,>=3.10" +groups = ["main", "dev"] +markers = "python_version >= \"3.12\"" +files = [ + {file = "exasol_bucketfs-2.1.0-py3-none-any.whl", hash = "sha256:82d0f87d756921846ce60b7af112a71a491019aa46976693046399805c303a12"}, + {file = "exasol_bucketfs-2.1.0.tar.gz", hash = "sha256:55583af02e8a65b40bb15bc462e8bcdf1c0195e00f8c1e70148724869fb6da29"}, +] + +[package.dependencies] +attrs = ">=23.2.0" +exasol-saas-api = ">2.0.0,<3.0" +httpx = ">=0.27.0" +requests = ">=2.32.4" + [[package]] name = "exasol-bucketfs" version = "2.2.0" @@ -1186,6 +1205,7 @@ description = "BucketFS utilities for the Python programming language" optional = false python-versions = "<3.14,>=3.10" groups = ["main", "dev"] +markers = "python_version < \"3.12\"" files = [ {file = "exasol_bucketfs-2.2.0-py3-none-any.whl", hash = "sha256:cc1e1331467f64b951ee484fa0e51ba40d1e28bd7963cd09a021b793ba66531c"}, {file = "exasol_bucketfs-2.2.0.tar.gz", hash = "sha256:a66fe0e80756e034adc665dc1bc7f9c2115ecbb4d83ffd214099c35c6c62e539"}, @@ -1263,21 +1283,21 @@ types-requests = ">=2.31.0.6,<3.0.0.0" [[package]] name = "exasol-script-languages-container-tool" -version = "4.1.0" +version = "4.2.0" description = "Script Languages Container Tool" optional = false -python-versions = "<3.14,>=3.10" +python-versions = "<3.15,>=3.10" groups = ["main", "dev"] files = [ - {file = "exasol_script_languages_container_tool-4.1.0-py3-none-any.whl", hash = "sha256:67be6fe190144983b8e1c9f55e850364895b70d5aa770ee8c22411159bcf3d15"}, - {file = "exasol_script_languages_container_tool-4.1.0.tar.gz", hash = "sha256:cca58883d576251ea4e5fc7b1eeaf134315cc76d91ada6c646f31c2fd8971bc9"}, + {file = "exasol_script_languages_container_tool-4.2.0-py3-none-any.whl", hash = "sha256:7f3f0ae14e089f9faa90d96d3cf655cc8a4ab3ce32ee4b93842b2618421f2d0c"}, + {file = "exasol_script_languages_container_tool-4.2.0.tar.gz", hash = "sha256:95034a7aad3ae90a87e5d479d2ce9c3f4b3944c5d4944dd034abcf57f15b04c2"}, ] markers = {main = "extra == \"slc-tool\""} [package.dependencies] click = ">=8.2.1,<9" exasol-bucketfs = ">=2.1.0,<3" -exasol-integration-test-docker-environment = ">=6.2.0,<7" +exasol-integration-test-docker-environment = ">=6.4.0,<7" exasol-script-languages-package-management = ">=1.0.0,<2" jsonschema = ">=4.23.0,<5" networkx = ">=3.3.0,<4" @@ -2898,6 +2918,30 @@ example = ["cairocffi (>=1.7)", "contextily (>=1.6)", "igraph (>=0.11)", "momepy extra = ["lxml (>=4.6)", "pydot (>=3.0.1)", "pygraphviz (>=1.14)", "sympy (>=1.10)"] test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"] +[[package]] +name = "networkx" +version = "3.6" +description = "Python package for creating and manipulating graphs and networks" +optional = false +python-versions = ">=3.11" +groups = ["main", "dev"] +files = [ + {file = "networkx-3.6-py3-none-any.whl", hash = "sha256:cdb395b105806062473d3be36458d8f1459a4e4b98e236a66c3a48996e07684f"}, + {file = "networkx-3.6.tar.gz", hash = "sha256:285276002ad1f7f7da0f7b42f004bcba70d381e936559166363707fdad3d72ad"}, +] +markers = {main = "python_version >= \"3.12\" and extra == \"slc-tool\"", dev = "python_version >= \"3.12\""} + +[package.extras] +benchmarking = ["asv", "virtualenv"] +default = ["matplotlib (>=3.8)", "numpy (>=1.25)", "pandas (>=2.0)", "scipy (>=1.11.2)"] +developer = ["mypy (>=1.15)", "pre-commit (>=4.1)"] +doc = ["intersphinx-registry", "myst-nb (>=1.1)", "numpydoc (>=1.8.0)", "pillow (>=10)", "pydata-sphinx-theme (>=0.16)", "sphinx (>=8.0)", "sphinx-gallery (>=0.18)", "texext (>=0.6.7)"] +example = ["cairocffi (>=1.7)", "contextily (>=1.6)", "igraph (>=0.11)", "iplotx (>=0.9.0)", "momepy (>=0.7.2)", "osmnx (>=2.0.0)", "scikit-learn (>=1.5)", "seaborn (>=0.13)"] +extra = ["lxml (>=4.6)", "pydot (>=3.0.1)", "pygraphviz (>=1.14)", "sympy (>=1.10)"] +release = ["build (>=0.10)", "changelist (==0.5)", "twine (>=4.0)", "wheel (>=0.40)"] +test = ["pytest (>=7.2)", "pytest-cov (>=4.0)", "pytest-xdist (>=3.0)"] +test-extras = ["pytest-mpl", "pytest-randomly"] + [[package]] name = "networkx" version = "3.6.1" @@ -2909,7 +2953,7 @@ files = [ {file = "networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762"}, {file = "networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509"}, ] -markers = {main = "python_version >= \"3.11\" and extra == \"slc-tool\"", dev = "python_version >= \"3.11\""} +markers = {main = "python_version == \"3.11\" and extra == \"slc-tool\"", dev = "python_version == \"3.11\""} [package.extras] benchmarking = ["asv", "virtualenv"] @@ -4693,7 +4737,7 @@ description = "C version of reader, parser and emitter for ruamel.yaml derived f optional = false python-versions = ">=3.9" groups = ["dev"] -markers = "platform_python_implementation == \"CPython\"" +markers = "python_version < \"3.14\" and platform_python_implementation == \"CPython\"" files = [ {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:88eea8baf72f0ccf232c22124d122a7f26e8a24110a0273d9bcddcb0f7e1fa03"}, {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9b6f7d74d094d1f3a4e157278da97752f16ee230080ae331fcc219056ca54f77"}, @@ -5277,14 +5321,14 @@ testing = ["bs4", "coverage", "pygments", "pytest (>=7.1)", "pytest-cov", "pytes [[package]] name = "sphinx-toolbox" -version = "4.2.0" +version = "4.3.0" description = "Box of handy tools for Sphinx 🧰 📔" optional = false python-versions = ">=3.7" groups = ["dev"] files = [ - {file = "sphinx_toolbox-4.2.0-py3-none-any.whl", hash = "sha256:09f7bef48ee8936ecd890a298b3e1908fda11fe9e6a21fc623495ac34e42c962"}, - {file = "sphinx_toolbox-4.2.0.tar.gz", hash = "sha256:f4dac92fc70e09e9af15d6b8ff003bde1f2aa3573118910f81cf486ced68d4ea"}, + {file = "sphinx_toolbox-4.3.0-py3-none-any.whl", hash = "sha256:edab650523d61d410f13e3f288a8067227bb6682701cf79896f08325a375e1e7"}, + {file = "sphinx_toolbox-4.3.0.tar.gz", hash = "sha256:07ec26176744ee3abe3c1eb4407419e81468f4536f332dcafc4e3240b0d6fae2"}, ] [package.dependencies] @@ -5438,7 +5482,7 @@ description = "Standard library imghdr redistribution. \"dead battery\"." optional = false python-versions = "*" groups = ["dev"] -markers = "python_version == \"3.13\"" +markers = "python_version >= \"3.13\"" files = [ {file = "standard_imghdr-3.10.14-py3-none-any.whl", hash = "sha256:cdf6883163349624dee9a81d2853a20260337c4cd41c04e99c082e01833a08e2"}, {file = "standard_imghdr-3.10.14.tar.gz", hash = "sha256:2598fe2e7c540dbda34b233295e10957ab8dc8ac6f3bd9eaa8d38be167232e52"}, @@ -6053,5 +6097,5 @@ slc-tool = ["exasol-script-languages-container-tool"] [metadata] lock-version = "2.1" -python-versions = ">=3.10,<3.14" -content-hash = "3b2484c5ccc2f018299baef4c957ac6138c527ee8a5a5406868c788ba9d31625" +python-versions = ">=3.10,<3.15" +content-hash = "4bff53ed4df27929483a50afa85ec475b68368c2fd40b94986c50571f9a75d9c" diff --git a/pyproject.toml b/pyproject.toml index 032cf46..ea0f73b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,9 +1,9 @@ [project] name = "exasol-python-extension-common" -version = "0.16.0" +version = "1.0.0" description = "A collection of common utilities for Exasol extensions." authors = [{ name = "Mikhail Beck", email = "mikhail.beck@exasol.com" }] -requires-python = ">=3.10,<3.14" +requires-python = ">=3.10,<3.15" readme = "README.md" license = "MIT" classifiers = [ @@ -12,6 +12,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", ] dependencies = [ "pyexasol>=0.25.0, <3", diff --git a/test/integration/cli/test_language_container_deployer_cli.py b/test/integration/cli/test_language_container_deployer_cli.py index 62705a3..88d9543 100644 --- a/test/integration/cli/test_language_container_deployer_cli.py +++ b/test/integration/cli/test_language_container_deployer_cli.py @@ -11,6 +11,7 @@ import pytest from click.testing import CliRunner +from exasol.python_extension_common.cli import std_options from exasol.python_extension_common.cli.language_container_deployer_cli import ( LanguageContainerDeployerCli, ) @@ -29,6 +30,29 @@ CONTAINER_NAME_ARG = "container_name" +@pytest.fixture(autouse=True) +def _patch_get_cli_arg_for_dash_prefixed_values(monkeypatch): + """ + SaaS database ids are randomly generated and may themselves start with "-" + (e.g. "--dI0m90RUKefql382tsWA"). `get_cli_arg` joins an option and its + value with a space, which click's parser can mistake for a new option + when the value itself looks like one. This is patched here, rather than + in `get_cli_arg` itself, to avoid changing that function's behavior for + its other, non-test callers. See + https://github.com/exasol/python-extension-common/issues/168 + """ + original_get_cli_arg = std_options.get_cli_arg + + def patched_get_cli_arg(std_param, param_value): + if isinstance(param_value, bool) or not str(param_value).startswith("-"): + return original_get_cli_arg(std_param, param_value) + option_name = std_param if isinstance(std_param, str) else std_param.name + option_name = option_name.replace("_", "-") + return f'--{option_name}="{param_value}"' + + monkeypatch.setattr(std_options, "get_cli_arg", patched_get_cli_arg) + + @pytest.fixture(scope="session") def onprem_cli_args( backend_aware_onprem_database, exasol_config, bucketfs_config, language_alias diff --git a/test/unit/cli/test_std_options.py b/test/unit/cli/test_std_options.py index c0908cd..babd474 100644 --- a/test/unit/cli/test_std_options.py +++ b/test/unit/cli/test_std_options.py @@ -1,5 +1,3 @@ -import os - import click import pytest from click.testing import CliRunner @@ -145,7 +143,7 @@ def func(**kwargs): runner.invoke(cmd, args=f"--version {version}", catch_exceptions=False, standalone_mode=False) -def test_hidden_opt_with_envar(): +def test_hidden_opt_with_envar(monkeypatch): """ This test checks the mechanism of providing a value of a confidential parameter via an environment variable. @@ -161,11 +159,8 @@ def func(**kwargs): opt = create_std_option(std_param, type=str, hide_input=True) cmd = click.Command("do_something", params=[opt], callback=func) runner = CliRunner() - os.environ[envar_name] = param_value - try: - runner.invoke(cmd) - finally: - os.environ.pop(envar_name) + monkeypatch.setenv(envar_name, param_value) + runner.invoke(cmd) @pytest.mark.parametrize(