2020import typing
2121
2222# Fake modules to avoid import errors
23-
2423requests = type (sys )("requests" )
2524requests .__dict__ ["Response" ] = type (
2625 "Response" , (), {"__module__" : "requests" }
2928sys .modules ["requests" ] = requests
3029sys .modules ["unidecode" ] = type (sys )("unidecode" )
3130
32- import ayon_api # noqa: E402
33- from ayon_api .server_api import ServerAPI , _PLACEHOLDER # noqa: E402
34- from ayon_api .utils import NOT_SET # noqa: E402
31+ CURRENT_DIR = os .path .dirname (os .path .abspath (__file__ ))
3532
3633EXCLUDED_METHODS = {
3734 "get_default_service_username" ,
@@ -120,34 +117,38 @@ def prepare_docstring(func):
120117 return f'"""{ docstring } { line_char } \n """'
121118
122119
120+ def _find_obj (obj_full , api_globals ):
121+ parts = list (reversed (obj_full .split ("." )))
122+ _name = None
123+ for part in parts :
124+ if _name is None :
125+ _name = part
126+ else :
127+ _name = f"{ part } .{ _name } "
128+ try :
129+ # Test if typehint is valid for known '_api' content
130+ exec (f"_: { _name } = None" , api_globals )
131+ return _name
132+ except NameError :
133+ pass
134+ return None
135+
136+
123137def _get_typehint (annotation , api_globals ):
138+ if isinstance (annotation , str ):
139+ annotation = annotation .replace ("'" , "" )
140+
124141 if inspect .isclass (annotation ):
125- module_name_parts = list (str (annotation .__module__ ).split ("." ))
126- module_name_parts .append (annotation .__name__ )
127- module_name_parts .reverse ()
128- options = []
129- _name = None
130- for name in module_name_parts :
131- if _name is None :
132- _name = name
133- options .append (name )
134- else :
135- _name = f"{ name } .{ _name } "
136- options .append (_name )
137-
138- options .reverse ()
139- for option in options :
140- try :
141- # Test if typehint is valid for known '_api' content
142- exec (f"_: { option } = None" , api_globals )
143- return option
144- except NameError :
145- pass
146-
147- typehint = options [0 ]
148- print ("Unknown typehint:" , typehint )
149- typehint = f'"{ typehint } "'
150- return typehint
142+ module_name = str (annotation .__module__ )
143+ full_name = annotation .__name__
144+ if module_name :
145+ full_name = f"{ module_name } .{ full_name } "
146+ obj_name = _find_obj (full_name , api_globals )
147+ if obj_name is not None :
148+ return obj_name
149+
150+ print ("Unknown typehint:" , full_name )
151+ return full_name
151152
152153 typehint = (
153154 str (annotation )
@@ -156,25 +157,67 @@ def _get_typehint(annotation, api_globals):
156157 full_path_regex = re .compile (
157158 r"(?P<full>(?P<name>[a-zA-Z0-9_\.]+))"
158159 )
160+
159161 for item in full_path_regex .finditer (str (typehint )):
160162 groups = item .groupdict ()
161- name = groups ["name" ].split ("." )[- 1 ]
163+ name = groups ["name" ]
164+ obj_name = _find_obj (name , api_globals )
165+ if obj_name :
166+ name = obj_name
167+ else :
168+ name = name .split ("." )[- 1 ]
162169 typehint = typehint .replace (groups ["full" ], name )
163170
164171 forwardref_regex = re .compile (
165172 r"(?P<full>ForwardRef\('(?P<name>[a-zA-Z0-9]+)'\))"
166173 )
167174 for item in forwardref_regex .finditer (str (typehint )):
168175 groups = item .groupdict ()
169- name = groups ["name" ].split ("." )[- 1 ]
170- typehint = typehint .replace (groups ["full" ], f'"{ name } "' )
176+ name = groups ["name" ]
177+ obj_name = _find_obj (name , api_globals )
178+ if obj_name :
179+ name = obj_name
180+ else :
181+ name = name .split ("." )[- 1 ]
182+ typehint = typehint .replace (groups ["full" ], name )
171183
172184 try :
173185 # Test if typehint is valid for known '_api' content
174186 exec (f"_: { typehint } = None" , api_globals )
187+ return typehint
175188 except NameError :
176189 print ("Unknown typehint:" , typehint )
177- typehint = f'"{ typehint } "'
190+
191+ _typehint = typehint
192+ _typehing_parents = []
193+ while True :
194+ # Too hard to manage typehints with commas
195+ if "[" not in _typehint :
196+ break
197+
198+ parts = _typehint .split ("[" )
199+ parent = parts .pop (0 )
200+
201+ try :
202+ # Test if typehint is valid for known '_api' content
203+ exec (f"_: { parent } = None" , api_globals )
204+ except NameError :
205+ _typehint = parent
206+ break
207+
208+ _typehint = "[" .join (parts )[:- 1 ]
209+ if "," in _typehint :
210+ _typing = parent
211+ break
212+
213+ _typehing_parents .append (parent )
214+
215+ if _typehing_parents :
216+ typehint = _typehint
217+ for parent in reversed (_typehing_parents ):
218+ typehint = f"{ parent } [{ typehint } ]"
219+ return typehint
220+
178221 return typehint
179222
180223
@@ -192,6 +235,9 @@ def _add_typehint(param_name, param, api_globals):
192235
193236
194237def _kw_default_to_str (param_name , param , api_globals ):
238+ from ayon_api ._api_helpers .base import _PLACEHOLDER
239+ from ayon_api .utils import NOT_SET
240+
195241 if param .default is inspect .Parameter .empty :
196242 return _add_typehint (param_name , param , api_globals )
197243
@@ -249,6 +295,9 @@ def sig_params_to_str(sig, param_names, api_globals, indent=0):
249295 body_params .append (f"*{ var_positional } " )
250296 func_params .append (f"*{ var_positional } " )
251297
298+ elif kw_only :
299+ func_params .append ("*" )
300+
252301 for param_name , param in kw_only :
253302 body_params .append (f"{ param_name } ={ param_name } " )
254303 func_params .append (_kw_default_to_str (param_name , param , api_globals ))
@@ -284,8 +333,54 @@ def sig_params_to_str(sig, param_names, api_globals, indent=0):
284333
285334
286335def prepare_api_functions (api_globals ):
336+ from ayon_api .server_api import ( # noqa: E402
337+ ServerAPI ,
338+ InstallersAPI ,
339+ DependencyPackagesAPI ,
340+ SecretsAPI ,
341+ BundlesAddonsAPI ,
342+ EventsAPI ,
343+ AttributesAPI ,
344+ ProjectsAPI ,
345+ FoldersAPI ,
346+ TasksAPI ,
347+ ProductsAPI ,
348+ VersionsAPI ,
349+ RepresentationsAPI ,
350+ WorkfilesAPI ,
351+ ThumbnailsAPI ,
352+ ActivitiesAPI ,
353+ ActionsAPI ,
354+ LinksAPI ,
355+ ListsAPI ,
356+ )
357+
287358 functions = []
288- for attr_name , attr in ServerAPI .__dict__ .items ():
359+ _items = list (ServerAPI .__dict__ .items ())
360+ _items .extend (InstallersAPI .__dict__ .items ())
361+ _items .extend (DependencyPackagesAPI .__dict__ .items ())
362+ _items .extend (SecretsAPI .__dict__ .items ())
363+ _items .extend (ActionsAPI .__dict__ .items ())
364+ _items .extend (ActivitiesAPI .__dict__ .items ())
365+ _items .extend (BundlesAddonsAPI .__dict__ .items ())
366+ _items .extend (EventsAPI .__dict__ .items ())
367+ _items .extend (AttributesAPI .__dict__ .items ())
368+ _items .extend (ProjectsAPI .__dict__ .items ())
369+ _items .extend (FoldersAPI .__dict__ .items ())
370+ _items .extend (TasksAPI .__dict__ .items ())
371+ _items .extend (ProductsAPI .__dict__ .items ())
372+ _items .extend (VersionsAPI .__dict__ .items ())
373+ _items .extend (RepresentationsAPI .__dict__ .items ())
374+ _items .extend (WorkfilesAPI .__dict__ .items ())
375+ _items .extend (LinksAPI .__dict__ .items ())
376+ _items .extend (ListsAPI .__dict__ .items ())
377+ _items .extend (ThumbnailsAPI .__dict__ .items ())
378+
379+ processed = set ()
380+ for attr_name , attr in _items :
381+ if attr_name in processed :
382+ continue
383+ processed .add (attr_name )
289384 if (
290385 attr_name .startswith ("_" )
291386 or attr_name in EXCLUDED_METHODS
@@ -323,10 +418,7 @@ def prepare_api_functions(api_globals):
323418def main ():
324419 print ("Creating public API functions based on ServerAPI methods" )
325420 # TODO order methods in some order
326- dirpath = os .path .dirname (os .path .dirname (
327- os .path .abspath (ayon_api .__file__ )
328- ))
329- ayon_api_root = os .path .join (dirpath , "ayon_api" )
421+ ayon_api_root = os .path .join (CURRENT_DIR , "ayon_api" )
330422 init_filepath = os .path .join (ayon_api_root , "__init__.py" )
331423 api_filepath = os .path .join (ayon_api_root , "_api.py" )
332424
@@ -350,15 +442,13 @@ def main():
350442 # Read content of first part of `_api.py` to get global variables
351443 # - disable type checking so imports done only during typechecking are
352444 # not executed
353- old_value = typing .TYPE_CHECKING
354445 typing .TYPE_CHECKING = False
355446 api_globals = {"__name__" : "ayon_api._api" }
356447 exec (parts [0 ], api_globals )
448+
357449 for attr_name in dir (__builtins__ ):
358450 api_globals [attr_name ] = getattr (__builtins__ , attr_name )
359- typing .TYPE_CHECKING = old_value
360451
361- # print(api_globals)
362452 print ("(3/5) Preparing functions body based on 'ServerAPI' class" )
363453 result = prepare_api_functions (api_globals )
364454
0 commit comments