Skip to content

Commit f3bda41

Browse files
committed
better handling of cached attributes
1 parent af88057 commit f3bda41

1 file changed

Lines changed: 90 additions & 28 deletions

File tree

ayon_api/_api_helpers/attributes.py

Lines changed: 90 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,107 @@
11
from __future__ import annotations
22

3+
import copy
4+
import time
35
import typing
46
from typing import Optional
5-
import copy
67

78
from .base import BaseServerAPI
89

910
if typing.TYPE_CHECKING:
1011
from ayon_api.typing import (
12+
AttributeSchemaDict,
1113
AttributeSchemaDataDict,
1214
AttributesSchemaDict,
1315
AttributeScope,
1416
)
1517

18+
class _AttributesCache:
19+
_schema = None
20+
_last_fetch = 0
21+
_timeout = 60
22+
_attributes_by_type = {}
23+
24+
def reset_schema(self) -> None:
25+
self._schema = None
26+
self._last_fetch = 0
27+
self._attributes_by_type = {}
28+
29+
def set_timeout(self, timeout: int) -> None:
30+
self._timeout = timeout
31+
32+
def get_schema(self) -> AttributesSchemaDict:
33+
return copy.deepcopy(self._schema)
34+
35+
def set_schema(self, schema: AttributesSchemaDict) -> None:
36+
self._schema = schema
37+
self._last_fetch = time.time()
38+
39+
def is_valid(self) -> bool:
40+
if self._schema is None:
41+
return False
42+
return time.time() - self._last_fetch < self._timeout
43+
44+
def invalidate(self) -> None:
45+
if not self.is_valid():
46+
self.reset_schema()
47+
48+
def get_attributes_for_type(
49+
self, entity_type: AttributeScope
50+
) -> list[AttributeSchemaDict]:
51+
attributes = self._attributes_by_type.get(entity_type)
52+
if attributes is not None:
53+
return attributes
54+
55+
attributes_schema = self.get_schema()
56+
if attributes_schema is None:
57+
raise ValueError("Attributes schema is not cached.")
58+
59+
attributes = []
60+
for attr in attributes_schema["attributes"]:
61+
if entity_type not in attr["scope"]:
62+
continue
63+
attributes.append(attr)
64+
65+
self._attributes_by_type[entity_type] = attributes
66+
return attributes
67+
68+
1669

1770
class AttributesAPI(BaseServerAPI):
18-
_attributes_schema = None
19-
_entity_type_attributes_cache = {}
71+
_attributes_cache = _AttributesCache()
2072

2173
def get_attributes_schema(
2274
self, use_cache: bool = True
2375
) -> AttributesSchemaDict:
2476
if not use_cache:
25-
self.reset_attributes_schema()
77+
self._attributes_cache.reset_schema()
78+
else:
79+
self._attributes_cache.invalidate()
2680

27-
if self._attributes_schema is None:
81+
if not self._attributes_cache.is_valid():
2882
result = self.get("attributes")
2983
result.raise_for_status()
30-
self._attributes_schema = result.data
31-
return copy.deepcopy(self._attributes_schema)
84+
self._attributes_cache.set_schema(result.data)
85+
return self._attributes_cache.get_schema()
3286

3387
def reset_attributes_schema(self) -> None:
34-
self._attributes_schema = None
35-
self._entity_type_attributes_cache = {}
88+
"""Reset attributes schema cache.
89+
90+
DEPRECATED:
91+
Use 'reset_attributes_cache' instead.
92+
93+
"""
94+
self.log.warning(
95+
"Used deprecated function 'reset_attributes_schema'."
96+
" Please use 'reset_attributes_cache' instead."
97+
)
98+
self.reset_attributes_cache()
99+
100+
def reset_attributes_cache(self) -> None:
101+
self._attributes_cache.reset_schema()
102+
103+
def set_attributes_cache_timeout(self, timeout: int) -> None:
104+
self._attributes_cache.set_timeout(timeout)
36105

37106
def set_attribute_config(
38107
self,
@@ -63,12 +132,10 @@ def set_attribute_config(
63132
position=position,
64133
builtin=builtin
65134
)
66-
if response.status_code != 204:
67-
# TODO raise different exception
68-
raise ValueError(
69-
f"Attribute \"{attribute_name}\" was not created/updated."
70-
f" {response.detail}"
71-
)
135+
response.raise_for_status(
136+
f"Attribute \"{attribute_name}\" was not created/updated."
137+
f" {response.detail}"
138+
)
72139

73140
self.reset_attributes_schema()
74141

@@ -128,19 +195,14 @@ def get_attributes_for_type(
128195
for entered entity type.
129196
130197
"""
131-
attributes = self._entity_type_attributes_cache.get(entity_type)
132-
if attributes is None:
133-
attributes_schema = self.get_attributes_schema()
134-
attributes = {}
135-
for attr in attributes_schema["attributes"]:
136-
if entity_type not in attr["scope"]:
137-
continue
138-
attr_name = attr["name"]
139-
attributes[attr_name] = attr["data"]
140-
141-
self._entity_type_attributes_cache[entity_type] = attributes
142-
143-
return copy.deepcopy(attributes)
198+
# Make sure attributes are cached
199+
self.get_attributes_schema()
200+
return {
201+
attr["name"]: attr["data"]
202+
for attr in self._attributes_cache.get_attributes_for_type(
203+
entity_type
204+
)
205+
}
144206

145207
def get_attributes_fields_for_type(
146208
self, entity_type: AttributeScope

0 commit comments

Comments
 (0)