fastapi-voyager 0.12.2__py3-none-any.whl → 0.12.4__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.
@@ -2,8 +2,7 @@
2
2
 
3
3
  Utilities to introspect a FastAPI application and visualize its routing tree.
4
4
  """
5
- from .version import __version__ # noqa: F401
6
-
7
5
  from .server import create_voyager
6
+ from .version import __version__ # noqa: F401
8
7
 
9
8
  __all__ = ["__version__", "create_voyager"]
fastapi_voyager/cli.py CHANGED
@@ -5,9 +5,9 @@ import importlib.util
5
5
  import logging
6
6
  import os
7
7
  import sys
8
- from typing import Optional
9
8
 
10
9
  from fastapi import FastAPI
10
+
11
11
  from fastapi_voyager import server as viz_server
12
12
  from fastapi_voyager.version import __version__
13
13
  from fastapi_voyager.voyager import Voyager
@@ -15,7 +15,7 @@ from fastapi_voyager.voyager import Voyager
15
15
  logger = logging.getLogger(__name__)
16
16
 
17
17
 
18
- def load_fastapi_app_from_file(module_path: str, app_name: str = "app") -> Optional[FastAPI]:
18
+ def load_fastapi_app_from_file(module_path: str, app_name: str = "app") -> FastAPI | None:
19
19
  """Load FastAPI app from a Python module file."""
20
20
  try:
21
21
  # Convert relative path to absolute path
@@ -47,7 +47,7 @@ def load_fastapi_app_from_file(module_path: str, app_name: str = "app") -> Optio
47
47
  return None
48
48
 
49
49
 
50
- def load_fastapi_app_from_module(module_name: str, app_name: str = "app") -> Optional[FastAPI]:
50
+ def load_fastapi_app_from_module(module_name: str, app_name: str = "app") -> FastAPI | None:
51
51
  """Load FastAPI app from a Python module name."""
52
52
  try:
53
53
  # Temporarily add the current working directory to sys.path
fastapi_voyager/filter.py CHANGED
@@ -1,7 +1,8 @@
1
1
  from __future__ import annotations
2
+
2
3
  from collections import deque
3
- from typing import Tuple
4
- from fastapi_voyager.type import Tag, Route, SchemaNode, Link, PK
4
+
5
+ from fastapi_voyager.type import PK, Link, Route, SchemaNode, Tag
5
6
 
6
7
 
7
8
  def filter_graph(
@@ -13,7 +14,7 @@ def filter_graph(
13
14
  nodes: list[SchemaNode],
14
15
  links: list[Link],
15
16
  node_set: dict[str, SchemaNode],
16
- ) -> Tuple[list[Tag], list[Route], list[SchemaNode], list[Link]]:
17
+ ) -> tuple[list[Tag], list[Route], list[SchemaNode], list[Link]]:
17
18
  """Filter tags, routes, schema nodes and links based on a target schema and optional field.
18
19
 
19
20
  Behaviour summary (mirrors previous Analytics.filter_nodes_and_schemas_based_on_schemas):
@@ -113,7 +114,7 @@ def filter_subgraph_by_module_prefix(
113
114
  links: list[Link],
114
115
  nodes: list[SchemaNode],
115
116
  module_prefix: str
116
- ) -> Tuple[list[Tag], list[Route], list[SchemaNode], list[Link]]:
117
+ ) -> tuple[list[Tag], list[Route], list[SchemaNode], list[Link]]:
117
118
  """Collapse schema graph so routes link directly to nodes whose module matches ``module_prefix``.
118
119
 
119
120
  The routine keeps tag→route links untouched, prunes schema nodes whose module does not start
@@ -202,7 +203,7 @@ def filter_subgraph_from_tag_to_schema_by_module_prefix(
202
203
  links: list[Link],
203
204
  nodes: list[SchemaNode],
204
205
  module_prefix: str
205
- ) -> Tuple[list[Tag], list[Route], list[SchemaNode], list[Link]]:
206
+ ) -> tuple[list[Tag], list[Route], list[SchemaNode], list[Link]]:
206
207
  """Collapse schema graph so routes link directly to nodes whose module matches ``module_prefix``.
207
208
 
208
209
  The routine keeps tag→route links untouched, prunes schema nodes whose module does not start
fastapi_voyager/module.py CHANGED
@@ -1,6 +1,7 @@
1
- from typing import Callable, Type, TypeVar, Any
2
- from fastapi_voyager.type import SchemaNode, ModuleNode, Route, ModuleRoute
1
+ from collections.abc import Callable
2
+ from typing import Any, TypeVar
3
3
 
4
+ from fastapi_voyager.type import ModuleNode, ModuleRoute, Route, SchemaNode
4
5
 
5
6
  N = TypeVar('N') # Node type: ModuleNode or ModuleRoute
6
7
  I = TypeVar('I') # Item type: SchemaNode or Route
@@ -10,7 +11,7 @@ def _build_module_tree(
10
11
  items: list[I],
11
12
  *,
12
13
  get_module_path: Callable[[I], str | None],
13
- NodeClass: Type[N],
14
+ NodeClass: type[N],
14
15
  item_list_attr: str,
15
16
  ) -> list[N]:
16
17
  """
@@ -34,13 +35,13 @@ def _build_module_tree(
34
35
  return NodeClass(**kwargs) # type: ignore[arg-type]
35
36
 
36
37
  def get_or_create(child_name: str, parent: N) -> N:
37
- for m in getattr(parent, 'modules'):
38
+ for m in parent.modules:
38
39
  if m.name == child_name:
39
40
  return m
40
- parent_full = getattr(parent, 'fullname')
41
+ parent_full = parent.fullname
41
42
  fullname = child_name if not parent_full or parent_full == "__root__" else f"{parent_full}.{child_name}"
42
43
  new_node = make_node(child_name, fullname)
43
- getattr(parent, 'modules').append(new_node)
44
+ parent.modules.append(new_node)
44
45
  return new_node
45
46
 
46
47
  # Build the tree
@@ -65,13 +66,13 @@ def _build_module_tree(
65
66
 
66
67
  # Collapse linear chains: no items on node and exactly one child module
67
68
  def collapse(node: N) -> None:
68
- while len(getattr(node, 'modules')) == 1 and len(getattr(node, item_list_attr)) == 0:
69
- child = getattr(node, 'modules')[0]
69
+ while len(node.modules) == 1 and len(getattr(node, item_list_attr)) == 0:
70
+ child = node.modules[0]
70
71
  node.name = f"{node.name}.{child.name}"
71
72
  node.fullname = child.fullname
72
73
  setattr(node, item_list_attr, getattr(child, item_list_attr))
73
- setattr(node, 'modules', getattr(child, 'modules'))
74
- for m in getattr(node, 'modules'):
74
+ node.modules = child.modules
75
+ for m in node.modules:
75
76
  collapse(m)
76
77
 
77
78
  for top in result:
fastapi_voyager/render.py CHANGED
@@ -1,8 +1,17 @@
1
- from typing import Optional
2
- from fastapi_voyager.type import SchemaNode, ModuleNode, Link, Tag, Route, FieldType, PK, ModuleRoute
3
- from fastapi_voyager.module import build_module_schema_tree, build_module_route_tree
4
1
  from logging import getLogger
5
2
 
3
+ from fastapi_voyager.module import build_module_route_tree, build_module_schema_tree
4
+ from fastapi_voyager.type import (
5
+ PK,
6
+ FieldType,
7
+ Link,
8
+ ModuleNode,
9
+ ModuleRoute,
10
+ Route,
11
+ SchemaNode,
12
+ Tag,
13
+ )
14
+
6
15
  logger = getLogger(__name__)
7
16
 
8
17
 
@@ -23,7 +32,7 @@ class Renderer:
23
32
  logger.info(f'show_module: {self.show_module}')
24
33
  logger.info(f'module_color: {self.module_color}')
25
34
 
26
- def render_schema_label(self, node: SchemaNode, color: Optional[str]=None) -> str:
35
+ def render_schema_label(self, node: SchemaNode, color: str | None=None) -> str:
27
36
  has_base_fields = any(f.from_base for f in node.fields)
28
37
  fields = [n for n in node.fields if n.from_base is False]
29
38
 
@@ -74,7 +83,7 @@ class Renderer:
74
83
  raise ValueError(f'Unknown link type: {link.type}')
75
84
 
76
85
  def render_module_schema_content(self, nodes: list[SchemaNode]) -> str:
77
- def render_node(node: SchemaNode, color: Optional[str]=None) -> str:
86
+ def render_node(node: SchemaNode, color: str | None=None) -> str:
78
87
  return f'''
79
88
  "{node.id}" [
80
89
  label = {self.render_schema_label(node, color)}
@@ -82,8 +91,10 @@ class Renderer:
82
91
  margin="0.5,0.1"
83
92
  ];'''
84
93
 
85
- def render_module_schema(mod: ModuleNode, inherit_color: Optional[str]=None, show_cluster:bool=True) -> str:
86
- color: Optional[str] = inherit_color
94
+ def render_module_schema(mod: ModuleNode, inherit_color: str | None=None, show_cluster:bool=True) -> str:
95
+ color: str | None = inherit_color
96
+ cluster_color: str | None = None
97
+
87
98
 
88
99
  # recursively vist module from short to long: 'a', 'a.b', 'a.b.c'
89
100
  # color_flag: {'a', 'a.b.c'}
@@ -94,6 +105,7 @@ class Renderer:
94
105
  if mod.fullname.startswith(k):
95
106
  module_color_flag.remove(k)
96
107
  color = self.module_color[k]
108
+ cluster_color = color if color != inherit_color else None
97
109
  break
98
110
 
99
111
  inner_nodes = [ render_node(node, color) for node in mod.schema_nodes ]
@@ -108,8 +120,8 @@ class Renderer:
108
120
  style="rounded"
109
121
  label = " {mod.name}"
110
122
  labeljust = "l"
111
- {(f'pencolor = "{color}"' if color else 'pencolor="#ccc"')}
112
- {(f'penwidth = 3' if color else 'penwidth=""')}
123
+ {(f'pencolor = "{cluster_color}"' if cluster_color else 'pencolor="#ccc"')}
124
+ {('penwidth = 3' if color else 'penwidth=""')}
113
125
  {inner_nodes_str}
114
126
  {child_str}
115
127
  }}'''
fastapi_voyager/server.py CHANGED
@@ -1,23 +1,24 @@
1
1
  from pathlib import Path
2
- from typing import Optional, Literal
3
- from fastapi import FastAPI, APIRouter
4
- from starlette.middleware.gzip import GZipMiddleware
5
- from pydantic import BaseModel
6
- from fastapi.responses import HTMLResponse, PlainTextResponse, JSONResponse
2
+ from typing import Literal
3
+
4
+ from fastapi import APIRouter, FastAPI
5
+ from fastapi.responses import HTMLResponse, JSONResponse, PlainTextResponse
7
6
  from fastapi.staticfiles import StaticFiles
8
- from fastapi_voyager.voyager import Voyager
9
- from fastapi_voyager.type import Tag, CoreData, SchemaNode
7
+ from pydantic import BaseModel
8
+ from starlette.middleware.gzip import GZipMiddleware
9
+
10
10
  from fastapi_voyager.render import Renderer
11
+ from fastapi_voyager.type import CoreData, SchemaNode, Tag
11
12
  from fastapi_voyager.type_helper import get_source, get_vscode_link
12
13
  from fastapi_voyager.version import __version__
13
-
14
+ from fastapi_voyager.voyager import Voyager
14
15
 
15
16
  WEB_DIR = Path(__file__).parent / "web"
16
17
  WEB_DIR.mkdir(exist_ok=True)
17
18
 
18
19
  GA_PLACEHOLDER = "<!-- GA_SNIPPET -->"
19
20
 
20
- def _build_ga_snippet(ga_id: Optional[str]) -> str:
21
+ def _build_ga_snippet(ga_id: str | None) -> str:
21
22
  if not ga_id:
22
23
  return ""
23
24
 
@@ -40,13 +41,13 @@ class OptionParam(BaseModel):
40
41
  enable_brief_mode: bool
41
42
  version: str
42
43
  initial_page_policy: INITIAL_PAGE_POLICY
43
- swagger_url: Optional[str] = None
44
+ swagger_url: str | None = None
44
45
 
45
46
  class Payload(BaseModel):
46
- tags: Optional[list[str]] = None
47
- schema_name: Optional[str] = None
48
- schema_field: Optional[str] = None
49
- route_name: Optional[str] = None
47
+ tags: list[str] | None = None
48
+ schema_name: str | None = None
49
+ schema_field: str | None = None
50
+ route_name: str | None = None
50
51
  show_fields: str = 'object'
51
52
  show_meta: bool = False
52
53
  brief: bool = False
@@ -58,11 +59,11 @@ def create_voyager(
58
59
  target_app: FastAPI,
59
60
  module_color: dict[str, str] | None = None,
60
61
  gzip_minimum_size: int | None = 500,
61
- module_prefix: Optional[str] = None,
62
- swagger_url: Optional[str] = None,
63
- online_repo_url: Optional[str] = None,
62
+ module_prefix: str | None = None,
63
+ swagger_url: str | None = None,
64
+ online_repo_url: str | None = None,
64
65
  initial_page_policy: INITIAL_PAGE_POLICY = 'first',
65
- ga_id: Optional[str] = None,
66
+ ga_id: str | None = None,
66
67
  ) -> FastAPI:
67
68
  router = APIRouter(tags=['fastapi-voyager'])
68
69
 
fastapi_voyager/type.py CHANGED
@@ -1,6 +1,8 @@
1
1
  from dataclasses import field
2
+ from typing import Literal
3
+
2
4
  from pydantic.dataclasses import dataclass
3
- from typing import Literal, Optional
5
+
4
6
 
5
7
  @dataclass
6
8
  class NodeBase:
@@ -76,5 +78,5 @@ class CoreData:
76
78
  nodes: list[SchemaNode]
77
79
  links: list[Link]
78
80
  show_fields: FieldType
79
- module_color: Optional[dict[str, str]] = None
80
- schema: Optional[str] = None
81
+ module_color: dict[str, str] | None = None
82
+ schema: str | None = None
@@ -1,11 +1,13 @@
1
1
  import inspect
2
2
  import logging
3
3
  import os
4
- from pydantic import BaseModel
5
- from typing import get_origin, get_args, Union, Annotated, Any, Type, Generic, Optional
6
- from fastapi_voyager.type import FieldInfo
7
4
  from types import UnionType
5
+ from typing import Annotated, Any, Generic, Union, get_args, get_origin
6
+
8
7
  import pydantic_resolve.constant as const
8
+ from pydantic import BaseModel
9
+
10
+ from fastapi_voyager.type import FieldInfo
9
11
 
10
12
  logger = logging.getLogger(__name__)
11
13
 
@@ -185,7 +187,7 @@ def get_pydantic_fields(schema: type[BaseModel], bases_fields: set[str]) -> list
185
187
  return fields
186
188
 
187
189
 
188
- def get_vscode_link(kls, online_repo_url: Optional[str] = None) -> str:
190
+ def get_vscode_link(kls, online_repo_url: str | None = None) -> str:
189
191
  """Build a VSCode deep link to the class definition.
190
192
 
191
193
  Priority:
@@ -241,7 +243,7 @@ def safe_issubclass(kls, target_kls):
241
243
 
242
244
  def update_forward_refs(kls):
243
245
  # TODO: refactor
244
- def update_pydantic_forward_refs(pydantic_kls: Type[BaseModel]):
246
+ def update_pydantic_forward_refs(pydantic_kls: type[BaseModel]):
245
247
  """
246
248
  recursively update refs.
247
249
  """
@@ -254,7 +256,11 @@ def update_forward_refs(kls):
254
256
  update_forward_refs(field.annotation)
255
257
 
256
258
  for shelled_type in get_core_types(kls):
257
- if getattr(shelled_type, const.PYDANTIC_FORWARD_REF_UPDATED, False):
259
+ # Only treat as updated if the flag is set on the class itself, not via inheritance
260
+
261
+ local_attrs = getattr(shelled_type, '__dict__', {})
262
+ if local_attrs.get(const.PYDANTIC_FORWARD_REF_UPDATED, False):
263
+ logger.debug("%s visited", shelled_type.__qualname__)
258
264
  continue
259
265
  if safe_issubclass(shelled_type, BaseModel):
260
266
  update_pydantic_forward_refs(shelled_type)
@@ -287,7 +293,7 @@ def is_non_pydantic_type(tp):
287
293
  return True
288
294
 
289
295
  if __name__ == "__main__":
290
- from tests.demo_anno import PageSprint, PageOverall
296
+ from tests.demo_anno import PageOverall, PageSprint
291
297
 
292
298
  update_forward_refs(PageOverall)
293
299
  update_forward_refs(PageSprint)
@@ -1,2 +1,2 @@
1
1
  __all__ = ["__version__"]
2
- __version__ = "0.12.2"
2
+ __version__ = "0.12.4"
@@ -1,21 +1,25 @@
1
- from pydantic import BaseModel
1
+
2
+ import pydantic_resolve.constant as const
2
3
  from fastapi import FastAPI, routing
3
- from typing import Callable
4
+ from pydantic import BaseModel
5
+
6
+ from fastapi_voyager.filter import (
7
+ filter_graph,
8
+ filter_subgraph_by_module_prefix,
9
+ filter_subgraph_from_tag_to_schema_by_module_prefix,
10
+ )
11
+ from fastapi_voyager.render import Renderer
12
+ from fastapi_voyager.type import PK, CoreData, FieldType, Link, LinkType, Route, SchemaNode, Tag
4
13
  from fastapi_voyager.type_helper import (
5
- get_core_types,
6
14
  full_class_name,
7
15
  get_bases_fields,
8
- is_inheritance_of_pydantic_base,
16
+ get_core_types,
9
17
  get_pydantic_fields,
10
18
  get_type_name,
19
+ is_inheritance_of_pydantic_base,
20
+ is_non_pydantic_type,
11
21
  update_forward_refs,
12
- is_non_pydantic_type
13
22
  )
14
- from pydantic import BaseModel
15
- from fastapi_voyager.type import Route, SchemaNode, Link, Tag, LinkType, FieldType, PK, CoreData
16
- from fastapi_voyager.filter import filter_graph, filter_subgraph_from_tag_to_schema_by_module_prefix, filter_subgraph_by_module_prefix
17
- from fastapi_voyager.render import Renderer
18
- import pydantic_resolve.constant as const
19
23
 
20
24
 
21
25
  class Voyager:
@@ -0,0 +1,17 @@
1
+ const { defineComponent, computed } = window.Vue;
2
+
3
+ import { store } from '../store.js'
4
+
5
+ export default defineComponent({
6
+ name: "Demo",
7
+ emits: ["close"],
8
+ setup() {
9
+ return { store };
10
+ },
11
+ template: `
12
+ <div>
13
+ <p>Count: {{ store.state.count }}</p>
14
+ <button @click="store.mutations.increment()">Add</button>
15
+ </div>
16
+ `
17
+ });
@@ -132,7 +132,6 @@ export class GraphUI {
132
132
  });
133
133
 
134
134
  // svg 背景点击高亮清空
135
-
136
135
  $(document)
137
136
  .off("click.graphui")
138
137
  .on("click.graphui", function (evt) {
@@ -298,7 +298,7 @@
298
298
  <template #after>
299
299
  <div style="position: relative; width: 100%; height: 100%;">
300
300
  <div id="graph" class="adjust-fit"></div>
301
- <div style="position: absolute; left: 8px; bottom: 8px; z-index: 10; background: rgba(255,255,255,0.85); border-radius: 4px; padding: 2px 8px;">
301
+ <div style="position: absolute; left: 8px; top: 8px; z-index: 10; background: rgba(255,255,255,0.85); border-radius: 4px; padding: 2px 8px;">
302
302
  <div class="q-mt-sm">
303
303
  <q-toggle
304
304
  v-model="state.focus"
@@ -337,21 +337,25 @@
337
337
  title="show module cluster"
338
338
  />
339
339
  </div>
340
+ <div class="q-mt-sm">
341
+ <q-toggle
342
+ v-model="state.focus"
343
+ v-show="schemaCodeName"
344
+ @update:model-value="val => onFocusChange(val)"
345
+ label="Focus"
346
+ dense
347
+ title="pick a schema and toggle focus on to display related nodes only"
348
+ />
349
+ </div>
350
+ <!-- <div class="q-mt-sm">
351
+ <demo-component></demo-component>
352
+ </div> -->
340
353
  </div>
341
354
  </div>
342
355
  </template>
343
356
  </q-splitter>
344
357
  </q-page-container>
345
358
  </q-layout>
346
- <!-- Detail Dialog -->
347
- <q-dialog v-model="showDetail" :persistent="true" :maximized="true">
348
- <detail-dialog
349
- :schema-name="schemaName"
350
- :show-fields="state.showFields"
351
- :model-value="showDetail"
352
- @close="closeDetail"
353
- />
354
- </q-dialog>
355
359
 
356
360
  <!-- Schema Field Filter Dialog -->
357
361
  <q-dialog
@@ -0,0 +1,17 @@
1
+ const { reactive, watch, ref } = window.Vue;
2
+
3
+ const state = reactive({
4
+ count: 0
5
+ })
6
+
7
+ const mutations = {
8
+ increment() {
9
+ state.count += 1
10
+ }
11
+ }
12
+
13
+
14
+ export const store = {
15
+ state,
16
+ mutations
17
+ }
@@ -1,6 +1,7 @@
1
1
  import SchemaFieldFilter from "./component/schema-field-filter.js";
2
2
  import SchemaCodeDisplay from "./component/schema-code-display.js";
3
3
  import RouteCodeDisplay from "./component/route-code-display.js";
4
+ import Demo from './component/demo.js'
4
5
  import RenderGraph from "./component/render-graph.js";
5
6
  import { GraphUI } from "./graph-ui.js";
6
7
  const { createApp, reactive, onMounted, watch, ref } = window.Vue;
@@ -39,17 +40,22 @@ const app = createApp({
39
40
 
40
41
  const showDetail = ref(false);
41
42
  const showSchemaFieldFilter = ref(false);
43
+
42
44
  const showDumpDialog = ref(false);
43
45
  const dumpJson = ref("");
44
46
  const showImportDialog = ref(false);
45
47
  const importJsonText = ref("");
48
+
46
49
  const showRenderGraph = ref(false);
50
+
47
51
  const renderCoreData = ref(null);
52
+
48
53
  const schemaName = ref(""); // used by detail dialog
49
54
  const schemaFieldFilterSchema = ref(null); // external schemaName for schema-field-filter
50
55
  const schemaCodeName = ref("");
51
56
  const routeCodeId = ref("");
52
57
  const showRouteDetail = ref(false);
58
+
53
59
  let graphUI = null;
54
60
 
55
61
  function openDetail() {
@@ -435,13 +441,18 @@ const app = createApp({
435
441
  };
436
442
  },
437
443
  });
444
+
438
445
  app.use(window.Quasar);
446
+
439
447
  // Set Quasar primary theme color to green
440
448
  if (window.Quasar && typeof window.Quasar.setCssVar === "function") {
441
449
  window.Quasar.setCssVar("primary", "#009485");
442
450
  }
443
- app.component("schema-field-filter", SchemaFieldFilter);
444
- app.component("schema-code-display", SchemaCodeDisplay);
445
- app.component("route-code-display", RouteCodeDisplay);
446
- app.component("render-graph", RenderGraph);
451
+
452
+ app.component("schema-field-filter", SchemaFieldFilter); // shift click and see relationships
453
+ app.component("schema-code-display", SchemaCodeDisplay); // double click to see node details
454
+ app.component("route-code-display", RouteCodeDisplay); // double click to see route details
455
+ app.component("render-graph", RenderGraph); // for debug, render pasted dot content
456
+ app.component('demo-component', Demo)
457
+
447
458
  app.mount("#q-app");
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-voyager
3
- Version: 0.12.2
3
+ Version: 0.12.4
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
@@ -12,7 +12,6 @@ Classifier: Framework :: FastAPI
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: License :: OSI Approved :: MIT License
14
14
  Classifier: Programming Language :: Python :: 3
15
- Classifier: Programming Language :: Python :: 3.9
16
15
  Classifier: Programming Language :: Python :: 3.10
17
16
  Classifier: Programming Language :: Python :: 3.11
18
17
  Classifier: Programming Language :: Python :: 3.12
@@ -32,14 +31,31 @@ Description-Content-Type: text/markdown
32
31
  [![PyPI Downloads](https://static.pepy.tech/badge/fastapi-voyager/month)](https://pepy.tech/projects/fastapi-voyager)
33
32
 
34
33
 
35
- > This repo is still in early stage, it supports pydantic v2 only
36
34
 
37
35
  Visualize your FastAPI endpoints, and explore them interactively.
38
36
 
39
- [visit online demo](https://www.newsyeah.fun/voyager/) of project: [composition oriented development pattern](https://github.com/allmonday/composition-oriented-development-pattern)
37
+ > This repo is still in early stage, it supports pydantic v2 only
38
+
39
+ [live demo](https://www.newsyeah.fun/voyager/) of project: [composition oriented development pattern](https://github.com/allmonday/composition-oriented-development-pattern)
40
40
 
41
41
  <img width="1600" height="986" alt="image" src="https://github.com/user-attachments/assets/8829cda0-f42d-4c84-be2f-b019bb5fe7e1" />
42
42
 
43
+ with configuration:
44
+
45
+ ```python
46
+ app.mount('/voyager',
47
+ create_voyager(
48
+ app,
49
+ module_color={'src.services': 'tomato'},
50
+ module_prefix='src.services',
51
+ swagger_url="/docs",
52
+ ga_id="G-R64S7Q49VL",
53
+ initial_page_policy='first',
54
+ online_repo_url='https://github.com/allmonday/composition-oriented-development-pattern/blob/master'))
55
+ ```
56
+
57
+ https://github.com/allmonday/composition-oriented-development-pattern/blob/master/src/main.py#L48
58
+
43
59
  ## Plan & Raodmap
44
60
  - [ideas](./docs/idea.md)
45
61
  - [changelog & roadmap](./docs/changelog.md)
@@ -0,0 +1,35 @@
1
+ fastapi_voyager/__init__.py,sha256=kqwzThE1YhmQ_7jPKGlnWvqRGC-hFrRqq_lKhKaleYU,229
2
+ fastapi_voyager/cli.py,sha256=td3yIIigEomhSdDO-Xkh-CgpEwCafwlwnpvxnT9QsBo,10488
3
+ fastapi_voyager/filter.py,sha256=AN_HIu8-DtKisIq5mFt7CnqRHtxKewedNGyyaI82hSY,11529
4
+ fastapi_voyager/module.py,sha256=h9YR3BpS-CAcJW9WCdVkF4opqwY32w9T67g9GfdLytk,3425
5
+ fastapi_voyager/render.py,sha256=7jSChISqTV2agaO55thhwyrOhqVOMux4x7k8rSQROnU,9960
6
+ fastapi_voyager/server.py,sha256=MZNRpcXor2q8Rj3OSf6EH8NkgDChxfzUtnIY8ilRkaY,7053
7
+ fastapi_voyager/type.py,sha256=7EL1zaIwKVRGpLig7fqaOrZGN5k0Rm31C9COfck3CSs,1750
8
+ fastapi_voyager/type_helper.py,sha256=JXD_OE_xTARkGjWDsnO_xfvyZ0vcwViYyqCp6oEHBTM,9719
9
+ fastapi_voyager/version.py,sha256=_-7Zp-y6CfERg3seD-eKm5cYiJTOf-VWfF-6iV8hzms,49
10
+ fastapi_voyager/voyager.py,sha256=LiRUb0ZG2cfnyY_pwRqoeZjxb6Pu6xy_lqPiMupxoKM,13510
11
+ fastapi_voyager/web/graph-ui.js,sha256=7ynB-o8Dse6WHWlp4bmuEfa1pUZJ2YFLJKKXjr4R6wU,5733
12
+ fastapi_voyager/web/graphviz.svg.css,sha256=zDCjjpT0Idufu5YOiZI76PL70-avP3vTyzGPh9M85Do,1563
13
+ fastapi_voyager/web/graphviz.svg.js,sha256=lvAdbjHc-lMSk4GQp-iqYA2PCFX4RKnW7dFaoe0LUHs,16005
14
+ fastapi_voyager/web/index.html,sha256=C8gqka2omSHqx40C5_CCSMKHP-VJSPV3UFTrqQHGRtU,19635
15
+ fastapi_voyager/web/quasar.min.css,sha256=F5jQe7X2XT54VlvAaa2V3GsBFdVD-vxDZeaPLf6U9CU,203145
16
+ fastapi_voyager/web/quasar.min.js,sha256=h0ftyPMW_CRiyzeVfQqiup0vrVt4_QWojpqmpnpn07E,502974
17
+ fastapi_voyager/web/store.js,sha256=LPJdFxyFn8j7vOI41FKtWoB8agoqIUrR3tN5HRS_5e4,210
18
+ fastapi_voyager/web/vue-main.js,sha256=qTPJ2CTT5T9kRR2h0DbroOGJVePRIF3e24u7JexqjPQ,13516
19
+ fastapi_voyager/web/component/demo.js,sha256=FUwZqyMYjwf2J_YAzUBZ-gW9RFdOrz5zbxogBBYiYYE,347
20
+ fastapi_voyager/web/component/render-graph.js,sha256=e8Xgh2Kl-nYU0P1gstEmAepCgFnk2J6UdxW8TlMafGs,2322
21
+ fastapi_voyager/web/component/route-code-display.js,sha256=8NJPPjNRUC21gjpY8XYEQs4RBbhX1pCiqEhJp39ku6k,3678
22
+ fastapi_voyager/web/component/schema-code-display.js,sha256=qKUMV2RFQzR8deof2iC4vyp65UaWadtVsDAXjY-i3vE,7042
23
+ fastapi_voyager/web/component/schema-field-filter.js,sha256=c--XiXJrhIS7sYo1x8ZwMoqak0k9xLkNYTWoli-zd38,6253
24
+ fastapi_voyager/web/icon/android-chrome-192x192.png,sha256=35sBy6jmUFJCcquStaafHH1qClZIbd-X3PIKSeLkrNo,37285
25
+ fastapi_voyager/web/icon/android-chrome-512x512.png,sha256=eb2eDjCwIruc05029_0L9hcrkVkv8KceLn1DJMYU0zY,210789
26
+ fastapi_voyager/web/icon/apple-touch-icon.png,sha256=gnWK46tPnvSw1-oYZjgI02wpoO4OrIzsVzGHC5oKWO0,33187
27
+ fastapi_voyager/web/icon/favicon-16x16.png,sha256=JC07jEzfIYxBIoQn_FHXvyHuxESdhWNKLtjWURfTSFQ,686
28
+ fastapi_voyager/web/icon/favicon-32x32.png,sha256=C7v1h58cfWOsiLp9yOIZtlx-dLasBcq3NqpHVGRmpt4,1859
29
+ fastapi_voyager/web/icon/favicon.ico,sha256=tZolYIXkkBcFiYl1A8ksaXN2VjGamzcSdes838dLvNc,15406
30
+ fastapi_voyager/web/icon/site.webmanifest,sha256=ep4Hzh9zhmiZF2At3Fp1dQrYQuYF_3ZPZxc1KcGBvwQ,263
31
+ fastapi_voyager-0.12.4.dist-info/METADATA,sha256=9VUoRQ9sMRLqvukIAlC7kUi1Bcf2raXR9v9JRx7Ltbk,6523
32
+ fastapi_voyager-0.12.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
33
+ fastapi_voyager-0.12.4.dist-info/entry_points.txt,sha256=pEIKoUnIDXEtdMBq8EmXm70m16vELIu1VPz9-TBUFWM,53
34
+ fastapi_voyager-0.12.4.dist-info/licenses/LICENSE,sha256=lNVRR3y_bFVoFKuK2JM8N4sFaj3m-7j29kvL3olFi5Y,1067
35
+ fastapi_voyager-0.12.4.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,33 +0,0 @@
1
- fastapi_voyager/__init__.py,sha256=tZy0Nkj8kTaMgbvHy-mGxVcFGVX0Km-36dnzsAIG2uk,230
2
- fastapi_voyager/cli.py,sha256=xK8DT-m2qP38FK2dGhLP-sHEuS29SBw6ACrnX9w85P0,10521
3
- fastapi_voyager/filter.py,sha256=9Y-NepveIiCLOI-5eXk6DNK9H3dr5_h4xUbWYHkbo7M,11552
4
- fastapi_voyager/module.py,sha256=Z2QHNmiLk6ZAJlm2nSmO875Q33TweSg8UxZSzIpU9zY,3499
5
- fastapi_voyager/render.py,sha256=8hVsEDQi2-aP3QKN6KI3RnNz0uG-FuDr_k4D7QNsdQQ,9823
6
- fastapi_voyager/server.py,sha256=fVKruGccBJ039z3EbYRjqUYHbbw5HOlo5jzK_n76ids,7092
7
- fastapi_voyager/type.py,sha256=VmcTB1G-LOT70EWCzi4LU_FUkSGWUIBJX15T_J5HnOo,1764
8
- fastapi_voyager/type_helper.py,sha256=TqtYP2_54aar_iQjD0XhjJPXYhfi6icnPPrxkj0a4sk,9523
9
- fastapi_voyager/version.py,sha256=jnAp1zbYtBp4Jx35wH9VM8Hi_uyCLhudDOycCg10IfY,49
10
- fastapi_voyager/voyager.py,sha256=nioo56oFDeZ8nwwPWDtaQbkpe4pVssFoBVHCWFhs0K4,13549
11
- fastapi_voyager/web/graph-ui.js,sha256=9ONPxQHvk4HxYq6KtKc_2VbJmUgd-gh7i3Biv1rkqC4,5734
12
- fastapi_voyager/web/graphviz.svg.css,sha256=zDCjjpT0Idufu5YOiZI76PL70-avP3vTyzGPh9M85Do,1563
13
- fastapi_voyager/web/graphviz.svg.js,sha256=lvAdbjHc-lMSk4GQp-iqYA2PCFX4RKnW7dFaoe0LUHs,16005
14
- fastapi_voyager/web/index.html,sha256=8cmlwQzE5tonHj_QozdKcr3z-7JFsvE7cjf70dye0y8,19377
15
- fastapi_voyager/web/quasar.min.css,sha256=F5jQe7X2XT54VlvAaa2V3GsBFdVD-vxDZeaPLf6U9CU,203145
16
- fastapi_voyager/web/quasar.min.js,sha256=h0ftyPMW_CRiyzeVfQqiup0vrVt4_QWojpqmpnpn07E,502974
17
- fastapi_voyager/web/vue-main.js,sha256=m6U24ythzjQuAYXUm9BsTdFrApFNqW26B0Bf7TsybqQ,13275
18
- fastapi_voyager/web/component/render-graph.js,sha256=e8Xgh2Kl-nYU0P1gstEmAepCgFnk2J6UdxW8TlMafGs,2322
19
- fastapi_voyager/web/component/route-code-display.js,sha256=8NJPPjNRUC21gjpY8XYEQs4RBbhX1pCiqEhJp39ku6k,3678
20
- fastapi_voyager/web/component/schema-code-display.js,sha256=qKUMV2RFQzR8deof2iC4vyp65UaWadtVsDAXjY-i3vE,7042
21
- fastapi_voyager/web/component/schema-field-filter.js,sha256=c--XiXJrhIS7sYo1x8ZwMoqak0k9xLkNYTWoli-zd38,6253
22
- fastapi_voyager/web/icon/android-chrome-192x192.png,sha256=35sBy6jmUFJCcquStaafHH1qClZIbd-X3PIKSeLkrNo,37285
23
- fastapi_voyager/web/icon/android-chrome-512x512.png,sha256=eb2eDjCwIruc05029_0L9hcrkVkv8KceLn1DJMYU0zY,210789
24
- fastapi_voyager/web/icon/apple-touch-icon.png,sha256=gnWK46tPnvSw1-oYZjgI02wpoO4OrIzsVzGHC5oKWO0,33187
25
- fastapi_voyager/web/icon/favicon-16x16.png,sha256=JC07jEzfIYxBIoQn_FHXvyHuxESdhWNKLtjWURfTSFQ,686
26
- fastapi_voyager/web/icon/favicon-32x32.png,sha256=C7v1h58cfWOsiLp9yOIZtlx-dLasBcq3NqpHVGRmpt4,1859
27
- fastapi_voyager/web/icon/favicon.ico,sha256=tZolYIXkkBcFiYl1A8ksaXN2VjGamzcSdes838dLvNc,15406
28
- fastapi_voyager/web/icon/site.webmanifest,sha256=ep4Hzh9zhmiZF2At3Fp1dQrYQuYF_3ZPZxc1KcGBvwQ,263
29
- fastapi_voyager-0.12.2.dist-info/METADATA,sha256=hXiqtPfKvni9gv4HxyjGdQTQFaDIAGJ69qE4WzsSzus,6059
30
- fastapi_voyager-0.12.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
31
- fastapi_voyager-0.12.2.dist-info/entry_points.txt,sha256=pEIKoUnIDXEtdMBq8EmXm70m16vELIu1VPz9-TBUFWM,53
32
- fastapi_voyager-0.12.2.dist-info/licenses/LICENSE,sha256=lNVRR3y_bFVoFKuK2JM8N4sFaj3m-7j29kvL3olFi5Y,1067
33
- fastapi_voyager-0.12.2.dist-info/RECORD,,