fastapi-voyager 0.7.5__py3-none-any.whl → 0.8.1__py3-none-any.whl

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.
fastapi_voyager/render.py CHANGED
@@ -104,7 +104,7 @@ class Renderer:
104
104
  inner_nodes = [
105
105
  f'''
106
106
  "{r.id}" [
107
- label = " {r.name} "
107
+ label = " {r.name}: {r.response_schema} "
108
108
  margin="0.5,0.1"
109
109
  shape = "record"
110
110
  ];''' for r in mod.routes
fastapi_voyager/server.py CHANGED
@@ -32,7 +32,8 @@ class Payload(BaseModel):
32
32
  route_name: Optional[str] = None
33
33
  show_fields: str = 'object'
34
34
  show_meta: bool = False
35
- brief: bool = False
35
+ brief: bool = False,
36
+ hide_primitive_route: bool = False,
36
37
 
37
38
  def create_route(
38
39
  target_app: FastAPI,
@@ -73,6 +74,7 @@ def create_route(
73
74
  module_color=module_color,
74
75
  route_name=payload.route_name,
75
76
  load_meta=False,
77
+ hide_primitive_route=payload.hide_primitive_route,
76
78
  )
77
79
  voyager.analysis(target_app)
78
80
  if payload.brief:
fastapi_voyager/type.py CHANGED
@@ -24,6 +24,8 @@ class Route(NodeBase):
24
24
  module: str
25
25
  source_code: str = ''
26
26
  vscode_link: str = '' # optional vscode deep link
27
+ response_schema: str = ''
28
+ is_primitive: bool = True
27
29
 
28
30
  @dataclass
29
31
  class ModuleRoute:
@@ -276,6 +276,12 @@ def is_generic_container(cls):
276
276
  return (hasattr(cls, '__bases__') and Generic in cls.__bases__ and (hasattr(cls, '__parameters__') and bool(cls.__parameters__)))
277
277
  except (TypeError, AttributeError):
278
278
  return False
279
+
280
+ def is_non_pydantic_type(tp):
281
+ for schema in get_core_types(tp):
282
+ if schema and issubclass(schema, BaseModel):
283
+ return False
284
+ return True
279
285
 
280
286
  if __name__ == "__main__":
281
287
  from tests.demo_anno import PageSprint, PageOverall
@@ -1,2 +1,2 @@
1
1
  __all__ = ["__version__"]
2
- __version__ = "0.7.5"
2
+ __version__ = "0.8.1"
@@ -8,7 +8,9 @@ from fastapi_voyager.type_helper import (
8
8
  get_pydantic_fields,
9
9
  get_vscode_link,
10
10
  get_source,
11
- update_forward_refs
11
+ get_type_name,
12
+ update_forward_refs,
13
+ is_non_pydantic_type
12
14
  )
13
15
  from pydantic import BaseModel
14
16
  from fastapi_voyager.type import Route, SchemaNode, Link, Tag, LinkType, FieldType, PK, CoreData
@@ -26,6 +28,7 @@ class Voyager:
26
28
  include_tags: list[str] | None = None,
27
29
  module_color: dict[str, str] | None = None,
28
30
  route_name: str | None = None,
31
+ hide_primitive_route: bool = False,
29
32
  load_meta: bool = False
30
33
  ):
31
34
 
@@ -48,6 +51,7 @@ class Voyager:
48
51
  self.module_color = module_color or {}
49
52
  self.route_name = route_name
50
53
  self.load_meta = load_meta
54
+ self.hide_primitive_route = hide_primitive_route
51
55
 
52
56
 
53
57
  def _get_available_route(self, app: FastAPI):
@@ -88,29 +92,37 @@ class Voyager:
88
92
  # filter by route_name (route.id) if provided
89
93
  if self.route_name is not None and route_id != self.route_name:
90
94
  continue
95
+ is_primitive_response = is_non_pydantic_type(route.response_model)
96
+
97
+ # filter primitive route if needed
98
+ if self.hide_primitive_route and is_primitive_response:
99
+ continue
100
+
101
+ self.links.append(Link(
102
+ source=tag_id,
103
+ source_origin=tag_id,
104
+ target=route_id,
105
+ target_origin=route_id,
106
+ type='tag_route'
107
+ ))
91
108
 
92
109
  route_obj = Route(
93
110
  id=route_id,
94
111
  name=route_name,
95
112
  module=route_module,
96
113
  vscode_link=get_vscode_link(route.endpoint) if self.load_meta else '',
97
- source_code=inspect.getsource(route.endpoint) if self.load_meta else ''
114
+ source_code=inspect.getsource(route.endpoint) if self.load_meta else '',
115
+ response_schema=get_type_name(route.response_model),
116
+ is_primitive=is_primitive_response
98
117
  )
99
-
100
118
  self.routes.append(route_obj)
101
119
  # add route into current tag
102
120
  self.tag_set[tag_id].routes.append(route_obj)
103
- self.links.append(Link(
104
- source=tag_id,
105
- source_origin=tag_id,
106
- target=route_id,
107
- target_origin=route_id,
108
- type='tag_route'
109
- ))
110
121
 
111
122
  # add response_models and create links from route -> response_model
112
123
  for schema in get_core_types(route.response_model):
113
124
  if schema and issubclass(schema, BaseModel):
125
+ is_primitive_response = False
114
126
  target_name = full_class_name(schema)
115
127
  self.links.append(Link(
116
128
  source=route_id,
@@ -88,6 +88,9 @@
88
88
  "
89
89
  >
90
90
  <div class="row items-center q-col-gutter-md">
91
+ <div class="col-auto">
92
+ <div style="width: 282px; margin-left: 8px;" class="text-h6">FastAPI Voyager</div>
93
+ </div>
91
94
  <div class="col-auto">
92
95
  <div class="column">
93
96
  <q-option-group
@@ -101,9 +104,6 @@
101
104
  </div>
102
105
  </div>
103
106
 
104
- <div class="col-auto">
105
- <q-toggle v-model="state.brief" label="Brief" dense />
106
- </div>
107
107
 
108
108
  <!-- <div class="col-auto">
109
109
  <q-btn-dropdown
@@ -126,6 +126,10 @@
126
126
  </div> -->
127
127
 
128
128
  <div class="col-auto q-ml-auto">
129
+ <q-toggle class="q-ml-md" v-model="state.brief" label="Brief Mode" title="config module_prefix to enable it" dense />
130
+ <q-toggle class="q-ml-md" v-model="state.hidePrimitiveRoute" label="Hide Primitive Routes" title="" dense />
131
+ </div>
132
+ <div class="col-auto">
129
133
  <q-btn outline @click="onReset" title="may be very slow" label="Show All" />
130
134
  </div>
131
135
  <div class="col-auto">
@@ -184,21 +188,20 @@
184
188
  v-for="tag in state.rawTags"
185
189
  :key="tag.name"
186
190
  expand-separator
187
- switch-toggle-side
188
191
  :model-value="state.tag === tag.name"
189
192
  @update:model-value="(val) => toggleTag(tag.name, val)"
190
- :header-class="state.tag === tag.name ? 'bg-primary text-white text-weight-medium' : 'text-weight-medium'"
193
+ :header-class="state.tag === tag.name ? 'text-primary text-bold' : ''"
191
194
  content-class="q-pa-none"
192
195
  >
193
196
  <template #header>
194
- <div class="row items-center no-wrap" style="width: 100%">
195
- <div class="col text-body2">{{ tag.name }}</div>
197
+ <div class="row items-center" style="width: 100%">
198
+ <div class="">{{ tag.name }}</div>
196
199
  </div>
197
200
  </template>
198
201
 
199
202
  <q-list separator>
200
203
  <q-item
201
- v-for="route in (tag.routes || [])"
204
+ v-for="route in (state.hidePrimitiveRoute ? tag.routes.filter(r => !r.is_primitive) :tag.routes || [])"
202
205
  :key="route.id"
203
206
  clickable
204
207
  v-ripple
@@ -206,7 +209,9 @@
206
209
  active-class=""
207
210
  @click="selectRoute(route.id)"
208
211
  >
209
- <q-item-section>{{ route.name }}</q-item-section>
212
+ <q-item-section>
213
+ <span class="q-ml-sm"> <span style="font-weight: bold;">⋅</span> &nbsp; {{ route.name }}</span>
214
+ </q-item-section>
210
215
  </q-item>
211
216
  <q-item v-if="!tag.routes || tag.routes.length === 0" dense>
212
217
  <q-item-section class="text-grey-6">No routes</q-item-section>
@@ -23,6 +23,7 @@ const app = createApp({
23
23
  { label: "All fields", value: "all" },
24
24
  ],
25
25
  brief: false,
26
+ hidePrimitiveRoute: false,
26
27
  generating: false,
27
28
  rawTags: [], // [{ name, routes: [{ id, name }] }]
28
29
  rawSchemas: [], // [{ name, fullname }]
@@ -51,20 +52,6 @@ const app = createApp({
51
52
  showDetail.value = false;
52
53
  }
53
54
 
54
- const skipNextRouteGenerate = ref(false);
55
-
56
- function applyRoutesForTag(tagName) {
57
- const tag = state.rawTags.find((t) => t.name === tagName);
58
- state.routeOptions = [];
59
- if (tag && Array.isArray(tag.routes)) {
60
- state.routeOptions.push(
61
- ...tag.routes.map((r) => ({ label: r.name, value: r.id }))
62
- );
63
- }
64
- skipNextRouteGenerate.value = true;
65
- state.routeId = "";
66
- }
67
-
68
55
  function onFilterTags(val, update) {
69
56
  const normalized = (val || "").toLowerCase();
70
57
  update(() => {
@@ -137,6 +124,7 @@ const app = createApp({
137
124
  route_name: state.routeId || null,
138
125
  show_fields: state.showFields,
139
126
  brief: state.brief,
127
+ hide_primitive_route: state.hidePrimitiveRoute
140
128
  };
141
129
 
142
130
  const res = await fetch("dot", {
@@ -257,44 +245,45 @@ const app = createApp({
257
245
  }
258
246
 
259
247
  function selectRoute(routeId) {
260
- state.routeId = state.routeId === routeId ? "" : routeId;
248
+ if (state.routeId === routeId) {
249
+ state.routeId = ''
250
+ } else {
251
+ state.routeId = routeId
252
+ }
253
+ onGenerate()
261
254
  }
262
255
 
263
256
  // react to tag changes to rebuild routes
264
257
  watch(
265
258
  () => state.tag,
266
259
  (val) => {
267
- applyRoutesForTag(val);
268
260
  if (!state.initializing) {
261
+ state.routeId = ''
269
262
  onGenerate();
270
263
  }
271
264
  }
272
265
  );
273
266
 
274
267
  watch(
275
- () => state.routeId,
268
+ () => state.showFields,
276
269
  () => {
277
- if (skipNextRouteGenerate.value) {
278
- skipNextRouteGenerate.value = false;
279
- return;
280
- }
281
270
  if (!state.initializing) {
282
271
  onGenerate();
283
272
  }
284
273
  }
285
- );
274
+ )
286
275
 
287
276
  watch(
288
- () => state.showFields,
277
+ () => state.brief,
289
278
  () => {
290
279
  if (!state.initializing) {
291
280
  onGenerate();
292
281
  }
293
282
  }
294
- )
283
+ );
295
284
 
296
285
  watch(
297
- () => state.brief,
286
+ () => state.hidePrimitiveRoute,
298
287
  () => {
299
288
  if (!state.initializing) {
300
289
  onGenerate();
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-voyager
3
- Version: 0.7.5
3
+ Version: 0.8.1
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
@@ -44,6 +44,9 @@ pip install fastapi-voyager
44
44
  uv add fastapi-voyager
45
45
  ```
46
46
 
47
+ ```shell
48
+ voyager -m path.to.your.app.module --server
49
+ ```
47
50
 
48
51
  ## Dependencies
49
52
 
@@ -98,6 +101,21 @@ click a node to highlight it's upperstream and downstream nodes. figure out the
98
101
  <img width="882" height="445" alt="image" src="https://github.com/user-attachments/assets/158560ef-63ca-4991-9b7d-587be4fa04e4" />
99
102
 
100
103
 
104
+ ## Mount to target project
105
+
106
+ ```python
107
+ from fastapi import FastAPI
108
+ from fastapi_voyager.server import create_app_with_fastapi
109
+ from tests.demo import app
110
+
111
+ app.mount('/voyager', create_app_with_fastapi(
112
+ app,
113
+ module_color={"tests.service": "red"},
114
+ module_prefix="tests.service"))
115
+ ```
116
+
117
+ more about [sub application](https://fastapi.tiangolo.com/advanced/sub-applications/?h=sub)
118
+
101
119
 
102
120
  ## Command Line Usage
103
121
 
@@ -170,9 +188,11 @@ features:
170
188
  - [ ] click field to highlight links
171
189
  - [ ] user can generate nodes/edges manually and connect to generated ones
172
190
  - [ ] add owner
191
+ - [ ] add extra info for schema
173
192
  - [ ] ui optimization
174
193
  - [ ] fixed left/right bar show field information
175
- - [ ] display standard ER diagram `difficult`
194
+ - [x] fixed left bar show tag/ route
195
+ - [ ] display standard ER diagram `difference`
176
196
  - [ ] display potential invalid links
177
197
  - [ ] integration with pydantic-resolve
178
198
  - [ ] show difference between resolve, post fields
@@ -181,12 +201,14 @@ features:
181
201
  - [x] export voyager core data into json (for better debugging)
182
202
  - [x] add api to rebuild core data from json, and render it
183
203
  - [x] fix Generic case `test_generic.py`
204
+ - [ ] show tips for routes not return pydantic type.
205
+ - [ ] add http method for route
184
206
 
185
207
  bugs & non feature:
186
208
  - [x] fix duplicated link from class and parent class, it also break clicking highlight
187
209
  - [ ] add tests
188
- - [ ] refactor
189
- - [ ] abstract render module
210
+ - [x] refactor
211
+ - [x] abstract render module
190
212
 
191
213
  ## Using with pydantic-resolve
192
214
 
@@ -203,6 +225,9 @@ TODO: ...
203
225
 
204
226
  ## Changelog
205
227
 
228
+ - 0.8:
229
+ - 0.8.1
230
+ - add feature: hide primitive routes
206
231
  - 0.7:
207
232
  - 0.7.5
208
233
  - fix show all display issue
@@ -2,19 +2,19 @@ fastapi_voyager/__init__.py,sha256=E5WTV_sYs2LK8I6jzA7AuvFU5a8_vjnDseC3DMha0iQ,1
2
2
  fastapi_voyager/cli.py,sha256=2eixX7mtPsZvukc4vrwQOt6XTPJgHUKIGLBy3IIC2jE,11127
3
3
  fastapi_voyager/filter.py,sha256=2Yt37o8mhqSqleafO4YRrumh_ExYUqzXFOxQRPuTbAc,8078
4
4
  fastapi_voyager/module.py,sha256=Z2QHNmiLk6ZAJlm2nSmO875Q33TweSg8UxZSzIpU9zY,3499
5
- fastapi_voyager/render.py,sha256=rOJEKqiIAPowa3kZheRIgZMnbNzHQz9QoJM5aWUfXXs,7744
6
- fastapi_voyager/server.py,sha256=UT-fHggdqicIo5m3uUX86-XFhAVDLXpXBsBQwd1HdIg,4001
7
- fastapi_voyager/type.py,sha256=nad4WNxTcZFi7Mskw6p2W7v2Gs4f0giVLNoFjZlKmbA,1778
8
- fastapi_voyager/type_helper.py,sha256=f2Gy5r3Zi6a2wTkbqU9W-AkvcetajEYCfroACzcIcVY,9064
9
- fastapi_voyager/version.py,sha256=6qBRnYL6PIB365jAxd_3kCArchSbLRDY1CXzkj2QbvA,48
10
- fastapi_voyager/voyager.py,sha256=uOQEzrs3o6UUUswvHGRKELNWYUH1ix0Z7SbzMHwm320,10709
5
+ fastapi_voyager/render.py,sha256=qy3g1Rz1s8XkuR_6n1Q1YPwy_oMOjWjNswTHQjdz4N0,7765
6
+ fastapi_voyager/server.py,sha256=t0Qby5No2XIVPPOn_wcJDOasJ05V7pQ4xtTDI8lgPSQ,4093
7
+ fastapi_voyager/type.py,sha256=k62cDofqmy-2q5rInW5LydLaRuDpKLsdbapps8Osqys,1838
8
+ fastapi_voyager/type_helper.py,sha256=hjBC4E0tgBpQDlYxGg74uK07SXjsrAgictEETJfIpYM,9231
9
+ fastapi_voyager/version.py,sha256=_cyxd8_oDXTOvUSRCStr2n0IhvzG9mtX5ww-UZQFRbg,48
10
+ fastapi_voyager/voyager.py,sha256=sy2chniANvZ5e4CjZzANTrA9RJcvr4aNoNygvusb5wc,11262
11
11
  fastapi_voyager/web/graph-ui.js,sha256=eEjDnJVMvk35LdRoxcqX_fZxLFS9_bUrGAZL6K2O5C0,4176
12
12
  fastapi_voyager/web/graphviz.svg.css,sha256=zDCjjpT0Idufu5YOiZI76PL70-avP3vTyzGPh9M85Do,1563
13
13
  fastapi_voyager/web/graphviz.svg.js,sha256=lvAdbjHc-lMSk4GQp-iqYA2PCFX4RKnW7dFaoe0LUHs,16005
14
- fastapi_voyager/web/index.html,sha256=oTGaZmYqqCdlaX2EtSm_nYzYstvvV5MD_YUxUcMm29s,12331
14
+ fastapi_voyager/web/index.html,sha256=iv4Fd0RsX854ZWrfSnVn-369_ky9ubFlK36vLiAbd0w,12760
15
15
  fastapi_voyager/web/quasar.min.css,sha256=F5jQe7X2XT54VlvAaa2V3GsBFdVD-vxDZeaPLf6U9CU,203145
16
16
  fastapi_voyager/web/quasar.min.js,sha256=h0ftyPMW_CRiyzeVfQqiup0vrVt4_QWojpqmpnpn07E,502974
17
- fastapi_voyager/web/vue-main.js,sha256=nnl5OgGdua9jysqwWXknBIjGeZV2GF2ICxxL47kpsR0,9900
17
+ fastapi_voyager/web/vue-main.js,sha256=f4re2Hys0FOli3R_AZ_byUd08YLScDZ01xf1hXeoHCA,9529
18
18
  fastapi_voyager/web/component/render-graph.js,sha256=e8Xgh2Kl-nYU0P1gstEmAepCgFnk2J6UdxW8TlMafGs,2322
19
19
  fastapi_voyager/web/component/route-code-display.js,sha256=NECC1OGcPCdDfbghtRJEnmFM6HmH5J3win2ibapWPeA,2649
20
20
  fastapi_voyager/web/component/schema-code-display.js,sha256=oOusgTvCaWGnoKb-NBwu0SXqJJf2PTUtp3lUczokTBM,5515
@@ -26,8 +26,8 @@ fastapi_voyager/web/icon/favicon-16x16.png,sha256=JC07jEzfIYxBIoQn_FHXvyHuxESdhW
26
26
  fastapi_voyager/web/icon/favicon-32x32.png,sha256=C7v1h58cfWOsiLp9yOIZtlx-dLasBcq3NqpHVGRmpt4,1859
27
27
  fastapi_voyager/web/icon/favicon.ico,sha256=tZolYIXkkBcFiYl1A8ksaXN2VjGamzcSdes838dLvNc,15406
28
28
  fastapi_voyager/web/icon/site.webmanifest,sha256=ep4Hzh9zhmiZF2At3Fp1dQrYQuYF_3ZPZxc1KcGBvwQ,263
29
- fastapi_voyager-0.7.5.dist-info/METADATA,sha256=mUQ-9OpixGnInICIvRV-1yQOsN8Qi3hQJI2xR0Tud2c,7372
30
- fastapi_voyager-0.7.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
31
- fastapi_voyager-0.7.5.dist-info/entry_points.txt,sha256=pEIKoUnIDXEtdMBq8EmXm70m16vELIu1VPz9-TBUFWM,53
32
- fastapi_voyager-0.7.5.dist-info/licenses/LICENSE,sha256=lNVRR3y_bFVoFKuK2JM8N4sFaj3m-7j29kvL3olFi5Y,1067
33
- fastapi_voyager-0.7.5.dist-info/RECORD,,
29
+ fastapi_voyager-0.8.1.dist-info/METADATA,sha256=1t2J9aFVGAkBjY9dB_mnHsK8BUQJ2dnL6kycUX_PDhI,8044
30
+ fastapi_voyager-0.8.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
31
+ fastapi_voyager-0.8.1.dist-info/entry_points.txt,sha256=pEIKoUnIDXEtdMBq8EmXm70m16vELIu1VPz9-TBUFWM,53
32
+ fastapi_voyager-0.8.1.dist-info/licenses/LICENSE,sha256=lNVRR3y_bFVoFKuK2JM8N4sFaj3m-7j29kvL3olFi5Y,1067
33
+ fastapi_voyager-0.8.1.dist-info/RECORD,,