retrieval.py 2.7 KB

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