Skip to content

Commit 3375c27

Browse files
authored
Fix for 'generate_route_manifest' CLI (#740)
* Removed version restriction for FastAPI * Extract type information from the '_type_adapter' attribute instead of the 'type_' attribute of the path parameter ModelField instance; 'type_' is a Pydantic v1 attribute that has been removed
1 parent 383823a commit 3375c27

File tree

2 files changed

+37
-4
lines changed

2 files changed

+37
-4
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ classifiers = [
3131
dependencies = [
3232
"aiohttp",
3333
"defusedxml", # For safely parsing XML files
34-
"fastapi[standard-no-fastapi-cloud-cli]>=0.116.0,<0.128.1",
34+
"fastapi[standard-no-fastapi-cloud-cli]>=0.116.0",
3535
"pydantic>=2",
3636
"pydantic-settings",
3737
"python-jose",

src/murfey/cli/generate_route_manifest.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from argparse import ArgumentParser
1212
from pathlib import Path
1313
from types import ModuleType
14-
from typing import Any
14+
from typing import Annotated, Union, get_args, get_origin
1515

1616
import yaml
1717
from fastapi import APIRouter
@@ -20,6 +20,39 @@
2020
from murfey.cli import PrettierDumper
2121

2222

23+
def extract_base_type(annotation):
24+
"""
25+
Given a Python type annotation, return its underlying base type.
26+
27+
This function unwraps `typing.Annotated` to extract the annotated type
28+
and simplifies `Optional[T]` / `Union[T, None]` to `T`. All other union
29+
types and complex annotations are returned unchanged.
30+
31+
Parameters
32+
----------
33+
annotation:
34+
A Python type annotation (e.g. int, Annotated[int, ...], Optional[int],
35+
Union[int, str])
36+
37+
Returns
38+
-------
39+
The unwrapped base type, or the original annotation if no unambiguous base
40+
type can be determined.
41+
"""
42+
# Unwrap Annotated type annotations
43+
if get_origin(annotation) is Annotated:
44+
annotation = get_args(annotation)[0]
45+
46+
# Unwrap and return single-type Optional type annotations
47+
if get_origin(annotation) is Union:
48+
args = [a for a in get_args(annotation) if a is not type(None)]
49+
if len(args) == 1:
50+
return args[0]
51+
52+
# Return complex multi-type annotations or simple unpacked ones
53+
return annotation
54+
55+
2356
def find_routers(name: str) -> dict[str, APIRouter]:
2457
def _extract_routers_from_module(module: ModuleType):
2558
routers = {}
@@ -74,7 +107,7 @@ def get_route_manifest(routers: dict[str, APIRouter]):
74107
for route in router.routes:
75108
path_params = []
76109
for param in route.dependant.path_params:
77-
param_type = param.type_ if param.type_ is not None else Any
110+
param_type = extract_base_type(param._type_adapter._type)
78111
param_info = {
79112
"name": param.name if hasattr(param, "name") else "",
80113
"type": (
@@ -86,7 +119,7 @@ def get_route_manifest(routers: dict[str, APIRouter]):
86119
path_params.append(param_info)
87120
for route_dependency in route.dependant.dependencies:
88121
for param in route_dependency.path_params:
89-
param_type = param.type_ if param.type_ is not None else Any
122+
param_type = extract_base_type(param._type_adapter._type)
90123
param_info = {
91124
"name": param.name if hasattr(param, "name") else "",
92125
"type": (

0 commit comments

Comments
 (0)