fastapi-voyager 0.4.1__tar.gz → 0.4.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/PKG-INFO +16 -10
  2. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/README.md +15 -9
  3. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/src/fastapi_voyager/graph.py +3 -1
  4. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/src/fastapi_voyager/type_helper.py +42 -3
  5. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/src/fastapi_voyager/version.py +1 -1
  6. fastapi_voyager-0.4.3/src/fastapi_voyager/web/icon/android-chrome-192x192.png +0 -0
  7. fastapi_voyager-0.4.3/src/fastapi_voyager/web/icon/android-chrome-512x512.png +0 -0
  8. fastapi_voyager-0.4.3/src/fastapi_voyager/web/icon/apple-touch-icon.png +0 -0
  9. fastapi_voyager-0.4.3/src/fastapi_voyager/web/icon/favicon-16x16.png +0 -0
  10. fastapi_voyager-0.4.3/src/fastapi_voyager/web/icon/favicon-32x32.png +0 -0
  11. fastapi_voyager-0.4.3/src/fastapi_voyager/web/icon/favicon.ico +0 -0
  12. fastapi_voyager-0.4.3/src/fastapi_voyager/web/icon/site.webmanifest +1 -0
  13. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/src/fastapi_voyager/web/index.html +8 -1
  14. fastapi_voyager-0.4.3/tests/demo_anno.py +72 -0
  15. fastapi_voyager-0.4.3/voyager.jpg +0 -0
  16. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/.gitignore +0 -0
  17. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/LICENSE +0 -0
  18. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/pyproject.toml +0 -0
  19. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/src/fastapi_voyager/__init__.py +0 -0
  20. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/src/fastapi_voyager/cli.py +0 -0
  21. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/src/fastapi_voyager/filter.py +0 -0
  22. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/src/fastapi_voyager/module.py +0 -0
  23. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/src/fastapi_voyager/server.py +0 -0
  24. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/src/fastapi_voyager/type.py +0 -0
  25. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/src/fastapi_voyager/web/component/route-code-display.js +0 -0
  26. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/src/fastapi_voyager/web/component/schema-code-display.js +0 -0
  27. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/src/fastapi_voyager/web/component/schema-field-filter.js +0 -0
  28. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/src/fastapi_voyager/web/graph-ui.js +0 -0
  29. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/src/fastapi_voyager/web/graphviz.svg.css +0 -0
  30. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/src/fastapi_voyager/web/graphviz.svg.js +0 -0
  31. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/src/fastapi_voyager/web/quasar.min.css +0 -0
  32. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/src/fastapi_voyager/web/quasar.min.js +0 -0
  33. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/src/fastapi_voyager/web/vue-main.js +0 -0
  34. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/tests/__init__.py +0 -0
  35. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/tests/demo.py +0 -0
  36. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/tests/service/__init__.py +0 -0
  37. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/tests/service/schema.py +0 -0
  38. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/tests/test_analysis.py +0 -0
  39. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/tests/test_import.py +0 -0
  40. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/tests/test_module.py +0 -0
  41. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/tests/test_type_alias.py +0 -0
  42. {fastapi_voyager-0.4.1 → fastapi_voyager-0.4.3}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-voyager
3
- Version: 0.4.1
3
+ Version: 0.4.3
4
4
  Summary: Visualize FastAPI application's routing tree and dependencies
5
5
  Project-URL: Homepage, https://github.com/allmonday/fastapi-voyager
6
6
  Project-URL: Source, https://github.com/allmonday/fastapi-voyager
@@ -28,7 +28,9 @@ Description-Content-Type: text/markdown
28
28
  [![pypi](https://img.shields.io/pypi/v/fastapi-voyager.svg)](https://pypi.python.org/pypi/fastapi-voyager)
29
29
  ![Python Versions](https://img.shields.io/pypi/pyversions/fastapi-voyager)
30
30
 
31
- > This repo is still in early stage, currently it support pydantic v2 only, previous name: fastapi-router-viz
31
+ <p align="center"><img src="./voyager.jpg" alt="" /></p>
32
+
33
+ > This repo is still in early stage, currently it supports pydantic v2 only, previous name: fastapi-router-viz
32
34
 
33
35
  Inspect your API interactively
34
36
 
@@ -69,28 +71,32 @@ voyager -m tests.demo
69
71
  --module_color=tests.demo:tomato
70
72
  ```
71
73
 
72
- pick tag, rotue (optional) and click `generate`.
74
+ ### generate the graph
75
+ after initialization, pick tag, rotue (optional) and click `generate`.
73
76
 
74
77
  <img width="1919" height="898" alt="image" style="border: 1px solid #aaa" src="https://github.com/user-attachments/assets/05e321d0-49f3-4af6-a7c7-f4c9c6b1dbfd" />
75
78
 
79
+ ### highlight
76
80
  click a node to highlight it's upperstream and downstream nodes. figure out the related models of one page, or homw many pages are related with one model.
77
81
 
78
82
  <img width="1485" height="616" alt="image" style="border: 1px solid #aaa" src="https://github.com/user-attachments/assets/70c4095f-86c7-45da-a6f0-fd41ac645813" />
79
83
 
80
-
81
- `shift` click a node to check related nodes.
82
-
83
- pick a field to narrow the result.
84
+ ### filter related nodes
85
+ `shift` click a node to check related node, pick a field to narrow the result.
84
86
 
85
87
  <img width="1917" height="800" alt="image" style="border: 1px solid #aaa" src="https://github.com/user-attachments/assets/e770dc70-f293-49e1-bcd7-d8dffa15d9ea" />
86
88
 
89
+ ### view source code
87
90
  `alt` click a node to show source code or open file in vscode.
88
91
 
89
- <img width="497" height="402" alt="image" style="border: 1px solid #aaa" src="https://github.com/user-attachments/assets/ac81711a-d9c2-4fb1-8b3a-0f4bd1f02572" />
92
+ <img width="1049" height="694" alt="image" src="https://github.com/user-attachments/assets/7839ac83-8d60-44ad-b1c9-9652a76339b1" />
93
+
94
+ <img width="1042" height="675" alt="image" src="https://github.com/user-attachments/assets/38ae705f-5982-4a02-9c3f-038b1d00bcf6" />
95
+
96
+ `alt` click a route to show source code or open file in vscode
90
97
 
91
- more in video:
98
+ <img width="882" height="445" alt="image" src="https://github.com/user-attachments/assets/158560ef-63ca-4991-9b7d-587be4fa04e4" />
92
99
 
93
- [![IMAGE ALT TEXT](http://img.youtube.com/vi/msYsB9Cc3CA/0.jpg)](https://www.youtube.com/watch?v=msYsB9Cc3CA "Unity Snake Game")
94
100
 
95
101
 
96
102
  ## Command Line Usage
@@ -1,7 +1,9 @@
1
1
  [![pypi](https://img.shields.io/pypi/v/fastapi-voyager.svg)](https://pypi.python.org/pypi/fastapi-voyager)
2
2
  ![Python Versions](https://img.shields.io/pypi/pyversions/fastapi-voyager)
3
3
 
4
- > This repo is still in early stage, currently it support pydantic v2 only, previous name: fastapi-router-viz
4
+ <p align="center"><img src="./voyager.jpg" alt="" /></p>
5
+
6
+ > This repo is still in early stage, currently it supports pydantic v2 only, previous name: fastapi-router-viz
5
7
 
6
8
  Inspect your API interactively
7
9
 
@@ -42,28 +44,32 @@ voyager -m tests.demo
42
44
  --module_color=tests.demo:tomato
43
45
  ```
44
46
 
45
- pick tag, rotue (optional) and click `generate`.
47
+ ### generate the graph
48
+ after initialization, pick tag, rotue (optional) and click `generate`.
46
49
 
47
50
  <img width="1919" height="898" alt="image" style="border: 1px solid #aaa" src="https://github.com/user-attachments/assets/05e321d0-49f3-4af6-a7c7-f4c9c6b1dbfd" />
48
51
 
52
+ ### highlight
49
53
  click a node to highlight it's upperstream and downstream nodes. figure out the related models of one page, or homw many pages are related with one model.
50
54
 
51
55
  <img width="1485" height="616" alt="image" style="border: 1px solid #aaa" src="https://github.com/user-attachments/assets/70c4095f-86c7-45da-a6f0-fd41ac645813" />
52
56
 
53
-
54
- `shift` click a node to check related nodes.
55
-
56
- pick a field to narrow the result.
57
+ ### filter related nodes
58
+ `shift` click a node to check related node, pick a field to narrow the result.
57
59
 
58
60
  <img width="1917" height="800" alt="image" style="border: 1px solid #aaa" src="https://github.com/user-attachments/assets/e770dc70-f293-49e1-bcd7-d8dffa15d9ea" />
59
61
 
62
+ ### view source code
60
63
  `alt` click a node to show source code or open file in vscode.
61
64
 
62
- <img width="497" height="402" alt="image" style="border: 1px solid #aaa" src="https://github.com/user-attachments/assets/ac81711a-d9c2-4fb1-8b3a-0f4bd1f02572" />
65
+ <img width="1049" height="694" alt="image" src="https://github.com/user-attachments/assets/7839ac83-8d60-44ad-b1c9-9652a76339b1" />
66
+
67
+ <img width="1042" height="675" alt="image" src="https://github.com/user-attachments/assets/38ae705f-5982-4a02-9c3f-038b1d00bcf6" />
68
+
69
+ `alt` click a route to show source code or open file in vscode
63
70
 
64
- more in video:
71
+ <img width="882" height="445" alt="image" src="https://github.com/user-attachments/assets/158560ef-63ca-4991-9b7d-587be4fa04e4" />
65
72
 
66
- [![IMAGE ALT TEXT](http://img.youtube.com/vi/msYsB9Cc3CA/0.jpg)](https://www.youtube.com/watch?v=msYsB9Cc3CA "Unity Snake Game")
67
73
 
68
74
 
69
75
  ## Command Line Usage
@@ -8,7 +8,8 @@ from fastapi_voyager.type_helper import (
8
8
  is_inheritance_of_pydantic_base,
9
9
  get_pydantic_fields,
10
10
  get_vscode_link,
11
- get_source
11
+ get_source,
12
+ update_forward_refs
12
13
  )
13
14
  from pydantic import BaseModel
14
15
  from fastapi_voyager.type import Route, SchemaNode, Link, Tag, ModuleNode
@@ -180,6 +181,7 @@ class Analytics:
180
181
  3. recursively run walk_schema
181
182
  """
182
183
 
184
+ update_forward_refs(schema)
183
185
  self.add_to_node_set(schema)
184
186
 
185
187
  # handle schema inside ensure_subset(schema)
@@ -1,9 +1,10 @@
1
1
  import inspect
2
2
  import os
3
3
  from pydantic import BaseModel
4
- from typing import get_origin, get_args, Union, Annotated, Any
4
+ from typing import get_origin, get_args, Union, Annotated, Any, Type
5
5
  from fastapi_voyager.type import FieldInfo
6
6
  from types import UnionType
7
+ import pydantic_resolve.constant as const
7
8
 
8
9
  # Python <3.12 compatibility: TypeAliasType exists only from 3.12 (PEP 695)
9
10
  try: # pragma: no cover - import guard
@@ -149,8 +150,9 @@ def get_type_name(anno):
149
150
 
150
151
  return name_of(anno)
151
152
 
153
+
152
154
  def is_inheritance_of_pydantic_base(cls):
153
- return issubclass(cls, BaseModel) and cls is not BaseModel
155
+ return issubclass(cls, BaseModel) and cls is not BaseModel
154
156
 
155
157
 
156
158
  def get_bases_fields(schemas: list[type[BaseModel]]) -> set[str]:
@@ -229,4 +231,41 @@ def get_source(kls):
229
231
  source = inspect.getsource(kls)
230
232
  return source
231
233
  except Exception:
232
- return "failed to get source"
234
+ return "failed to get source"
235
+
236
+
237
+ def safe_issubclass(kls, classinfo):
238
+ try:
239
+ return issubclass(kls, classinfo)
240
+ except TypeError:
241
+ return False
242
+
243
+
244
+ def update_forward_refs(kls):
245
+ def update_pydantic_forward_refs(kls2: Type[BaseModel]):
246
+ """
247
+ recursively update refs.
248
+ """
249
+
250
+ kls2.model_rebuild()
251
+ setattr(kls2, const.PYDANTIC_FORWARD_REF_UPDATED, True)
252
+
253
+ values = kls2.model_fields.values()
254
+ for field in values:
255
+ update_forward_refs(field.annotation)
256
+
257
+ if safe_issubclass(kls, BaseModel):
258
+ kls.model_rebuild()
259
+ setattr(kls, const.PYDANTIC_FORWARD_REF_UPDATED, True)
260
+
261
+ for shelled_type in get_core_types(kls):
262
+ if getattr(shelled_type, const.PYDANTIC_FORWARD_REF_UPDATED, False):
263
+ continue
264
+ if safe_issubclass(shelled_type, BaseModel):
265
+ update_pydantic_forward_refs(shelled_type)
266
+
267
+ if __name__ == "__main__":
268
+ from tests.demo_anno import PageSprint, PageOverall
269
+
270
+ update_forward_refs(PageOverall)
271
+ update_forward_refs(PageSprint)
@@ -1,2 +1,2 @@
1
1
  __all__ = ["__version__"]
2
- __version__ = "0.4.1"
2
+ __version__ = "0.4.3"
@@ -0,0 +1 @@
1
+ {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
@@ -1,8 +1,15 @@
1
1
  <html>
2
2
  <head>
3
- <title>FastAPI Router Viz</title>
3
+ <title>FastAPI Voyager</title>
4
+ <meta name="theme-color" content="#ffffff" />
4
5
  <link rel="stylesheet" href="/static/graphviz.svg.css" />
5
6
  <link rel="stylesheet" href="/static/quasar.min.css" />
7
+ <!-- App Icons / Favicons -->
8
+ <link rel="apple-touch-icon" sizes="180x180" href="/static/icon/apple-touch-icon.png" />
9
+ <link rel="icon" type="image/png" sizes="32x32" href="/static/icon/favicon-32x32.png" />
10
+ <link rel="icon" type="image/png" sizes="16x16" href="/static/icon/favicon-16x16.png" />
11
+ <link rel="icon" href="/static/icon/favicon.ico" sizes="any" />
12
+ <link rel="manifest" href="/static/icon/site.webmanifest" />
6
13
  <link
7
14
  href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
8
15
  rel="stylesheet"
@@ -0,0 +1,72 @@
1
+ from __future__ import annotations
2
+ from pydantic import BaseModel, Field
3
+ from fastapi import FastAPI
4
+ from typing import Optional
5
+ from pydantic_resolve import ensure_subset, Resolver
6
+ from tests.service.schema import Story, Task
7
+ import tests.service.schema as serv
8
+
9
+ app = FastAPI(title="Demo API", description="A demo FastAPI application for router visualization")
10
+
11
+ @app.get("/sprints", tags=['for-restapi'], response_model=list[serv.Sprint])
12
+ def get_sprint():
13
+ return []
14
+
15
+ class Tree(BaseModel):
16
+ id: int
17
+ name: str
18
+ children: list[Tree] = []
19
+
20
+ class PageMember(serv.Member):
21
+ fullname: str = ''
22
+ def post_fullname(self):
23
+ return self.first_name + ' ' + self.last_name
24
+
25
+ class TaskA(Task):
26
+ task_type: str = 'A'
27
+
28
+ class TaskB(Task):
29
+ task_type: str = 'B'
30
+
31
+
32
+ type TaskUnion = TaskA | TaskB
33
+ class PageTask(Task):
34
+ owner: Optional[PageMember]
35
+
36
+
37
+ class PageOverall(BaseModel):
38
+ sprints: list[PageSprint]
39
+
40
+ class PageSprint(serv.Sprint):
41
+ stories: list[PageStory]
42
+ owner: Optional[PageMember] = None
43
+
44
+
45
+ @ensure_subset(Story)
46
+ class PageStory(BaseModel):
47
+ id: int
48
+ sprint_id: int
49
+ title: str = Field(exclude=True)
50
+
51
+ desc: str = ''
52
+ def post_desc(self):
53
+ return self.title + ' (processed)'
54
+
55
+ tasks: list[PageTask] = []
56
+ owner: Optional[PageMember] = None
57
+ union_tasks: list[TaskUnion] = []
58
+
59
+ tree: Optional[Tree] = None
60
+
61
+ @app.get("/page_overall", tags=['for-page'], response_model=PageOverall)
62
+ async def get_page_info():
63
+ page_overall = PageOverall(sprints=[]) # focus on schema only
64
+ return await Resolver().resolve(page_overall)
65
+
66
+
67
+ class PageStories(BaseModel):
68
+ stories: list[PageStory]
69
+
70
+ @app.get("/page_info/", tags=['for-page'], response_model=PageStories)
71
+ def get_page_stories():
72
+ return {} # no implementation
Binary file
File without changes
File without changes