diff --git a/.appveyor.yml b/.appveyor.yml index 9361fbc6..39ba33bf 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -18,6 +18,7 @@ install: - set PATH=C:\Python%PYTHON_MAJOR%%PYTHON_MINOR%\Scripts;%PATH% # Install system dependencies - choco install make + - set POETRY_VERSION=1.8.5 - curl -sSL https://install.python-poetry.org | python - - set PATH=%USERPROFILE%\AppData\Roaming\Python\Scripts;%PATH% - make doctor diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d574289d..72b6d88a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -19,7 +19,7 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install Poetry - run: curl -sSL https://install.python-poetry.org | python3 - + run: curl -sSL https://install.python-poetry.org | POETRY_VERSION=1.8.5 python3 - - name: Check dependencies run: make doctor diff --git a/.tool-versions b/.tool-versions index ec7f1a64..dce3c9ce 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ python 3.11.5 -poetry 1.8.2 +poetry 1.8.5 diff --git a/CHANGELOG.md b/CHANGELOG.md index d80d0f0b..e8fa2eee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Release Notes +## 2.3.1 (2025-02-11) + +- Removed unnecessary `eval()` call to map annotations to types. + ## 2.3 (2024-10-30) - Added support for the [JSON5](https://json5.org) file format. diff --git a/datafiles/converters/__init__.py b/datafiles/converters/__init__.py index a5a93658..bb95831c 100644 --- a/datafiles/converters/__init__.py +++ b/datafiles/converters/__init__.py @@ -1,5 +1,4 @@ import dataclasses -import inspect import types from enum import Enum from inspect import isclass @@ -38,18 +37,6 @@ def register(cls: Union[type, str], converter: type): register(dict, Dictionary) -def resolve(annotation, obj=None): - if isinstance(annotation, str): - log.debug(f"Attempting to eval {annotation!r} using {obj}") - annotation = annotation.replace("List", "list").replace("Dict", "list") - namespace = inspect.getmodule(obj).__dict__ if obj else None - try: - return eval(annotation, namespace) # pylint: disable=eval-used - except NameError as e: - log.warn(f"Unable to eval: {e}") - return annotation - - @cached def map_type(cls, *, name: str = "", item_cls: Optional[type] = None): """Infer the converter type from a dataclass, type, or annotation.""" @@ -66,7 +53,7 @@ def map_type(cls, *, name: str = "", item_cls: Optional[type] = None): if dataclasses.is_dataclass(cls): converters = {} for field in dataclasses.fields(cls): - converters[field.name] = map_type(resolve(field.type), name=field.name) # type: ignore + converters[field.name] = map_type(field.type, name=field.name) # type: ignore converter = Dataclass.of_mappings(cls, converters) log.debug(f"Mapped {cls!r} to new converter: {converter}") return converter @@ -79,8 +66,6 @@ def map_type(cls, *, name: str = "", item_cls: Optional[type] = None): converter = converter.as_optional() return converter - cls = resolve(cls) - if hasattr(cls, "__origin__"): converter = None diff --git a/datafiles/mapper.py b/datafiles/mapper.py index 401d928b..9d55ee9d 100644 --- a/datafiles/mapper.py +++ b/datafiles/mapper.py @@ -13,7 +13,7 @@ from cached_property import cached_property from . import config, formats, hooks -from .converters import Converter, List, map_type, resolve +from .converters import Converter, List, map_type from .types import Missing, Trilean from .utils import display, get_default_field_value, recursive_update, write @@ -301,7 +301,7 @@ def create_mapper(obj, root=None) -> Mapper: for field in [field for field in dataclasses.fields(obj) if field.init]: self_name = f"self.{field.name}" if pattern is None or self_name not in pattern: - attrs[field.name] = map_type(resolve(field.type, obj), name=field.name) # type: ignore + attrs[field.name] = map_type(field.type, name=field.name) # type: ignore return Mapper( obj, diff --git a/datafiles/tests/test_manager.py b/datafiles/tests/test_manager.py index 837f0d50..afec04ee 100644 --- a/datafiles/tests/test_manager.py +++ b/datafiles/tests/test_manager.py @@ -1,6 +1,5 @@ # pylint: disable=unused-variable,unused-argument -import os import shutil from dataclasses import dataclass from pathlib import Path @@ -42,11 +41,6 @@ def manager(files: Path): model = create_model(MyClass, pattern="files/{self.foo}.yml") return Manager(model) - @pytest.fixture - def manager_at_home(): - model = create_model(Nested, pattern="~/.{self.name}.json") - return Manager(model) - @pytest.fixture def manager_with_files(files: Path): files.mkdir(exist_ok=True) @@ -145,11 +139,6 @@ def when_no_files_exist(expect, manager: Manager): items = list(manager.all()) expect(items) == [] - def with_home_directory(expect, manager_at_home: Manager): - items = list(manager_at_home.all()) - if "CI" not in os.environ: - expect(len(items)) > 0 - def describe_filter(): @patch("datafiles.mapper.Mapper.exists", False) def when_no_files_exist(expect, manager: Manager): diff --git a/pyproject.toml b/pyproject.toml index 6523d86c..54be99c5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [tool.poetry] name = "datafiles" -version = "2.3" +version = "2.3.1" description = "File-based ORM for dataclasses." license = "MIT" diff --git a/tests/test_custom_converters_future.py b/tests/test_custom_converters_future.py index 12b63f74..598c2ed3 100644 --- a/tests/test_custom_converters_future.py +++ b/tests/test_custom_converters_future.py @@ -9,6 +9,7 @@ from datafiles import datafile +@pytest.mark.xfail(reason="https://github.com/jacebrowning/datafiles/issues/131") def test_optional_type(expect): @datafile("../tmp/sample.yml") class MyObject: