retrieval.py 2.5 KB

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