Coverage for /Users/davegaeddert/Developer/dropseed/plain/plain-models/plain/models/utils.py: 71%

1 import functools

2 from collections import namedtuple

3

4

5 def make_model_tuple(model):

6 """

7 Take a model or a string of the form "package_label.ModelName" and return a

8 corresponding ("package_label", "modelname") tuple. If a tuple is passed in,

9 assume it's a valid model tuple already and return it unchanged.

10 """

11 try:

12 if isinstance(model, tuple):

13 model_tuple = model

14 elif isinstance(model, str):

15 package_label, model_name = model.split(".")

16 model_tuple = package_label, model_name.lower()

17 else:

18 model_tuple = model._meta.package_label, model._meta.model_name

19 assert len(model_tuple) == 2

20 return model_tuple

21 except (ValueError, AssertionError):

22 raise ValueError(

23 f"Invalid model reference '{model}'. String model references "

24 "must be of the form 'package_label.ModelName'."

25 )

26

27

28 def resolve_callables(mapping):

29 """

30 Generate key/value pairs for the given mapping where the values are

31 evaluated if they're callable.

32 """

33 for k, v in mapping.items():

34 yield k, v() if callable(v) else v

35

36

37 def unpickle_named_row(names, values):

38 return create_namedtuple_class(*names)(*values)

39

40

41@functools.lru_cache

42 def create_namedtuple_class(*names):

43 # Cache type() with @lru_cache since it's too slow to be called for every

44 # QuerySet evaluation.

45 def __reduce__(self):

46 return unpickle_named_row, (names, tuple(self))

47

48 return type(

49 "Row",

50 (namedtuple("Row", names),),

51 {"__reduce__": __reduce__, "__slots__": ()},

52 )

53

54

55 class AltersData:

56 """

57 Make subclasses preserve the alters_data attribute on overridden methods.

58 """

59

60 def __init_subclass__(cls, **kwargs):

61 for fn_name, fn in vars(cls).items():

62 if callable(fn) and not hasattr(fn, "alters_data"):

63 for base in cls.__bases__:

64 if base_fn := getattr(base, fn_name, None):

65 if hasattr(base_fn, "alters_data"):

66 fn.alters_data = base_fn.alters_data

67 break

68

69 super().__init_subclass__(**kwargs)