Skip to content

Commit c8ddf28

Browse files
authored
Fix loading datetimes with - instead of T (#41)
1 parent a153df1 commit c8ddf28

File tree

3 files changed

+74
-7
lines changed

3 files changed

+74
-7
lines changed

onvif/client.py

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from .managers import NotificationManager, PullPointManager
2727
from .settings import DEFAULT_SETTINGS
2828
from .transport import ASYNC_TRANSPORT
29+
from .types import FastDateTime
2930
from .util import create_no_verify_ssl_context, normalize_url, path_isfile, utcnow
3031
from .wrappers import retry_connection_error # noqa: F401
3132

@@ -80,10 +81,44 @@ def apply(self, envelope, headers):
8081
return result
8182

8283

83-
@lru_cache(maxsize=128)
84-
def _cached_document(url: str) -> Document:
84+
_DOCUMENT_CACHE: dict[str, Document] = {}
85+
86+
original_load = Document.load
87+
88+
89+
class DocumentWithDeferredLoad(Document):
90+
def load(self, *args: Any, **kwargs: Any) -> None:
91+
"""Deferred load of the document."""
92+
93+
def original_load(self, *args: Any, **kwargs: Any) -> None:
94+
"""Original load of the document."""
95+
return original_load(self, *args, **kwargs)
96+
97+
98+
async def _cached_document(url: str) -> Document:
8599
"""Load external XML document from disk."""
86-
return Document(url, ASYNC_TRANSPORT, settings=DEFAULT_SETTINGS)
100+
if url in _DOCUMENT_CACHE:
101+
return _DOCUMENT_CACHE[url]
102+
loop = asyncio.get_event_loop()
103+
104+
def _load_document() -> DocumentWithDeferredLoad:
105+
document = DocumentWithDeferredLoad(url, ASYNC_TRANSPORT, DEFAULT_SETTINGS)
106+
# Override the default datetime type to use FastDateTime
107+
# This is a workaround for the following issue:
108+
# https://github.com/mvantellingen/python-zeep/pull/1370
109+
schema = document.types.documents.get_by_namespace(
110+
"http://www.w3.org/2001/XMLSchema", False
111+
)[0]
112+
instance = FastDateTime(is_global=True)
113+
schema.register_type(FastDateTime._default_qname, instance)
114+
document.types.add_documents([None], url)
115+
# Perform the original load
116+
document.original_load(url)
117+
return document
118+
119+
document = await loop.run_in_executor(None, _load_document)
120+
_DOCUMENT_CACHE[url] = document
121+
return document
87122

88123

89124
class ZeepAsyncClient(BaseZeepAsyncClient):
@@ -201,9 +236,7 @@ async def setup(self):
201236
wsse = UsernameDigestTokenDtDiff(
202237
self.user, self.passwd, dt_diff=self.dt_diff, use_digest=self.encrypt
203238
)
204-
self.document = await self.loop.run_in_executor(
205-
None, _cached_document, self.url
206-
)
239+
self.document = await _cached_document(self.url)
207240
self.zeep_client_authless = ZeepAsyncClient(
208241
wsdl=self.document,
209242
transport=self.transport,

onvif/types.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""ONVIF types."""
2+
3+
4+
import ciso8601
5+
import isodate
6+
from zeep.xsd.types.builtins import DateTime, treat_whitespace
7+
8+
9+
# see https://github.com/mvantellingen/python-zeep/pull/1370
10+
class FastDateTime(DateTime):
11+
"""Fast DateTime that supports timestamps with - instead of T."""
12+
13+
@treat_whitespace("collapse")
14+
def pythonvalue(self, value):
15+
"""Convert the xml value into a python value."""
16+
try:
17+
return ciso8601.parse_datetime(value)
18+
except ValueError:
19+
pass
20+
# Determine based on the length of the value if it only contains a date
21+
# lazy hack ;-)
22+
23+
if len(value) == 10:
24+
value += "T00:00:00"
25+
elif (len(value) == 19 or len(value) == 26) and value[10] == " ":
26+
value = "T".join(value.split(" "))
27+
elif len(value) > 10 and value[10] == "-": # 2010-01-01-00:00:00...
28+
value[10] = "T"
29+
return isodate.parse_datetime(value)

setup.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@
77
version_path = os.path.join(here, "onvif/version.txt")
88
version = open(version_path).read().strip()
99

10-
requires = ["httpx>=0.19.0,<1.0.0", "zeep[async]>=4.2.1,<5.0.0"]
10+
requires = [
11+
"httpx>=0.19.0,<1.0.0",
12+
"zeep[async]>=4.2.1,<5.0.0",
13+
"ciso8601>=2.1.3",
14+
"isodate>=0.6.0",
15+
]
1116

1217
CLASSIFIERS = [
1318
"Development Status :: 3 - Alpha",

0 commit comments

Comments
 (0)