utils.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. import inspect
  2. import string
  3. from typing import Callable
  4. from typing import List
  5. from typing import Optional
  6. TEMPLATE_FRAGMENT_KEY_TEMPLATE = "_template_fragment_cache_%s%s"
  7. # Used to remove control characters and whitespace from cache keys.
  8. valid_chars = set(string.ascii_letters + string.digits + "_.")
  9. del_chars = "".join(c for c in map(chr, range(256)) if c not in valid_chars)
  10. null_control = ({k: None for k in del_chars},)
  11. def wants_args(f: Callable) -> bool:
  12. """Check if the function wants any arguments"""
  13. arg_spec = inspect.getfullargspec(f)
  14. return bool(arg_spec.args or arg_spec.varargs or arg_spec.varkw)
  15. def get_function_parameters(f: Callable) -> List:
  16. """Get function parameters
  17. :param f
  18. :return: Parameter list of function
  19. """
  20. return list(inspect.signature(f).parameters.values())
  21. def get_arg_names(f: Callable) -> List[str]:
  22. """Return arguments of function
  23. :param f:
  24. :return: String list of arguments
  25. """
  26. return [
  27. parameter.name
  28. for parameter in get_function_parameters(f)
  29. if parameter.kind == parameter.POSITIONAL_OR_KEYWORD
  30. ]
  31. def get_arg_default(f: Callable, position: int):
  32. arg = get_function_parameters(f)[position]
  33. arg_def = arg.default
  34. return arg_def if arg_def != inspect.Parameter.empty else None
  35. def get_id(obj):
  36. return getattr(obj, "__caching_id__", repr)(obj)
  37. def function_namespace(f, args=None):
  38. """Attempts to returns unique namespace for function"""
  39. m_args = get_arg_names(f)
  40. instance_token = None
  41. instance_self = getattr(f, "__self__", None)
  42. if instance_self and not inspect.isclass(instance_self):
  43. instance_token = get_id(f.__self__)
  44. elif m_args and m_args[0] == "self" and args:
  45. instance_token = get_id(args[0])
  46. module = f.__module__
  47. if m_args and m_args[0] == "cls" and not inspect.isclass(args[0]):
  48. raise ValueError(
  49. "When using `delete_memoized` on a "
  50. "`@classmethod` you must provide the "
  51. "class as the first argument"
  52. )
  53. if hasattr(f, "__qualname__"):
  54. name = f.__qualname__
  55. else:
  56. klass = getattr(f, "__self__", None)
  57. if klass and not inspect.isclass(klass):
  58. klass = klass.__class__
  59. if not klass:
  60. klass = getattr(f, "im_class", None)
  61. if not klass:
  62. if m_args and args:
  63. if m_args[0] == "self":
  64. klass = args[0].__class__
  65. elif m_args[0] == "cls":
  66. klass = args[0]
  67. if klass:
  68. name = klass.__name__ + "." + f.__name__
  69. else:
  70. name = f.__name__
  71. ns = ".".join((module, name)).translate(*null_control)
  72. ins = (
  73. ".".join((module, name, instance_token)).translate(*null_control)
  74. if instance_token
  75. else None
  76. )
  77. return ns, ins
  78. def make_template_fragment_key(
  79. fragment_name: str, vary_on: Optional[List[str]] = None
  80. ) -> str:
  81. """Make a cache key for a specific fragment name."""
  82. if vary_on:
  83. fragment_name = "%s_" % fragment_name
  84. else:
  85. vary_on = []
  86. return TEMPLATE_FRAGMENT_KEY_TEMPLATE % (fragment_name, "_".join(vary_on))