From ff5d716c68b78c23515297dfb9a6d947f5b23a55 Mon Sep 17 00:00:00 2001 From: Luke Helt Date: Fri, 5 Jan 2024 12:02:59 -0500 Subject: [PATCH 01/12] try --- requirements.txt | 3 ++- setup.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index c43412b..f754c25 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ appdirs==1.4.4 +python-dotenv==1.0.0 fire==0.4.0 numpy==1.21.3 -pydantic[dotenv]==1.8.2 +pydantic==2.4 python-dateutil==2.8.2 requests==2.31.0 diff --git a/setup.py b/setup.py index d603e1f..c3653aa 100644 --- a/setup.py +++ b/setup.py @@ -5,9 +5,10 @@ requirements = [ "appdirs", + "python-dotenv", "fire", "numpy", - "pydantic[dotenv]<2", + "pydantic", "python-dateutil", "requests", ] From 3b71eb90e829694f4ecefb288fa3e011e68742dd Mon Sep 17 00:00:00 2001 From: Luke Helt Date: Fri, 5 Jan 2024 12:11:45 -0500 Subject: [PATCH 02/12] bump dotenv down --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f754c25..c37204c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ appdirs==1.4.4 -python-dotenv==1.0.0 +python-dotenv==0.21.1 fire==0.4.0 numpy==1.21.3 pydantic==2.4 From c21f34a1d7300438359b01321e93bd6168361c16 Mon Sep 17 00:00:00 2001 From: Luke Helt Date: Fri, 5 Jan 2024 12:13:12 -0500 Subject: [PATCH 03/12] changes from bump-pydantic --- xcc/settings.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/xcc/settings.py b/xcc/settings.py index 15905d8..e6d1964 100644 --- a/xcc/settings.py +++ b/xcc/settings.py @@ -8,7 +8,7 @@ from appdirs import user_config_dir from dotenv import dotenv_values, set_key, unset_key -from pydantic import BaseSettings +from pydantic_settings import BaseSettings, SettingsConfigDict # Matches when string contains chars outside Base64URL set # https://base64.guru/standards/base64url @@ -108,11 +108,7 @@ class Settings(BaseSettings): TLS: bool = True """Whether to use HTTPS for requests to the Xanadu Cloud.""" - - class Config: # pylint: disable=missing-class-docstring - case_sensitive = True - env_file = get_path_to_env_file() - env_prefix = get_name_of_env_var() + model_config = SettingsConfigDict(case_sensitive=True, env_file=get_path_to_env_file(), env_prefix=get_name_of_env_var()) def save(self) -> None: """Saves the current settings to the .env file.""" From ee5276306e199a2e0e0199d2b7c3f973255f1d2e Mon Sep 17 00:00:00 2001 From: Luke Helt Date: Fri, 5 Jan 2024 12:17:35 -0500 Subject: [PATCH 04/12] fmt --- xcc/settings.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/xcc/settings.py b/xcc/settings.py index e6d1964..f97cf45 100644 --- a/xcc/settings.py +++ b/xcc/settings.py @@ -108,7 +108,9 @@ class Settings(BaseSettings): TLS: bool = True """Whether to use HTTPS for requests to the Xanadu Cloud.""" - model_config = SettingsConfigDict(case_sensitive=True, env_file=get_path_to_env_file(), env_prefix=get_name_of_env_var()) + model_config = SettingsConfigDict( + case_sensitive=True, env_file=get_path_to_env_file(), env_prefix=get_name_of_env_var() + ) def save(self) -> None: """Saves the current settings to the .env file.""" From f427b864fdd33d87e8a375e4adc2638e717b0231 Mon Sep 17 00:00:00 2001 From: Luke Helt Date: Fri, 5 Jan 2024 12:20:14 -0500 Subject: [PATCH 05/12] pydantic-settings --- requirements.txt | 1 + setup.py | 1 + 2 files changed, 2 insertions(+) diff --git a/requirements.txt b/requirements.txt index c37204c..a7aea15 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,5 +3,6 @@ python-dotenv==0.21.1 fire==0.4.0 numpy==1.21.3 pydantic==2.4 +pydantic-settings==2.1.0 python-dateutil==2.8.2 requests==2.31.0 diff --git a/setup.py b/setup.py index c3653aa..fc87090 100644 --- a/setup.py +++ b/setup.py @@ -9,6 +9,7 @@ "fire", "numpy", "pydantic", + "pydantic-settings", "python-dateutil", "requests", ] From 337e58222d748186daffc3b0438424ef5a7e5496 Mon Sep 17 00:00:00 2001 From: Luke Helt Date: Fri, 5 Jan 2024 12:22:56 -0500 Subject: [PATCH 06/12] investigate --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a7aea15..d39f909 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,6 @@ python-dotenv==0.21.1 fire==0.4.0 numpy==1.21.3 pydantic==2.4 -pydantic-settings==2.1.0 +pydantic-settings>=0.2.5 python-dateutil==2.8.2 requests==2.31.0 From 3df35fe2846fe9a35f79f15752bd3101d78f1e79 Mon Sep 17 00:00:00 2001 From: Luke Helt Date: Fri, 5 Jan 2024 12:43:43 -0500 Subject: [PATCH 07/12] simplify --- requirements.txt | 1 - setup.py | 1 - xcc/settings.py | 10 ++++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index d39f909..c37204c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,5 @@ python-dotenv==0.21.1 fire==0.4.0 numpy==1.21.3 pydantic==2.4 -pydantic-settings>=0.2.5 python-dateutil==2.8.2 requests==2.31.0 diff --git a/setup.py b/setup.py index fc87090..c3653aa 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,6 @@ "fire", "numpy", "pydantic", - "pydantic-settings", "python-dateutil", "requests", ] diff --git a/xcc/settings.py b/xcc/settings.py index f97cf45..149902a 100644 --- a/xcc/settings.py +++ b/xcc/settings.py @@ -8,7 +8,7 @@ from appdirs import user_config_dir from dotenv import dotenv_values, set_key, unset_key -from pydantic_settings import BaseSettings, SettingsConfigDict +from pydantic.v1 import BaseSettings # Matches when string contains chars outside Base64URL set # https://base64.guru/standards/base64url @@ -108,9 +108,11 @@ class Settings(BaseSettings): TLS: bool = True """Whether to use HTTPS for requests to the Xanadu Cloud.""" - model_config = SettingsConfigDict( - case_sensitive=True, env_file=get_path_to_env_file(), env_prefix=get_name_of_env_var() - ) + + class Config: # pylint: disable=missing-class-docstring + case_sensitive = True + env_file = get_path_to_env_file() + env_prefix = get_name_of_env_var() def save(self) -> None: """Saves the current settings to the .env file.""" From c655d847af6ed550807bc479daf1359b0ffcb8cf Mon Sep 17 00:00:00 2001 From: Luke Helt Date: Fri, 5 Jan 2024 12:54:22 -0500 Subject: [PATCH 08/12] v1 --- xcc/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xcc/commands.py b/xcc/commands.py index 88406b2..78f8ad3 100644 --- a/xcc/commands.py +++ b/xcc/commands.py @@ -12,7 +12,7 @@ import numpy as np from fire.core import FireError from fire.formatting import Error -from pydantic import ValidationError +from pydantic.v1 import ValidationError from ._version import __version__ from .connection import Connection From 6f2c6377c0c12f0ebfdca8a01a982079d0c3c11a Mon Sep 17 00:00:00 2001 From: Luke Helt Date: Fri, 5 Jan 2024 13:03:12 -0500 Subject: [PATCH 09/12] CHANGELOG --- .github/CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index e9dd6c8..6c6b851 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -1,3 +1,15 @@ +## Release 0.4.1 (development release) + +### Bug fixes + +* Updates required `pydantic` version from 1.8.2 to 2.4, while still continuing to use "v1" features. [#46](https://github.com/XanaduAI/xanadu-cloud-client/pull/46) + +### Contributors + +This release contains contributions from (in alphabetical order): + +[Luke Helt](https://github.com/heltluke). + ## Release 0.4.0 (development release) ### Contributors From 02c58edd597a3813d721627ae531134cfd4d9d63 Mon Sep 17 00:00:00 2001 From: Mikhail Andrenkov Date: Fri, 5 Jan 2024 15:18:00 -0500 Subject: [PATCH 10/12] Better Pydantic v2 support (#47) * Add `pydantic-settings` as a new dependency * Replace nested `Config` class with `SettingsConfigDict` * Import Pydantic v2 `ValidationError` * Fix Pydantic v2 warnings by using `model_dump()` * Update tests to reflect Pydantic v2 changes * Replace Python 3.7 support with Python 3.11 * Write Python versions as strings in GHA workflows * Drop CI support for Python 3.11 --- .github/workflows/format.yml | 2 +- .github/workflows/tests.yml | 2 +- .github/workflows/upload.yml | 2 +- requirements.txt | 3 ++- setup.py | 4 ++-- tests/conftest.py | 4 ++-- tests/test_commands.py | 5 ++++- tests/test_settings.py | 8 ++++---- xcc/commands.py | 16 ++++++++-------- xcc/settings.py | 25 +++++++++++++------------ 10 files changed, 38 insertions(+), 33 deletions(-) diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 7923238..2b29f36 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -18,7 +18,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v2 with: - python-version: 3.7 + python-version: "3.8" - uses: actions/checkout@v2 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 78e3b52..d577477 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: os: [macos-latest, ubuntu-latest] - python-version: [3.7, 3.8, 3.9] + python-version: ["3.8", "3.9", "3.10"] steps: - name: Cancel Previous Runs diff --git a/.github/workflows/upload.yml b/.github/workflows/upload.yml index 49a9aca..d9c9504 100644 --- a/.github/workflows/upload.yml +++ b/.github/workflows/upload.yml @@ -13,7 +13,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v2 with: - python-version: 3.7 + python-version: "3.8" - name: Install dependencies run: | diff --git a/requirements.txt b/requirements.txt index c37204c..f78ce53 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,8 @@ appdirs==1.4.4 -python-dotenv==0.21.1 fire==0.4.0 numpy==1.21.3 pydantic==2.4 +pydantic-settings==2.1.0 python-dateutil==2.8.2 +python-dotenv==0.21.1 requests==2.31.0 diff --git a/setup.py b/setup.py index c3653aa..9b6a004 100644 --- a/setup.py +++ b/setup.py @@ -5,11 +5,12 @@ requirements = [ "appdirs", - "python-dotenv", "fire", "numpy", "pydantic", + "pydantic-settings", "python-dateutil", + "python-dotenv", "requests", ] @@ -42,7 +43,6 @@ "Operating System :: Microsoft :: Windows", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", diff --git a/tests/conftest.py b/tests/conftest.py index 95cd8df..7e5d91c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -19,14 +19,14 @@ def connection() -> xcc.Connection: def settings(monkeypatch) -> Iterator[xcc.Settings]: """Returns a :class:`xcc.Settings` instance configured to use a mock .env file.""" with NamedTemporaryFile("w") as env_file: - monkeypatch.setattr("xcc.Settings.Config.env_file", env_file.name) + monkeypatch.setitem(xcc.Settings.model_config, "env_file", env_file.name) settings_ = xcc.Settings(REFRESH_TOKEN="j.w.t", HOST="example.com", PORT=80, TLS=False) # Saving ensures that new Settings instances are loaded with the same values. settings_.save() # Environment variables take precedence over fields in the .env file. - for env_var in map(xcc.settings.get_name_of_env_var, settings_.dict()): + for env_var in map(xcc.settings.get_name_of_env_var, settings_.model_dump()): monkeypatch.delenv(env_var, raising=False) yield settings_ diff --git a/tests/test_commands.py b/tests/test_commands.py index d2b429f..f20f758 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -179,7 +179,10 @@ def test_invalid_name(self): def test_invalid_value(self): """Tests that a ValueError is raised when the value of a setting is invalid.""" - match = r"Failed to update PORT setting: value is not a valid integer" + match = ( + r"Failed to update PORT setting: " + r"Input should be a valid integer, unable to parse string as an integer" + ) with pytest.raises(ValueError, match=match): xcc.commands.set_setting(name="PORT", value="string") diff --git a/tests/test_settings.py b/tests/test_settings.py index 52f54ba..118033b 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -15,7 +15,7 @@ def env_file(monkeypatch): """Returns a mock .env file which :class:`xcc.Settings` is configured to use.""" with NamedTemporaryFile("w") as env_file: - monkeypatch.setattr("xcc.settings.Settings.Config.env_file", env_file.name) + monkeypatch.setitem(xcc.Settings.model_config, "env_file", env_file.name) yield env_file @@ -77,7 +77,7 @@ def test_save_bad_base64_url(self, settings): settings.save() # Check that the .env file was not modified since there was a "\n" in the refresh token. - assert dotenv_values(xcc.Settings.Config.env_file) == { + assert dotenv_values(settings.model_config["env_file"]) == { "XANADU_CLOUD_REFRESH_TOKEN": "j.w.t", "XANADU_CLOUD_HOST": "example.com", "XANADU_CLOUD_PORT": "80", @@ -86,7 +86,7 @@ def test_save_bad_base64_url(self, settings): def test_save_multiple_times(self, settings): """Tests that settings can be saved to a .env file multiple times.""" - path_to_env_file = xcc.Settings.Config.env_file + path_to_env_file = settings.model_config["env_file"] settings.REFRESH_TOKEN = None settings.save() @@ -108,7 +108,7 @@ def test_save_to_nonexistent_directory(self, monkeypatch): """Tests that settings can be saved to a .env file in a nonexistent directory.""" with TemporaryDirectory() as env_dir: env_file = os.path.join(env_dir, "foo", "bar", ".env") - monkeypatch.setattr("xcc.settings.Settings.Config.env_file", env_file) + monkeypatch.setitem(xcc.Settings.model_config, "env_file", env_file) xcc.Settings().save() assert os.path.exists(env_file) is True diff --git a/xcc/commands.py b/xcc/commands.py index 78f8ad3..9a01916 100644 --- a/xcc/commands.py +++ b/xcc/commands.py @@ -12,7 +12,7 @@ import numpy as np from fire.core import FireError from fire.formatting import Error -from pydantic.v1 import ValidationError +from pydantic import ValidationError from ._version import __version__ from .connection import Connection @@ -87,7 +87,7 @@ def list_settings() -> Mapping[str, Any]: Returns: Mapping[str, Any]: Mapping from setting names to values. """ - return Settings().dict() + return Settings().model_dump() @beautify @@ -108,14 +108,14 @@ def set_setting(name: str, value: Union[str, int, bool]) -> str: key, _ = _resolve_setting(name) try: - settings = Settings(**{key: value}) + settings = Settings.model_validate({key: value}) settings.save() except ValidationError as exc: err = exc.errors()[0].get("msg", "invalid value") raise ValueError(f"Failed to update {key} setting: {err}") from exc # Using repr() ensures that strings are quoted. - val = repr(settings.dict()[key]) + val = repr(settings.model_dump()[key]) return f"Successfully updated {key} setting to {val}." @@ -133,11 +133,11 @@ def _resolve_setting(name: str) -> Tuple[str, Any]: """ key = name.upper() - settings = Settings() - if key not in settings.dict(): - raise ValueError(f"The setting name '{name}' must be one of {list(settings.dict())}.") + settings_dict = Settings().model_dump() + if key not in settings_dict: + raise ValueError(f"The setting name '{name}' must be one of {list(settings_dict)}.") - return key, settings.dict()[key] + return key, settings_dict[key] # Device CLI diff --git a/xcc/settings.py b/xcc/settings.py index 149902a..6eed19e 100644 --- a/xcc/settings.py +++ b/xcc/settings.py @@ -8,7 +8,7 @@ from appdirs import user_config_dir from dotenv import dotenv_values, set_key, unset_key -from pydantic.v1 import BaseSettings +from pydantic_settings import BaseSettings, SettingsConfigDict # Matches when string contains chars outside Base64URL set # https://base64.guru/standards/base64url @@ -69,7 +69,7 @@ class Settings(BaseSettings): >>> import xcc >>> settings = xcc.Settings() >>> settings - REFRESH_TOKEN=None ACCESS_TOKEN=None HOST='platform.xanadu.ai' PORT=443 TLS=True + Settings(REFRESH_TOKEN=None, ACCESS_TOKEN=None, HOST'platform.xanadu.ai', PORT=443, TLS=True) Now, individual options can be accessed or assigned through their corresponding attribute: @@ -84,9 +84,9 @@ class Settings(BaseSettings): Several aggregate representations of options are also available, such as - >>> settings.dict() + >>> settings.model_dump() {'REFRESH_TOKEN': None, 'ACCESS_TOKEN': None, ..., 'TLS': True} - >>> settings.json() + >>> settings.model_dump_json() '{"REFRESH_TOKEN": null, "ACCESS_TOKEN": null, ..., "TLS": true}' Finally, saving a configuration can be done by invoking :meth:`Settings.save`: @@ -109,25 +109,26 @@ class Settings(BaseSettings): TLS: bool = True """Whether to use HTTPS for requests to the Xanadu Cloud.""" - class Config: # pylint: disable=missing-class-docstring - case_sensitive = True - env_file = get_path_to_env_file() - env_prefix = get_name_of_env_var() + model_config = SettingsConfigDict( + case_sensitive=True, + env_file=get_path_to_env_file(), + env_prefix=get_name_of_env_var(), + ) def save(self) -> None: """Saves the current settings to the .env file.""" - env_file = Settings.Config.env_file + env_file = self.model_config["env_file"] env_dir = os.path.dirname(env_file) os.makedirs(env_dir, exist_ok=True) saved = dotenv_values(dotenv_path=env_file) - # must be done first as dict is not ordered - for key, val in self.dict().items(): + # Must be done first as the dictionary is not ordered. + for key, val in self.model_dump().items(): _check_for_invalid_values(key, val) - for key, val in self.dict().items(): + for key, val in self.model_dump().items(): field = get_name_of_env_var(key) # Remove keys that are assigned to None. From 9074dcb670cee7e67e99e2219691f3e29dcd0365 Mon Sep 17 00:00:00 2001 From: Luke Helt Date: Fri, 5 Jan 2024 15:44:11 -0500 Subject: [PATCH 11/12] suggestions from code review --- .github/CHANGELOG.md | 12 +++--------- setup.py | 2 +- xcc/_version.py | 2 +- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 6c6b851..3d31a4c 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -1,20 +1,14 @@ -## Release 0.4.1 (development release) +## Release 0.3.2 (development release) ### Bug fixes -* Updates required `pydantic` version from 1.8.2 to 2.4, while still continuing to use "v1" features. [#46](https://github.com/XanaduAI/xanadu-cloud-client/pull/46) +* Sets lower bound on compatible `pydantic` versions to v2.0.0. [(#46)](https://github.com/XanaduAI/xanadu-cloud-client/pull/46) ### Contributors This release contains contributions from (in alphabetical order): -[Luke Helt](https://github.com/heltluke). - -## Release 0.4.0 (development release) - -### Contributors - -This release contains contributions from (in alphabetical order): +[Mikhail Andrenkov](https://github.com/Mandrenkov), [Luke Helt](https://github.com/heltluke). ## Release 0.3.1 (current release) diff --git a/setup.py b/setup.py index 9b6a004..10b106a 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ "appdirs", "fire", "numpy", - "pydantic", + "pydantic>=2", "pydantic-settings", "python-dateutil", "python-dotenv", diff --git a/xcc/_version.py b/xcc/_version.py index 28cd148..0519186 100644 --- a/xcc/_version.py +++ b/xcc/_version.py @@ -3,4 +3,4 @@ See https://semver.org/. """ -__version__ = "0.4.0-dev" +__version__ = "0.3.2" From a614f308f769c6074b1ae140026a423f5362df19 Mon Sep 17 00:00:00 2001 From: Luke Helt <31250931+heltluke@users.noreply.github.com> Date: Fri, 5 Jan 2024 15:51:02 -0500 Subject: [PATCH 12/12] current release Co-authored-by: Mikhail Andrenkov --- .github/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 3d31a4c..e5d1c37 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -1,4 +1,4 @@ -## Release 0.3.2 (development release) +## Release 0.3.2 (current release) ### Bug fixes @@ -10,7 +10,7 @@ This release contains contributions from (in alphabetical order): [Mikhail Andrenkov](https://github.com/Mandrenkov), [Luke Helt](https://github.com/heltluke). -## Release 0.3.1 (current release) +## Release 0.3.1 ### Improvements