11from __future__ import annotations
22
3+ import copy
4+ import time
35import typing
46from typing import Optional
5- import copy
67
78from .base import BaseServerAPI
89
910if typing .TYPE_CHECKING :
1011 from ayon_api .typing import (
11- AttributeSchemaDataDict ,
1212 AttributeSchemaDict ,
13+ AttributeSchemaDataDict ,
1314 AttributesSchemaDict ,
1415 AttributeScope ,
1516 )
1617
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+
1769
1870class AttributesAPI (BaseServerAPI ):
19- _attributes_schema = None
20- _entity_type_attributes_cache = {}
71+ _attributes_cache = _AttributesCache ()
2172
2273 def get_attributes_schema (
2374 self , use_cache : bool = True
2475 ) -> AttributesSchemaDict :
2576 if not use_cache :
26- self .reset_attributes_schema ()
77+ self ._attributes_cache .reset_schema ()
78+ else :
79+ self ._attributes_cache .invalidate ()
2780
28- if self ._attributes_schema is None :
81+ if not self ._attributes_cache . is_valid () :
2982 result = self .get ("attributes" )
3083 result .raise_for_status ()
31- self ._attributes_schema = result .data
32- return copy . deepcopy ( self ._attributes_schema )
84+ self ._attributes_cache . set_schema ( result .data )
85+ return self ._attributes_cache . get_schema ( )
3386
3487 def reset_attributes_schema (self ) -> None :
35- self ._attributes_schema = None
36- 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 )
37105
38106 def set_attribute_config (
39107 self ,
@@ -64,12 +132,10 @@ def set_attribute_config(
64132 position = position ,
65133 builtin = builtin
66134 )
67- if response .status_code != 204 :
68- # TODO raise different exception
69- raise ValueError (
70- f"Attribute \" { attribute_name } \" was not created/updated."
71- f" { response .detail } "
72- )
135+ response .raise_for_status (
136+ f"Attribute \" { attribute_name } \" was not created/updated."
137+ f" { response .detail } "
138+ )
73139
74140 self .reset_attributes_schema ()
75141
@@ -92,7 +158,7 @@ def remove_attribute_config(self, attribute_name: str) -> None:
92158
93159 def get_attributes_for_type (
94160 self , entity_type : AttributeScope
95- ) -> dict [str , AttributeSchemaDict ]:
161+ ) -> dict [str , AttributeSchemaDataDict ]:
96162 """Get attribute schemas available for an entity type.
97163
98164 Example::
@@ -129,29 +195,32 @@ def get_attributes_for_type(
129195 for entered entity type.
130196
131197 """
132- attributes = self ._entity_type_attributes_cache .get (entity_type )
133- if attributes is None :
134- attributes_schema = self .get_attributes_schema ()
135- attributes = {}
136- for attr in attributes_schema ["attributes" ]:
137- if entity_type not in attr ["scope" ]:
138- continue
139- attr_name = attr ["name" ]
140- attributes [attr_name ] = attr ["data" ]
141-
142- self ._entity_type_attributes_cache [entity_type ] = attributes
143-
144- 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+ }
145206
146207 def get_attributes_fields_for_type (
147208 self , entity_type : AttributeScope
148209 ) -> set [str ]:
149210 """Prepare attribute fields for entity type.
150211
212+ DEPRECATED: Field 'attrib' is marked as deprecated and should not be
213+ used for GraphQL queries.
214+
151215 Returns:
152216 set[str]: Attributes fields for entity type.
153217
154218 """
219+ self .log .warning (
220+ "Method 'get_attributes_fields_for_type' is deprecated and should"
221+ " not be used for GraphQL queries. Use 'allAttrib' field instead"
222+ " of 'attrib'."
223+ )
155224 attributes = self .get_attributes_for_type (entity_type )
156225 return {
157226 f"attrib.{ attr } "
0 commit comments