From e8c1fb63abe4d94ded52d373c81451aa7258509f Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Thu, 14 Nov 2024 12:42:15 +0000 Subject: [PATCH 1/3] refactor KmlDateTime class and add get_all_attrs utility function --- fastkml/times.py | 8 +++----- fastkml/utils.py | 40 +++++++++++++++++++++++++++++----------- tox.ini | 1 - 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/fastkml/times.py b/fastkml/times.py index 093902df..a2f35b5f 100644 --- a/fastkml/times.py +++ b/fastkml/times.py @@ -45,7 +45,7 @@ # regular expression to parse a gYearMonth string # year and month may be separated by an optional dash -# year is always 4 digits, month is always 2 digits +# year is always 4 digits, month, day is always 2 digits year_month_day = re.compile( r"^(?P\d{4})(?:-)?(?P\d{2})?(?:-)?(?P\d{2})?$", ) @@ -205,10 +205,8 @@ def parse(cls, datestr: str) -> Optional["KmlDateTime"]: resolution = DateTimeResolution.year_month if year_month_day_match.group("month") is None: resolution = DateTimeResolution.year - elif len(datestr) > 10: # noqa: PLR2004 - dt = arrow.get(datestr).datetime - resolution = DateTimeResolution.datetime - return cls(dt, resolution) if dt else None + return cls(dt, resolution) + return cls(arrow.get(datestr).datetime, DateTimeResolution.datetime) @classmethod def get_ns_id(cls) -> str: diff --git a/fastkml/utils.py b/fastkml/utils.py index 87cc7d38..9f3bc0f7 100644 --- a/fastkml/utils.py +++ b/fastkml/utils.py @@ -30,6 +30,32 @@ def has_attribute_values(obj: object, **kwargs: Any) -> bool: return False +def get_all_attrs(obj: object) -> Generator[object, None, None]: + """ + Get all attributes of an object. + + Args: + ---- + obj: The object to get attributes from. + + Returns: + ------- + An iterable of all attributes of the object or, if the attribute itself is + iterable, iterate over the attribute values. + + """ + try: + attrs = (attr for attr in obj.__dict__ if not attr.startswith("_")) + except AttributeError: + return + for attr_name in attrs: + attr = getattr(obj, attr_name) + try: + yield from attr + except TypeError: + yield attr + + def find_all( obj: object, *, @@ -55,17 +81,9 @@ def find_all( **kwargs, ): yield obj - try: - attrs = (attr for attr in obj.__dict__ if not attr.startswith("_")) - except AttributeError: - return - for attr_name in attrs: - attr = getattr(obj, attr_name) - try: - for item in attr: - yield from find_all(item, of_type=of_type, **kwargs) - except TypeError: - yield from find_all(attr, of_type=of_type, **kwargs) + + for attr in get_all_attrs(obj): + yield from find_all(attr, of_type=of_type, **kwargs) def find( diff --git a/tox.ini b/tox.ini index 3456d188..4fb8dc1a 100644 --- a/tox.ini +++ b/tox.ini @@ -9,7 +9,6 @@ per-file-ignores = examples/*.py: DALL fastkml/gx.py: LIT002 fastkml/views.py: LIT002 - fastkml/utils.py: CCR001 fastkml/registry.py: E704 docs/conf.py: E402 enable-extensions=G From e9e4915513dc86a20231906bce57bcebd9739fe7 Mon Sep 17 00:00:00 2001 From: Christian Ledermann Date: Thu, 14 Nov 2024 13:32:51 +0000 Subject: [PATCH 2/3] refactor datetime_subelement_kwarg to use try-except for error handling and improve test cases for KmlDateTime parsing --- fastkml/helpers.py | 19 ++++++++++--------- tests/times_test.py | 12 ++++++++++-- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/fastkml/helpers.py b/fastkml/helpers.py index 1b3d582e..85a888e8 100644 --- a/fastkml/helpers.py +++ b/fastkml/helpers.py @@ -1100,15 +1100,16 @@ def datetime_subelement_kwarg( return {} node_text = node.text.strip() if node.text else "" if node_text: - if kdt := cls.parse(node_text): # type: ignore[attr-defined] - return {kwarg: kdt} - handle_error( - error=ValueError(f"Invalid DateTime value: {node_text}"), - strict=strict, - element=element, - node=node, - expected="DateTime", - ) + try: + return {kwarg: cls.parse(node_text)} # type: ignore[attr-defined] + except ValueError as exc: + handle_error( + error=exc, + strict=strict, + element=element, + node=node, + expected="DateTime", + ) return {} diff --git a/tests/times_test.py b/tests/times_test.py index 3b52e9ff..2b96978a 100644 --- a/tests/times_test.py +++ b/tests/times_test.py @@ -200,11 +200,19 @@ def test_parse_datetime_no_tz(self) -> None: assert dt.dt == datetime.datetime(1997, 7, 16, 7, 30, 15, tzinfo=tzutc()) def test_parse_datetime_empty(self) -> None: - assert KmlDateTime.parse("") is None + with pytest.raises( + ValueError, + match="^Could not match input '' to any of the following formats:", + ): + KmlDateTime.parse("") def test_parse_year_month_5(self) -> None: """Test that a single digit month is invalid.""" - assert KmlDateTime.parse("19973") is None + with pytest.raises( + ValueError, + match="^Could not match input '19973' to any of the following formats:", + ): + KmlDateTime.parse("19973") class TestStdLibrary(StdLibrary): From 9fe1ae9733a873d7bbe59ecf118b50a6acb08dbf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 22:29:39 +0000 Subject: [PATCH 3/3] Bump codecov/codecov-action from 4 to 5 Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4 to 5. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v4...v5) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/run-all-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-all-tests.yml b/.github/workflows/run-all-tests.yml index 4e47acd1..e6ca054a 100644 --- a/.github/workflows/run-all-tests.yml +++ b/.github/workflows/run-all-tests.yml @@ -46,7 +46,7 @@ jobs: pytest tests --cov=fastkml --cov=tests --cov-fail-under=95 --cov-report=xml - name: "Upload coverage to Codecov" if: ${{ matrix.python-version==3.11 }} - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: fail_ci_if_error: true verbose: true