|
10 | 10 | from rich.style import Style |
11 | 11 |
|
12 | 12 |
|
13 | | -def _resolve_redirect(obj: griffe.Object | griffe.Alias) -> griffe.Object | griffe.Alias | None: |
14 | | - """Follow back-compat redirections to a real class definition. |
15 | | -
|
16 | | - We use two patterns to preserve import paths after a schema rename: |
17 | | - * `from .new_path import NewClass` → `griffe.Alias` |
18 | | - * `OldName = NewClass` (module-level assignment) → `griffe.Attribute` |
19 | | - Both are runtime-equivalent re-exports and should expose the target |
20 | | - class's attributes for breaking-change detection. Without resolving them, |
21 | | - griffe reports every old attribute as removed. |
22 | | - """ |
23 | | - visited: set[int] = set() |
24 | | - current: griffe.Object | griffe.Alias = obj |
25 | | - while id(current) not in visited: |
26 | | - visited.add(id(current)) |
27 | | - if isinstance(current, griffe.Alias): |
28 | | - try: |
29 | | - return current.final_target |
30 | | - except Exception: |
31 | | - return None |
32 | | - if isinstance(current, griffe.Attribute): |
33 | | - value = current.value |
34 | | - parent = current.parent |
35 | | - if parent is None or value is None: |
36 | | - return None |
37 | | - if isinstance(value, griffe.ExprName): |
38 | | - next_obj = parent.members.get(str(value)) |
39 | | - if next_obj is None: |
40 | | - return None |
41 | | - current = next_obj |
42 | | - continue |
43 | | - if isinstance(value, griffe.ExprAttribute): |
44 | | - # Qualified path like `task_group_status.TaskGroupStatus`. |
45 | | - # Walk segment by segment, resolving any module aliases as we go. |
46 | | - next_obj = parent |
47 | | - for segment in value.values: |
48 | | - if not isinstance(segment, griffe.ExprName): |
49 | | - continue |
50 | | - if isinstance(next_obj, griffe.Alias): |
51 | | - try: |
52 | | - next_obj = next_obj.final_target |
53 | | - except Exception: |
54 | | - return None |
55 | | - if next_obj is None or not hasattr(next_obj, "members"): |
56 | | - return None |
57 | | - next_obj = next_obj.members.get(str(segment)) |
58 | | - if next_obj is None: |
59 | | - return None |
60 | | - current = next_obj |
61 | | - continue |
62 | | - return None |
63 | | - return current |
64 | | - return None |
65 | | - |
66 | | - |
67 | 13 | def public_members(obj: griffe.Object | griffe.Alias) -> dict[str, griffe.Object | griffe.Alias]: |
68 | | - target = _resolve_redirect(obj) |
69 | | - if target is not None and target is not obj: |
70 | | - obj = target |
71 | | - |
72 | 14 | if isinstance(obj, griffe.Alias): |
73 | | - # Truly opaque alias we couldn't resolve. |
| 15 | + # ignore imports for now, they're technically part of the public API |
| 16 | + # but we don't have good preventative measures in place to prevent |
| 17 | + # changing them |
74 | 18 | return {} |
75 | 19 |
|
76 | 20 | return {name: value for name, value in obj.all_members.items() if not name.startswith("_")} |
|
0 commit comments