Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
5f562ab
added more methods to base class
iLLiCiTiT Aug 12, 2025
1d5ef25
moved request type to utils
iLLiCiTiT Aug 12, 2025
8d33597
moved addons logic to separate class
iLLiCiTiT Aug 12, 2025
b9011f8
add addons api to automated api
iLLiCiTiT Aug 12, 2025
ee6ec10
updated order of functions in public api
iLLiCiTiT Aug 12, 2025
4daab3e
move 'fill_own_attribs' to utils
iLLiCiTiT Aug 12, 2025
f04edad
added more black magic
iLLiCiTiT Aug 12, 2025
1e94982
change imports in init
iLLiCiTiT Aug 12, 2025
ac96f87
separated project methods
iLLiCiTiT Aug 12, 2025
bbd75df
added annotations imports
iLLiCiTiT Aug 12, 2025
ec23b65
move links to separate file
iLLiCiTiT Aug 12, 2025
186bc36
move more methods related to projects
iLLiCiTiT Aug 12, 2025
4841222
change order of api functions
iLLiCiTiT Aug 12, 2025
f1114ae
change links order
iLLiCiTiT Aug 12, 2025
41890a0
move 'prepare_list_filters' to utils
iLLiCiTiT Aug 12, 2025
531b95e
define 'ServerVersion' type
iLLiCiTiT Aug 12, 2025
eaf901e
implement 'get_rest_entity_by_id'
iLLiCiTiT Aug 12, 2025
ec5f5e6
move folders to separate class
iLLiCiTiT Aug 12, 2025
f02e25c
moved events api to separate file
iLLiCiTiT Aug 12, 2025
a34be6f
moved activities api
iLLiCiTiT Aug 12, 2025
f876275
move few things to utils
iLLiCiTiT Aug 12, 2025
462f9e2
move thumbnails api to separate file
iLLiCiTiT Aug 12, 2025
f00f4e6
moved workfiles info into separate file
iLLiCiTiT Aug 12, 2025
14b56b7
moved representations api to separate file
iLLiCiTiT Aug 12, 2025
11ba7d8
moved tasks, products and versions
iLLiCiTiT Aug 12, 2025
81729e3
use set, list and dict for typehints
iLLiCiTiT Aug 12, 2025
dc058e0
use list, dict and set in typing too
iLLiCiTiT Aug 12, 2025
99df5d9
updated typehints in public api
iLLiCiTiT Aug 12, 2025
815eccc
added get_site_id to base
iLLiCiTiT Aug 13, 2025
200574f
removed unused imports
iLLiCiTiT Aug 13, 2025
cf0e691
import placeholder from base
iLLiCiTiT Aug 13, 2025
013cc8f
move all bundles and addons endpoints to single class
iLLiCiTiT Aug 13, 2025
de77890
rename the class
iLLiCiTiT Aug 13, 2025
6ba7388
rename file
iLLiCiTiT Aug 14, 2025
74c65d3
move apis to subfolder
iLLiCiTiT Aug 14, 2025
af7b909
use new imports
iLLiCiTiT Aug 14, 2025
9fe3b1c
future annotations
iLLiCiTiT Aug 14, 2025
1448ffb
reorder imports
iLLiCiTiT Aug 14, 2025
32bbda9
move anatomy and roots methods to project api
iLLiCiTiT Aug 14, 2025
ea0632a
separated attributes methods
iLLiCiTiT Aug 14, 2025
2387765
moved dependency packages and installers endpoints to separate files
iLLiCiTiT Aug 14, 2025
27bf88b
moved secrets to separate file
iLLiCiTiT Aug 14, 2025
2957f8c
update public api
iLLiCiTiT Aug 14, 2025
d9994be
ruff fixes
iLLiCiTiT Aug 14, 2025
d6b112c
added annotations import to public api
iLLiCiTiT Aug 14, 2025
8a2571e
more ruff fixes
iLLiCiTiT Aug 14, 2025
699d374
don't use wrapped typehins
iLLiCiTiT Aug 28, 2025
5555eb8
added log property do base
iLLiCiTiT Aug 28, 2025
460e0ce
use 'get_server_version_tuple'
iLLiCiTiT Aug 28, 2025
2d5fb18
added typehints to save and delete secret
iLLiCiTiT Aug 28, 2025
7d5d49e
better event typehints
iLLiCiTiT Aug 28, 2025
7b362b7
added 'create_event' function that returns event id
iLLiCiTiT Aug 28, 2025
c7a476e
Merge branch 'develop' into enhancement/171-split-ayon-python-api
iLLiCiTiT Aug 28, 2025
d7326ad
minor typehint fixes
iLLiCiTiT Aug 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 127 additions & 48 deletions automated_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import typing

# Fake modules to avoid import errors

requests = type(sys)("requests")
requests.__dict__["Response"] = type(
"Response", (), {"__module__": "requests"}
Expand All @@ -29,14 +28,7 @@
sys.modules["requests"] = requests
sys.modules["unidecode"] = type(sys)("unidecode")

import ayon_api # noqa: E402
from ayon_api.server_api import ( # noqa: E402
ServerAPI,
_PLACEHOLDER,
_ActionsAPI,
_ListsAPI,
)
from ayon_api.utils import NOT_SET # noqa: E402
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))

EXCLUDED_METHODS = {
"get_default_service_username",
Expand Down Expand Up @@ -125,34 +117,38 @@ def prepare_docstring(func):
return f'"""{docstring}{line_char}\n"""'


def _find_obj(obj_full, api_globals):
parts = list(reversed(obj_full.split(".")))
_name = None
for part in parts:
if _name is None:
_name = part
else:
_name = f"{part}.{_name}"
try:
# Test if typehint is valid for known '_api' content
exec(f"_: {_name} = None", api_globals)
return _name
except NameError:
pass
return None


def _get_typehint(annotation, api_globals):
if isinstance(annotation, str):
annotation = annotation.replace("'", "")

if inspect.isclass(annotation):
module_name_parts = list(str(annotation.__module__).split("."))
module_name_parts.append(annotation.__name__)
module_name_parts.reverse()
options = []
_name = None
for name in module_name_parts:
if _name is None:
_name = name
options.append(name)
else:
_name = f"{name}.{_name}"
options.append(_name)

options.reverse()
for option in options:
try:
# Test if typehint is valid for known '_api' content
exec(f"_: {option} = None", api_globals)
return option
except NameError:
pass

typehint = options[0]
print("Unknown typehint:", typehint)
typehint = f'"{typehint}"'
return typehint
module_name = str(annotation.__module__)
full_name = annotation.__name__
if module_name:
full_name = f"{module_name}.{full_name}"
obj_name = _find_obj(full_name, api_globals)
if obj_name is not None:
return obj_name

print("Unknown typehint:", full_name)
return full_name

typehint = (
str(annotation)
Expand All @@ -161,25 +157,67 @@ def _get_typehint(annotation, api_globals):
full_path_regex = re.compile(
r"(?P<full>(?P<name>[a-zA-Z0-9_\.]+))"
)

for item in full_path_regex.finditer(str(typehint)):
groups = item.groupdict()
name = groups["name"].split(".")[-1]
name = groups["name"]
obj_name = _find_obj(name, api_globals)
if obj_name:
name = obj_name
else:
name = name.split(".")[-1]
typehint = typehint.replace(groups["full"], name)

forwardref_regex = re.compile(
r"(?P<full>ForwardRef\('(?P<name>[a-zA-Z0-9]+)'\))"
)
for item in forwardref_regex.finditer(str(typehint)):
groups = item.groupdict()
name = groups["name"].split(".")[-1]
typehint = typehint.replace(groups["full"], f'"{name}"')
name = groups["name"]
obj_name = _find_obj(name, api_globals)
if obj_name:
name = obj_name
else:
name = name.split(".")[-1]
typehint = typehint.replace(groups["full"], name)

try:
# Test if typehint is valid for known '_api' content
exec(f"_: {typehint} = None", api_globals)
return typehint
except NameError:
print("Unknown typehint:", typehint)
typehint = f'"{typehint}"'

_typehint = typehint
_typehing_parents = []
while True:
# Too hard to manage typehints with commas
if "[" not in _typehint:
break

parts = _typehint.split("[")
parent = parts.pop(0)

try:
# Test if typehint is valid for known '_api' content
exec(f"_: {parent} = None", api_globals)
except NameError:
_typehint = parent
break

_typehint = "[".join(parts)[:-1]
if "," in _typehint:
_typing = parent
break

_typehing_parents.append(parent)

if _typehing_parents:
typehint = _typehint
for parent in reversed(_typehing_parents):
typehint = f"{parent}[{typehint}]"
return typehint

return typehint


Expand All @@ -197,6 +235,9 @@ def _add_typehint(param_name, param, api_globals):


def _kw_default_to_str(param_name, param, api_globals):
from ayon_api._api_helpers.base import _PLACEHOLDER
from ayon_api.utils import NOT_SET

if param.default is inspect.Parameter.empty:
return _add_typehint(param_name, param, api_globals)

Expand Down Expand Up @@ -292,11 +333,54 @@ def sig_params_to_str(sig, param_names, api_globals, indent=0):


def prepare_api_functions(api_globals):
from ayon_api.server_api import ( # noqa: E402
ServerAPI,
InstallersAPI,
DependencyPackagesAPI,
SecretsAPI,
BundlesAddonsAPI,
EventsAPI,
AttributesAPI,
ProjectsAPI,
FoldersAPI,
TasksAPI,
ProductsAPI,
VersionsAPI,
RepresentationsAPI,
WorkfilesAPI,
ThumbnailsAPI,
ActivitiesAPI,
ActionsAPI,
LinksAPI,
ListsAPI,
)

functions = []
_items = list(ServerAPI.__dict__.items())
_items.extend(_ActionsAPI.__dict__.items())
_items.extend(_ListsAPI.__dict__.items())
_items.extend(InstallersAPI.__dict__.items())
_items.extend(DependencyPackagesAPI.__dict__.items())
_items.extend(SecretsAPI.__dict__.items())
_items.extend(ActionsAPI.__dict__.items())
_items.extend(ActivitiesAPI.__dict__.items())
_items.extend(BundlesAddonsAPI.__dict__.items())
_items.extend(EventsAPI.__dict__.items())
_items.extend(AttributesAPI.__dict__.items())
_items.extend(ProjectsAPI.__dict__.items())
_items.extend(FoldersAPI.__dict__.items())
_items.extend(TasksAPI.__dict__.items())
_items.extend(ProductsAPI.__dict__.items())
_items.extend(VersionsAPI.__dict__.items())
_items.extend(RepresentationsAPI.__dict__.items())
_items.extend(WorkfilesAPI.__dict__.items())
_items.extend(LinksAPI.__dict__.items())
_items.extend(ListsAPI.__dict__.items())
_items.extend(ThumbnailsAPI.__dict__.items())

processed = set()
for attr_name, attr in _items:
if attr_name in processed:
continue
processed.add(attr_name)
if (
attr_name.startswith("_")
or attr_name in EXCLUDED_METHODS
Expand Down Expand Up @@ -334,10 +418,7 @@ def prepare_api_functions(api_globals):
def main():
print("Creating public API functions based on ServerAPI methods")
# TODO order methods in some order
dirpath = os.path.dirname(os.path.dirname(
os.path.abspath(ayon_api.__file__)
))
ayon_api_root = os.path.join(dirpath, "ayon_api")
ayon_api_root = os.path.join(CURRENT_DIR, "ayon_api")
init_filepath = os.path.join(ayon_api_root, "__init__.py")
api_filepath = os.path.join(ayon_api_root, "_api.py")

Expand All @@ -361,15 +442,13 @@ def main():
# Read content of first part of `_api.py` to get global variables
# - disable type checking so imports done only during typechecking are
# not executed
old_value = typing.TYPE_CHECKING
typing.TYPE_CHECKING = False
api_globals = {"__name__": "ayon_api._api"}
exec(parts[0], api_globals)

for attr_name in dir(__builtins__):
api_globals[attr_name] = getattr(__builtins__, attr_name)
typing.TYPE_CHECKING = old_value

# print(api_globals)
print("(3/5) Preparing functions body based on 'ServerAPI' class")
result = prepare_api_functions(api_globals)

Expand Down
Loading