Skip to content

Commit 234278e

Browse files
Gen stubs
1 parent 9f434f2 commit 234278e

File tree

4 files changed

+797
-614
lines changed

4 files changed

+797
-614
lines changed

ci/gen_stubs.py

Lines changed: 90 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,54 +2,103 @@
22
# SPDX-License-Identifier: BSD 3-Clause License
33
# Update sync code stubs
44
import ast
5+
import subprocess
56

6-
import kr8s._objects as base_objects
7+
ELLIPSIS_NODE = ast.parse("...").body
8+
# TODO: Preserve docstrings in method/function definitions
9+
# TODO: Remove unused imports
710

11+
# Load the AST for the base object definitions
812
with open("kr8s/_objects.py") as base_source_fh:
913
base_source = base_source_fh.read()
1014
base_tree = ast.parse(base_source)
1115

12-
16+
# Load the AST for the sync API
1317
with open("kr8s/objects.py") as source_fh:
1418
source = source_fh.read()
1519
tree = ast.parse(source)
1620

17-
# Update module docstring
18-
if isinstance(tree.body[0], ast.Expr) and isinstance(
19-
tree.body[0].value, ast.Constant
20-
):
21-
tree.body[0].value.value += (
22-
"\n"
23-
"WARNING: This typing stub file was generated by ci/gen_stubs.py\n"
24-
"DO NOT MODIFY DIRECTLY\n"
25-
)
26-
27-
for node in tree.body:
28-
if isinstance(node, ast.ImportFrom):
29-
if node.level != 1:
30-
tree.body.remove(node)
31-
32-
elif isinstance(node, ast.ClassDef):
33-
# Clear the sync decorator
34-
for decorator in node.decorator_list:
35-
if isinstance(decorator, ast.Name) and decorator.id == "sync":
36-
node.decorator_list.remove(decorator)
37-
node.body = [subnode for subnode in node.body if not isinstance(subnode, ast.Assign)]
38-
# Populate public methods from base classes
39-
for base in node.bases:
40-
if isinstance(base, ast.Name):
41-
# import the base class in this script with importlib
42-
base_class = getattr(base_objects, base.id[1:])
43-
# iterate over the methods of the base class
44-
for base_class_attr in base_class.__dict__.keys():
45-
method = getattr(base_class, base_class_attr)
46-
if callable(method):
47-
if not base_class_attr.startswith("_") and not base_class_attr.startswith("async_"):
48-
node.body.append(ast.parse(f"def {base_class_attr}(self): ...").body[0])
49-
50-
51-
else:
52-
pass
53-
54-
with open("kr8s/objects_tmp.pyi", "w") as target_fh:
55-
target_fh.write(ast.unparse(tree))
21+
# Update module docstring
22+
if isinstance(tree.body[0], ast.Expr) and isinstance(tree.body[0].value, ast.Constant):
23+
tree.body[0].value.value += (
24+
"\n"
25+
"WARNING: This typing stub file was generated by ci/gen_stubs.py\n"
26+
"DO NOT MODIFY DIRECTLY\n"
27+
)
28+
29+
30+
# Add all imports from base_tree (these will be pruned later)
31+
for node in base_tree.body:
32+
if isinstance(node, ast.Import):
33+
tree.body.insert(2, node)
34+
elif isinstance(node, ast.ImportFrom):
35+
# If not __future__ import, add it
36+
if node.module != "__future__":
37+
tree.body.insert(2, node)
38+
39+
for node_index, node in enumerate(tree.body):
40+
41+
# # Remove non-relative imports
42+
# if isinstance(node, ast.ImportFrom):
43+
# if node.level != 1:
44+
# tree.body.remove(node)
45+
46+
# Update class definitions
47+
if isinstance(node, ast.ClassDef):
48+
# Replace the class definitions with the bass class
49+
for base_node in base_tree.body:
50+
if isinstance(base_node, ast.ClassDef) and base_node.name == node.name:
51+
if node.name == "APIObject":
52+
base_node.bases = node.bases
53+
54+
# Replace all methods with type stubs
55+
for subnode_index, subnode in enumerate(base_node.body):
56+
if isinstance(subnode, ast.FunctionDef):
57+
subnode.body = ELLIPSIS_NODE
58+
elif isinstance(subnode, ast.AsyncFunctionDef):
59+
base_node.body[subnode_index] = ast.FunctionDef(
60+
name=subnode.name,
61+
args=subnode.args,
62+
body=ELLIPSIS_NODE,
63+
decorator_list=subnode.decorator_list,
64+
returns=subnode.returns,
65+
type_comment=subnode.type_comment,
66+
type_params=subnode.type_params,
67+
lineno=subnode.lineno,
68+
)
69+
pass
70+
elif isinstance(subnode, ast.AnnAssign):
71+
# Remove value
72+
subnode.value = None
73+
else:
74+
pass
75+
76+
tree.body[node_index] = base_node
77+
78+
elif isinstance(node, ast.FunctionDef):
79+
# Replace all functions with version from bass_tree
80+
for base_node in base_tree.body:
81+
if (
82+
isinstance(base_node, ast.FunctionDef)
83+
or isinstance(base_node, ast.AsyncFunctionDef)
84+
) and base_node.name == node.name:
85+
# Replace the function definition
86+
tree.body[node_index] = ast.FunctionDef(
87+
name=base_node.name,
88+
args=base_node.args,
89+
body=ELLIPSIS_NODE,
90+
decorator_list=base_node.decorator_list,
91+
returns=base_node.returns,
92+
type_comment=base_node.type_comment,
93+
type_params=base_node.type_params,
94+
lineno=base_node.lineno,
95+
)
96+
else:
97+
pass
98+
99+
with open("kr8s/objects.pyi", "w") as target_fh:
100+
target_fh.write(ast.unparse(tree))
101+
102+
# Run black and ruff
103+
subprocess.run(["black", "kr8s/objects.pyi"])
104+
subprocess.run(["ruff", "check", "--fix", "kr8s/objects.pyi"])

kr8s/objects.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
This module provides classes that represent Kubernetes resources.
66
These classes are used to interact with resources in the Kubernetes API server.
77
"""
8+
from __future__ import annotations
9+
810
from functools import partial
911

1012
from ._async_utils import run_sync, sync
@@ -347,8 +349,21 @@ class Table(_Table):
347349
_asyncio = False
348350

349351

350-
object_from_name_type = run_sync(partial(_object_from_name_type, _asyncio=False))
351-
objects_from_files = run_sync(partial(_objects_from_files, _asyncio=False))
352-
get_class = partial(_get_class, _asyncio=False)
353-
new_class = partial(_new_class, asyncio=False)
354-
object_from_spec = partial(_object_from_spec, _asyncio=False)
352+
def object_from_name_type(*args, **kwargs):
353+
return run_sync(partial(_object_from_name_type, _asyncio=False))(*args, **kwargs)
354+
355+
356+
def objects_from_files(*args, **kwargs):
357+
return run_sync(partial(_objects_from_files, _asyncio=False))(*args, **kwargs)
358+
359+
360+
def get_class(*args, **kwargs):
361+
return partial(_get_class, _asyncio=False)(*args, **kwargs)
362+
363+
364+
def new_class(*args, **kwargs):
365+
return partial(_new_class, asyncio=False)(*args, **kwargs)
366+
367+
368+
def object_from_spec(*args, **kwargs):
369+
return partial(_object_from_spec, _asyncio=False)(*args, **kwargs)

0 commit comments

Comments
 (0)