diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 973acf9c..328fd2e0 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -18,7 +18,7 @@ jobs: python-version: "3.8" runs-on: ${{ matrix.os }} - + steps: #---------------------------------------------- diff --git a/linkml_runtime/utils/metamodelcore.py b/linkml_runtime/utils/metamodelcore.py index f46b8d4a..6a184b34 100644 --- a/linkml_runtime/utils/metamodelcore.py +++ b/linkml_runtime/utils/metamodelcore.py @@ -3,9 +3,11 @@ import re from dataclasses import field from decimal import Decimal +import sys from typing import Union, Optional, Tuple from urllib.parse import urlparse +import isodate from rdflib import Literal, BNode, URIRef from rdflib.namespace import is_ncname from rdflib.term import Identifier as rdflib_Identifier @@ -230,13 +232,10 @@ def __new__(cls, value: Union[str, datetime.time, datetime.datetime, Literal]) - if not isinstance(value, datetime.time): value = datetime.time.fromisoformat(value) return datetime.time.fromisoformat(str(value)).isoformat() - except TypeError as e: - pass - except ValueError as e: - pass - if not is_strict(): - return str(value) - raise e + except (TypeError, ValueError) as e: + if is_strict(): + raise e + return str(value) @classmethod def is_valid(cls, value: Union[str, datetime.time, datetime.datetime, Literal]) -> bool: @@ -260,15 +259,15 @@ def __new__(cls, value: Union[str, datetime.date, Literal]) -> str: value = value.value try: if not isinstance(value, datetime.date): - value = datetime.date.fromisoformat(str(value)) + if sys.version_info >= (3, 11): + value = datetime.date.fromisoformat(str(value)) + else: + value = isodate.parse_date(value) return value.isoformat() - except TypeError as e: - pass - except ValueError as e: - pass - if not is_strict(): - return str(value) - raise e + except (TypeError, ValueError) as e: + if is_strict(): + raise e + return str(value) @classmethod def is_valid(cls, value: Union[str, datetime.date, Literal]) -> bool: @@ -279,7 +278,10 @@ def is_valid(cls, value: Union[str, datetime.date, Literal]) -> bool: if not re.match(r'^\d{4}-\d{2}-\d{2}$', value): return False try: - datetime.date.fromisoformat(str(value)) + if sys.version_info >= (3, 11): + datetime.date.fromisoformat(str(value)) + else: + value = isodate.parse_date(value) except ValueError: return False return True @@ -294,15 +296,18 @@ def __new__(cls, value: Union[str, datetime.datetime, Literal]) -> str: value = value.value try: if not isinstance(value, datetime.datetime): - value = datetime.datetime.fromisoformat(value) # Note that this handles non 'T' format as well + if sys.version_info >= (3, 11): + value = datetime.datetime.fromisoformat(value) # Note that this handles non 'T' format as well + else: + if "T" in str(value): + value = isodate.parse_datetime(value) + else: + value = isodate.parse_datetime("T".join(value.strip().split(' ', 1))) return value.isoformat() - except TypeError as e: - pass - except ValueError as e: - pass - if not is_strict(): - return str(value) - raise e + except (TypeError, ValueError) as e: + if is_strict(): + raise e + return str(value) @classmethod def is_valid(cls, value: Union[str, datetime.datetime, Literal]) -> bool: @@ -311,8 +316,16 @@ def is_valid(cls, value: Union[str, datetime.datetime, Literal]) -> bool: if isinstance(value, datetime.datetime): value = value.isoformat() try: - datetime.datetime.fromisoformat(value) - except ValueError: + if sys.version_info >= (3, 11): + datetime.datetime.fromisoformat(value) + else: + if "T" in str(value): + isodate.parse_datetime(value) + elif " " in value.strip(): + isodate.parse_datetime("T".join(value.strip().split(' ', 1))) + else: + datetime.datetime.fromisoformat(value) + except (ValueError, TypeError): return False return True diff --git a/tests/test_utils/test_metamodelcore.py b/tests/test_utils/test_metamodelcore.py index 1bc26739..f70b88d9 100644 --- a/tests/test_utils/test_metamodelcore.py +++ b/tests/test_utils/test_metamodelcore.py @@ -153,6 +153,7 @@ def test_time(self): XSDDate(datetime.datetime.now()) self.assertFalse(XSDTime.is_valid('Jan 12, 2019')) self.assertFalse(XSDTime.is_valid(datetime.datetime.now())) + self.assertFalse(XSDTime.is_valid("2019-07-06T17:22:39Z")) self.assertTrue(XSDTime.is_valid(v)) def test_date(self): @@ -168,6 +169,9 @@ def test_date(self): XSDDate('Jan 12, 2019') with self.assertRaises(ValueError): XSDDate(datetime.datetime.now()) + with self.assertRaises(ValueError): + XSDDate("2019-07-06T17:22:39Z") + lax() bv = XSDDate('Jan 12, 2019') self.assertEqual('Jan 12, 2019', bv) @@ -188,6 +192,7 @@ def test_datetime(self): vstr = str(Literal(v).value) self.assertEqual('2019-07-06 17:22:39.007300', vstr) # Note that this has no 'T' self.assertEqual('2019-07-06T17:22:39.007300', XSDDateTime(vstr)) + self.assertEqual('2019-07-06T17:22:39+00:00', XSDDateTime("2019-07-06T17:22:39Z")) with self.assertRaises(ValueError): XSDDateTime('Jan 12, 2019') lax() diff --git a/tox.ini b/tox.ini index d33deaa0..fa4cf1f4 100644 --- a/tox.ini +++ b/tox.ini @@ -1,12 +1,10 @@ [tox] -envlist = py37, py38, py39, py310 -isolated_build = true -skipsdist = true - +envlist = py39, py310, py311, py312 [testenv] -whitelist_externals = poetry -commands= - poetry install -v - poetry run python -m unittest - poetry run comparefiles --help +skip_install = true +allowlist_externals = poetry +commands_pre = + poetry install +commands = + poetry run pytest