retrieval.py 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. """
  2. Helpers related to (dynamic) resource retrieval.
  3. """
  4. from __future__ import annotations
  5. from functools import lru_cache
  6. from typing import TYPE_CHECKING, Callable
  7. import json
  8. try:
  9. from typing_extensions import TypeVar
  10. except ImportError: # pragma: no cover
  11. from typing import TypeVar
  12. from referencing import Resource
  13. if TYPE_CHECKING:
  14. from referencing.typing import URI, D, Retrieve
  15. #: A serialized document (e.g. a JSON string)
  16. _T = TypeVar("_T", default=str)
  17. def to_cached_resource(
  18. cache: Callable[[Retrieve[D]], Retrieve[D]] | None = None,
  19. loads: Callable[[_T], D] = json.loads,
  20. from_contents: Callable[[D], Resource[D]] = Resource.from_contents,
  21. ) -> Callable[[Callable[[URI], _T]], Retrieve[D]]:
  22. """
  23. Create a retriever which caches its return values from a simpler callable.
  24. Takes a function which returns things like serialized JSON (strings) and
  25. returns something suitable for passing to `Registry` as a retrieve
  26. function.
  27. This decorator both reduces a small bit of boilerplate for a common case
  28. (deserializing JSON from strings and creating `Resource` objects from the
  29. result) as well as makes the probable need for caching a bit easier.
  30. Retrievers which otherwise do expensive operations (like hitting the
  31. network) might otherwise be called repeatedly.
  32. Examples
  33. --------
  34. .. testcode::
  35. from referencing import Registry
  36. from referencing.typing import URI
  37. import referencing.retrieval
  38. @referencing.retrieval.to_cached_resource()
  39. def retrieve(uri: URI):
  40. print(f"Retrieved {uri}")
  41. # Normally, go get some expensive JSON from the network, a file ...
  42. return '''
  43. {
  44. "$schema": "https://json-schema.org/draft/2020-12/schema",
  45. "foo": "bar"
  46. }
  47. '''
  48. one = Registry(retrieve=retrieve).get_or_retrieve("urn:example:foo")
  49. print(one.value.contents["foo"])
  50. # Retrieving the same URI again reuses the same value (and thus doesn't
  51. # print another retrieval message here)
  52. two = Registry(retrieve=retrieve).get_or_retrieve("urn:example:foo")
  53. print(two.value.contents["foo"])
  54. .. testoutput::
  55. Retrieved urn:example:foo
  56. bar
  57. bar
  58. """
  59. if cache is None:
  60. cache = lru_cache(maxsize=None)
  61. def decorator(retrieve: Callable[[URI], _T]):
  62. @cache
  63. def cached_retrieve(uri: URI):
  64. response = retrieve(uri)
  65. contents = loads(response)
  66. return from_contents(contents)
  67. return cached_retrieve
  68. return decorator