fastapi-voyager 0.16.0a1__py3-none-any.whl → 0.16.0a3__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.
@@ -32,13 +32,3 @@ class VoyagerAdapter(ABC):
32
32
  A framework-specific application object
33
33
  """
34
34
  pass
35
-
36
- @abstractmethod
37
- def get_mount_path(self) -> str:
38
- """
39
- Get the recommended mount path for the voyager UI.
40
-
41
- Returns:
42
- The path where voyager should be mounted (e.g., "/voyager")
43
- """
44
- pass
@@ -32,6 +32,7 @@ class DjangoNinjaAdapter(VoyagerAdapter):
32
32
  ga_id: str | None = None,
33
33
  er_diagram: Any = None,
34
34
  enable_pydantic_resolve_meta: bool = False,
35
+ server_mode: bool = False,
35
36
  ):
36
37
  self.ctx = VoyagerContext(
37
38
  target_app=target_app,
@@ -45,6 +46,7 @@ class DjangoNinjaAdapter(VoyagerAdapter):
45
46
  enable_pydantic_resolve_meta=enable_pydantic_resolve_meta,
46
47
  framework_name="Django Ninja",
47
48
  )
49
+ self.server_mode = server_mode
48
50
  # Note: gzip should be handled by Django's middleware, not here
49
51
 
50
52
  async def _handle_request(self, scope, receive, send):
@@ -55,8 +57,8 @@ class DjangoNinjaAdapter(VoyagerAdapter):
55
57
  # Parse the request
56
58
  method = scope["method"]
57
59
  path = scope["path"]
58
- # Remove /voyager prefix for internal routing
59
- if path.startswith("/voyager"):
60
+ # Remove /voyager prefix for internal routing (unless in server_mode)
61
+ if not self.server_mode and path.startswith("/voyager"):
60
62
  path = path[8:] # Remove '/voyager'
61
63
  if path == "":
62
64
  path = "/"
@@ -284,16 +286,15 @@ class DjangoNinjaAdapter(VoyagerAdapter):
284
286
  """Create and return an ASGI application."""
285
287
 
286
288
  async def asgi_app(scope, receive, send):
287
- # Route /voyager/* to voyager handler
288
- if scope["type"] == "http" and scope["path"].startswith("/voyager"):
289
- await self._handle_request(scope, receive, send)
289
+ # In server_mode, handle all paths; otherwise only handle /voyager/*
290
+ if scope["type"] == "http":
291
+ if self.server_mode or scope["path"].startswith("/voyager"):
292
+ await self._handle_request(scope, receive, send)
293
+ else:
294
+ # Return 404 for non-voyager paths
295
+ # (Django should handle these before they reach here)
296
+ await self._send_404(send)
290
297
  else:
291
- # Return 404 for non-voyager paths
292
- # (Django should handle these before they reach here)
293
298
  await self._send_404(send)
294
299
 
295
300
  return asgi_app
296
-
297
- def get_mount_path(self) -> str:
298
- """Get the recommended mount path for voyager."""
299
- return "/voyager"
@@ -5,11 +5,7 @@ This module provides the FastAPI-specific implementation of the voyager server.
5
5
  """
6
6
  from typing import Any, Literal
7
7
 
8
- from fastapi import APIRouter, FastAPI
9
- from fastapi.responses import HTMLResponse, JSONResponse, PlainTextResponse
10
- from fastapi.staticfiles import StaticFiles
11
8
  from pydantic import BaseModel
12
- from starlette.middleware.gzip import GZipMiddleware
13
9
 
14
10
  from fastapi_voyager.adapters.base import VoyagerAdapter
15
11
  from fastapi_voyager.adapters.common import STATIC_FILES_PATH, VoyagerContext
@@ -83,6 +79,7 @@ class FastAPIAdapter(VoyagerAdapter):
83
79
  ga_id: str | None = None,
84
80
  er_diagram: Any = None,
85
81
  enable_pydantic_resolve_meta: bool = False,
82
+ server_mode: bool = False,
86
83
  ):
87
84
  self.ctx = VoyagerContext(
88
85
  target_app=target_app,
@@ -97,9 +94,17 @@ class FastAPIAdapter(VoyagerAdapter):
97
94
  framework_name="FastAPI",
98
95
  )
99
96
  self.gzip_minimum_size = gzip_minimum_size
97
+ # Note: server_mode is accepted for API consistency but not used
98
+ # since FastAPI apps are always standalone with routes at /
100
99
 
101
- def create_app(self) -> FastAPI:
100
+ def create_app(self) -> Any:
102
101
  """Create and return a FastAPI application with voyager endpoints."""
102
+ # Lazy import FastAPI to avoid import errors when framework is not installed
103
+ from fastapi import APIRouter, FastAPI
104
+ from fastapi.responses import HTMLResponse, JSONResponse, PlainTextResponse
105
+ from fastapi.staticfiles import StaticFiles
106
+ from starlette.middleware.gzip import GZipMiddleware
107
+
103
108
  router = APIRouter(tags=["fastapi-voyager"])
104
109
 
105
110
  @router.post("/er-diagram", response_class=PlainTextResponse)
@@ -159,7 +164,3 @@ class FastAPIAdapter(VoyagerAdapter):
159
164
  app.include_router(router)
160
165
 
161
166
  return app
162
-
163
- def get_mount_path(self) -> str:
164
- """Get the recommended mount path for voyager."""
165
- return "/voyager"
@@ -5,9 +5,6 @@ This module provides the Litestar-specific implementation of the voyager server.
5
5
  """
6
6
  from typing import Any
7
7
 
8
- from litestar import Litestar, MediaType, Request, Response, get, post
9
- from litestar.static_files import create_static_files_router
10
-
11
8
  from fastapi_voyager.adapters.base import VoyagerAdapter
12
9
  from fastapi_voyager.adapters.common import STATIC_FILES_PATH, WEB_DIR, VoyagerContext
13
10
  from fastapi_voyager.type import CoreData, SchemaNode, Tag
@@ -32,6 +29,7 @@ class LitestarAdapter(VoyagerAdapter):
32
29
  ga_id: str | None = None,
33
30
  er_diagram: Any = None,
34
31
  enable_pydantic_resolve_meta: bool = False,
32
+ server_mode: bool = False,
35
33
  ):
36
34
  self.ctx = VoyagerContext(
37
35
  target_app=target_app,
@@ -46,9 +44,14 @@ class LitestarAdapter(VoyagerAdapter):
46
44
  framework_name="Litestar",
47
45
  )
48
46
  self.gzip_minimum_size = gzip_minimum_size
47
+ # Note: server_mode is accepted for API consistency but not used
48
+ # since Litestar apps are always standalone with routes at /
49
49
 
50
- def create_app(self) -> Litestar:
50
+ def create_app(self) -> Any:
51
51
  """Create and return a Litestar application with voyager endpoints."""
52
+ # Lazy import Litestar to avoid import errors when framework is not installed
53
+ from litestar import Litestar, MediaType, Request, Response, get, post
54
+ from litestar.static_files import create_static_files_router
52
55
 
53
56
  @get("/er-diagram")
54
57
  async def get_er_diagram(request: Request) -> str:
@@ -182,7 +185,3 @@ class LitestarAdapter(VoyagerAdapter):
182
185
  for f in schema.fields
183
186
  ],
184
187
  }
185
-
186
- def get_mount_path(self) -> str:
187
- """Get the recommended mount path for voyager."""
188
- return "/voyager"
fastapi_voyager/cli.py CHANGED
@@ -5,8 +5,7 @@ import importlib.util
5
5
  import logging
6
6
  import os
7
7
  import sys
8
-
9
- from fastapi import FastAPI
8
+ from typing import Any
10
9
 
11
10
  from fastapi_voyager import server as viz_server
12
11
  from fastapi_voyager.version import __version__
@@ -14,41 +13,49 @@ from fastapi_voyager.voyager import Voyager
14
13
 
15
14
  logger = logging.getLogger(__name__)
16
15
 
16
+ # Framework type constants
17
+ SUPPORTED_FRAMEWORKS = ["fastapi", "litestar", "django-ninja"]
18
+
17
19
 
18
- def load_fastapi_app_from_file(module_path: str, app_name: str = "app") -> FastAPI | None:
19
- """Load FastAPI app from a Python module file."""
20
+ def load_app_from_file(module_path: str, app_name: str = "app", framework: str | None = None) -> Any:
21
+ """Load web framework app from a Python module file."""
20
22
  try:
21
23
  # Convert relative path to absolute path
22
24
  if not os.path.isabs(module_path):
23
25
  module_path = os.path.abspath(module_path)
24
-
26
+
25
27
  # Load the module
26
28
  spec = importlib.util.spec_from_file_location("app_module", module_path)
27
29
  if spec is None or spec.loader is None:
28
30
  logger.error(f"Could not load module from {module_path}")
29
31
  return None
30
-
32
+
31
33
  module = importlib.util.module_from_spec(spec)
32
34
  sys.modules["app_module"] = module
33
35
  spec.loader.exec_module(module)
34
-
35
- # Get the FastAPI app instance
36
- if hasattr(module, app_name):
37
- app = getattr(module, app_name)
38
- if isinstance(app, FastAPI):
39
- return app
40
- logger.error(f"'{app_name}' is not a FastAPI instance")
36
+
37
+ # Get the app instance
38
+ if not hasattr(module, app_name):
39
+ logger.error(f"No attribute '{app_name}' found in the module")
41
40
  return None
42
- logger.error(f"No attribute '{app_name}' found in the module")
43
- return None
44
-
41
+
42
+ app = getattr(module, app_name)
43
+
44
+ # Verify app type if framework is specified
45
+ if framework is not None:
46
+ if not _validate_app_framework(app, framework):
47
+ logger.error(f"'{app_name}' is not a {framework} instance")
48
+ return None
49
+
50
+ return app
51
+
45
52
  except Exception as e:
46
- logger.error(f"Error loading FastAPI app: {e}")
53
+ logger.error(f"Error loading app: {e}")
47
54
  return None
48
55
 
49
56
 
50
- def load_fastapi_app_from_module(module_name: str, app_name: str = "app") -> FastAPI | None:
51
- """Load FastAPI app from a Python module name."""
57
+ def load_app_from_module(module_name: str, app_name: str = "app", framework: str | None = None) -> Any:
58
+ """Load web framework app from a Python module name."""
52
59
  try:
53
60
  # Temporarily add the current working directory to sys.path
54
61
  current_dir = os.getcwd()
@@ -57,35 +64,62 @@ def load_fastapi_app_from_module(module_name: str, app_name: str = "app") -> Fas
57
64
  path_added = True
58
65
  else:
59
66
  path_added = False
60
-
67
+
61
68
  try:
62
69
  # Import the module by name
63
70
  module = importlib.import_module(module_name)
64
-
65
- # Get the FastAPI app instance
66
- if hasattr(module, app_name):
67
- app = getattr(module, app_name)
68
- if isinstance(app, FastAPI):
69
- return app
70
- logger.error(f"'{app_name}' is not a FastAPI instance")
71
+
72
+ # Get the app instance
73
+ if not hasattr(module, app_name):
74
+ logger.error(f"No attribute '{app_name}' found in module '{module_name}'")
71
75
  return None
72
- logger.error(f"No attribute '{app_name}' found in module '{module_name}'")
73
- return None
76
+
77
+ app = getattr(module, app_name)
78
+
79
+ # Verify app type if framework is specified
80
+ if framework is not None:
81
+ if not _validate_app_framework(app, framework):
82
+ logger.error(f"'{app_name}' is not a {framework} instance")
83
+ return None
84
+
85
+ return app
74
86
  finally:
75
87
  # Cleanup: if we added the path, remove it
76
88
  if path_added and current_dir in sys.path:
77
89
  sys.path.remove(current_dir)
78
-
90
+
79
91
  except ImportError as e:
80
92
  logger.error(f"Could not import module '{module_name}': {e}")
81
93
  return None
82
94
  except Exception as e:
83
- logger.error(f"Error loading FastAPI app from module '{module_name}': {e}")
95
+ logger.error(f"Error loading app from module '{module_name}': {e}")
84
96
  return None
85
97
 
86
98
 
99
+ def _validate_app_framework(app: Any, framework: str) -> bool:
100
+ """Validate that the app matches the expected framework type."""
101
+ try:
102
+ if framework == "fastapi":
103
+ from fastapi import FastAPI
104
+ return isinstance(app, FastAPI)
105
+ elif framework == "litestar":
106
+ from litestar import Litestar
107
+ return isinstance(app, Litestar)
108
+ elif framework == "django-ninja":
109
+ from ninja import NinjaAPI
110
+ return isinstance(app, NinjaAPI)
111
+ return False
112
+ except ImportError as e:
113
+ logger.error(
114
+ f"The {framework} package is not installed. "
115
+ f"Install it with: uv add fastapi-voyager[{framework}]"
116
+ )
117
+ logger.debug(f"Import error details: {e}")
118
+ return False
119
+
120
+
87
121
  def generate_visualization(
88
- app: FastAPI,
122
+ app: Any,
89
123
  output_file: str = "router_viz.dot", tags: list[str] | None = None,
90
124
  schema: str | None = None,
91
125
  show_fields: bool = False,
@@ -93,7 +127,7 @@ def generate_visualization(
93
127
  route_name: str | None = None,
94
128
  ):
95
129
 
96
- """Generate DOT file for FastAPI router visualization."""
130
+ """Generate DOT file for API router visualization."""
97
131
  analytics = Voyager(
98
132
  include_tags=tags,
99
133
  schema=schema,
@@ -117,39 +151,46 @@ def generate_visualization(
117
151
  def main():
118
152
  """Main CLI entry point."""
119
153
  parser = argparse.ArgumentParser(
120
- description="Visualize FastAPI application's routing tree and dependencies",
154
+ description="Visualize web application's routing tree and dependencies (supports FastAPI, Litestar, Django-Ninja)",
121
155
  formatter_class=argparse.RawDescriptionHelpFormatter,
122
156
  epilog="""
123
157
  Examples:
124
- voyager app.py # Load 'app' from app.py
125
- voyager -m tests.demo # Load 'app' from demo module
126
- voyager -m tests.demo --app=app # Load 'app' from tests.demo
127
- voyager -m tests.demo --schema=NodeA # [str] filter nodes by schema name
128
- voyager -m tests.demo --tags=page restful # list[str] filter nodes route's tags
129
- voyager -m tests.demo --module_color=tests.demo:red --module_color=tests.service:yellow # list[str] filter nodes route's tags
130
- voyager -m tests.demo -o my_graph.dot # Output to my_graph.dot
131
- voyager -m tests.demo --server # start a local server to preview
132
- voyager -m tests.demo --server --port=8001 # start a local server to preview
158
+ voyager app.py --web fastapi # Load 'app' from app.py (FastAPI)
159
+ voyager app.py --web litestar # Load 'app' from app.py (Litestar)
160
+ voyager -m tests.demo --web django-ninja # Load 'app' from demo module (Django-Ninja)
161
+ voyager -m tests.demo --app=api --web fastapi # Load 'api' from tests.demo
162
+ voyager -m tests.demo --web fastapi --schema=NodeA # filter nodes by schema name
163
+ voyager -m tests.demo --web fastapi --tags=page restful # filter routes by tags
164
+ voyager -m tests.demo --web fastapi --module_color=tests.demo:red --module_color=tests.service:yellow
165
+ voyager -m tests.demo --web fastapi -o my_graph.dot # Output to my_graph.dot
166
+ voyager -m tests.demo --web fastapi --server # start a local server to preview
167
+ voyager -m tests.demo --web fastapi --server --port=8001 # start a local server to preview
133
168
  """
134
169
  )
135
-
170
+
136
171
  # Create mutually exclusive group for module loading options
137
172
  group = parser.add_mutually_exclusive_group(required=False)
138
173
  group.add_argument(
139
174
  "module",
140
175
  nargs="?",
141
- help="Python file containing the FastAPI application"
176
+ help="Python file containing the web application"
142
177
  )
143
178
  group.add_argument(
144
179
  "-m", "--module",
145
180
  dest="module_name",
146
- help="Python module name containing the FastAPI application (like python -m)"
181
+ help="Python module name containing the web application (like python -m)"
147
182
  )
148
-
183
+
184
+ parser.add_argument(
185
+ "--web",
186
+ choices=SUPPORTED_FRAMEWORKS,
187
+ help="Web framework type (required when using --server): fastapi, litestar, django-ninja"
188
+ )
189
+
149
190
  parser.add_argument(
150
191
  "--app", "-a",
151
192
  default="app",
152
- help="Name of the FastAPI app variable (default: app)"
193
+ help="Name of the app variable (default: app)"
153
194
  )
154
195
 
155
196
  parser.add_argument(
@@ -223,26 +264,34 @@ Examples:
223
264
  )
224
265
 
225
266
  args = parser.parse_args()
226
-
267
+
268
+ # Validate arguments
227
269
  if args.module_prefix and not args.server:
228
270
  parser.error("--module_prefix can only be used together with --server")
229
271
 
230
272
  if not (args.module_name or args.module):
231
- parser.error("You must provide a module file, -m module name")
273
+ parser.error("You must provide a module file or -m module name")
274
+
275
+ # When --server is used, --web is required
276
+ if args.server and not args.web:
277
+ parser.error("--web is required when using --server. Please specify: fastapi, litestar, or django-ninja")
278
+
279
+ # Determine the framework (default to the one specified, or None for non-server mode)
280
+ framework = args.web if args.server else None
232
281
 
233
282
  # Configure logging based on --log-level
234
283
  level_name = (args.log_level or "INFO").upper()
235
284
  logging.basicConfig(level=level_name)
236
285
 
237
- # Load FastAPI app based on the input method (module_name takes precedence)
286
+ # Load app based on the input method (module_name takes precedence)
238
287
  if args.module_name:
239
- app = load_fastapi_app_from_module(args.module_name, args.app)
288
+ app = load_app_from_module(args.module_name, args.app, framework)
240
289
  else:
241
290
  if not os.path.exists(args.module):
242
291
  logger.error(f"File '{args.module}' not found")
243
292
  sys.exit(1)
244
- app = load_fastapi_app_from_file(args.module, args.app)
245
-
293
+ app = load_app_from_file(args.module, args.app, framework)
294
+
246
295
  if app is None:
247
296
  sys.exit(1)
248
297
 
@@ -263,18 +312,21 @@ Examples:
263
312
  try:
264
313
  module_color = parse_kv_pairs(args.module_color)
265
314
  if args.server:
266
- # Build a preview server which computes DOT via Analytics using closure state
315
+ # Build a preview server using the appropriate framework
267
316
  try:
268
317
  import uvicorn
269
318
  except ImportError:
270
319
  logger.info("uvicorn is required to run the server. Install via 'pip install uvicorn' or 'uv add uvicorn'.")
271
320
  sys.exit(1)
321
+
322
+ # Create voyager app - it auto-detects framework and returns appropriate app type
272
323
  app_server = viz_server.create_voyager(
273
324
  app,
274
325
  module_color=module_color,
275
326
  module_prefix=args.module_prefix,
327
+ server_mode=True, # Enable server mode to serve at root path
276
328
  )
277
- logger.info(f"Starting preview server at http://{args.host}:{args.port} ... (Ctrl+C to stop)")
329
+ logger.info(f"Starting {args.web} preview server at http://{args.host}:{args.port} ... (Ctrl+C to stop)")
278
330
  uvicorn.run(app_server, host=args.host, port=args.port, log_level=level_name.lower())
279
331
  else:
280
332
  # Generate and write dot file locally
@@ -4,11 +4,13 @@ FastAPI implementation of the AppIntrospector interface.
4
4
  This module provides the adapter that allows fastapi-voyager to work with FastAPI applications.
5
5
  """
6
6
  from collections.abc import Iterator
7
-
8
- from fastapi import FastAPI, routing
7
+ from typing import TYPE_CHECKING, Any
9
8
 
10
9
  from fastapi_voyager.introspectors.base import AppIntrospector, RouteInfo
11
10
 
11
+ if TYPE_CHECKING:
12
+ from fastapi import FastAPI
13
+
12
14
 
13
15
  class FastAPIIntrospector(AppIntrospector):
14
16
  """
@@ -18,7 +20,7 @@ class FastAPIIntrospector(AppIntrospector):
18
20
  and converts it to the framework-agnostic RouteInfo format.
19
21
  """
20
22
 
21
- def __init__(self, app: FastAPI, swagger_url: str | None = None):
23
+ def __init__(self, app: "FastAPI", swagger_url: str | None = None):
22
24
  """
23
25
  Initialize the FastAPI introspector.
24
26
 
@@ -26,6 +28,12 @@ class FastAPIIntrospector(AppIntrospector):
26
28
  app: The FastAPI application instance
27
29
  swagger_url: Optional custom URL to Swagger documentation
28
30
  """
31
+ # Lazy import to avoid import errors when FastAPI is not installed
32
+ from fastapi import FastAPI
33
+
34
+ if not isinstance(app, FastAPI):
35
+ raise TypeError(f"Expected FastAPI instance, got {type(app)}")
36
+
29
37
  self.app = app
30
38
  self.swagger_url = swagger_url or "/docs"
31
39
 
@@ -36,6 +44,9 @@ class FastAPIIntrospector(AppIntrospector):
36
44
  Yields:
37
45
  RouteInfo: Standardized route information for each API route
38
46
  """
47
+ # Lazy import routing to avoid import errors when FastAPI is not installed
48
+ from fastapi import routing
49
+
39
50
  for route in self.app.routes:
40
51
  # Only process APIRoute instances (not static files, etc.)
41
52
  if isinstance(route, routing.APIRoute):
@@ -66,7 +77,7 @@ class FastAPIIntrospector(AppIntrospector):
66
77
  """
67
78
  return self.swagger_url
68
79
 
69
- def _get_route_id(self, route: routing.APIRoute) -> str:
80
+ def _get_route_id(self, route: Any) -> str:
70
81
  """
71
82
  Generate a unique identifier for the route.
72
83
 
fastapi_voyager/server.py CHANGED
@@ -25,6 +25,7 @@ def _get_adapter(
25
25
  ga_id: str | None = None,
26
26
  er_diagram: ErDiagram | None = None,
27
27
  enable_pydantic_resolve_meta: bool = False,
28
+ server_mode: bool = False,
28
29
  ) -> Any:
29
30
  """
30
31
  Get the appropriate adapter for the given target app.
@@ -64,6 +65,7 @@ def _get_adapter(
64
65
  ga_id=ga_id,
65
66
  er_diagram=er_diagram,
66
67
  enable_pydantic_resolve_meta=enable_pydantic_resolve_meta,
68
+ server_mode=server_mode,
67
69
  )
68
70
 
69
71
  elif framework == FrameworkType.LITESTAR:
@@ -78,6 +80,7 @@ def _get_adapter(
78
80
  ga_id=ga_id,
79
81
  er_diagram=er_diagram,
80
82
  enable_pydantic_resolve_meta=enable_pydantic_resolve_meta,
83
+ server_mode=server_mode,
81
84
  )
82
85
 
83
86
  elif framework == FrameworkType.DJANGO_NINJA:
@@ -92,6 +95,7 @@ def _get_adapter(
92
95
  ga_id=ga_id,
93
96
  er_diagram=er_diagram,
94
97
  enable_pydantic_resolve_meta=enable_pydantic_resolve_meta,
98
+ server_mode=server_mode,
95
99
  )
96
100
 
97
101
  # If we get here, the app type is not supported
@@ -114,6 +118,7 @@ def create_voyager(
114
118
  ga_id: str | None = None,
115
119
  er_diagram: ErDiagram | None = None,
116
120
  enable_pydantic_resolve_meta: bool = False,
121
+ server_mode: bool = False,
117
122
  ) -> Any:
118
123
  """
119
124
  Create a voyager UI application for the given target app.
@@ -136,6 +141,7 @@ def create_voyager(
136
141
  ga_id: Optional Google Analytics tracking ID
137
142
  er_diagram: Optional ER diagram from pydantic-resolve
138
143
  enable_pydantic_resolve_meta: Enable display of pydantic-resolve metadata
144
+ server_mode: If True, serve voyager UI at root path (for standalone preview mode)
139
145
 
140
146
  Returns:
141
147
  A framework-specific application object that provides the voyager UI
@@ -176,6 +182,7 @@ def create_voyager(
176
182
  ga_id=ga_id,
177
183
  er_diagram=er_diagram,
178
184
  enable_pydantic_resolve_meta=enable_pydantic_resolve_meta,
185
+ server_mode=server_mode,
179
186
  )
180
187
 
181
188
  return adapter.create_app()
@@ -1,2 +1,2 @@
1
1
  __all__ = ["__version__"]
2
- __version__ = "0.16.0alpha-1"
2
+ __version__ = "0.16.0alpha-3"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-voyager
3
- Version: 0.16.0a1
3
+ Version: 0.16.0a3
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
@@ -20,15 +20,33 @@ Classifier: Programming Language :: Python :: 3.14
20
20
  Requires-Python: >=3.10
21
21
  Requires-Dist: jinja2>=3.0.0
22
22
  Requires-Dist: pydantic-resolve>=2.4.3
23
+ Provides-Extra: all
24
+ Requires-Dist: django-ninja>=1.5.3; extra == 'all'
25
+ Requires-Dist: django>=4.2; extra == 'all'
26
+ Requires-Dist: fastapi>=0.110; extra == 'all'
27
+ Requires-Dist: httpx; extra == 'all'
28
+ Requires-Dist: litestar>=2.19.0; extra == 'all'
29
+ Requires-Dist: pydantic>=2.0; extra == 'all'
30
+ Requires-Dist: pytest; extra == 'all'
31
+ Requires-Dist: pytest-asyncio; extra == 'all'
32
+ Requires-Dist: ruff; extra == 'all'
33
+ Requires-Dist: uvicorn; extra == 'all'
23
34
  Provides-Extra: dev
24
- Requires-Dist: django-ninja; extra == 'dev'
25
- Requires-Dist: fastapi>=0.110; extra == 'dev'
26
35
  Requires-Dist: httpx; extra == 'dev'
27
- Requires-Dist: litestar; extra == 'dev'
28
36
  Requires-Dist: pytest; extra == 'dev'
29
37
  Requires-Dist: pytest-asyncio; extra == 'dev'
30
38
  Requires-Dist: ruff; extra == 'dev'
31
- Requires-Dist: uvicorn; extra == 'dev'
39
+ Provides-Extra: django-ninja
40
+ Requires-Dist: django-ninja>=1.5.3; extra == 'django-ninja'
41
+ Requires-Dist: django>=4.2; extra == 'django-ninja'
42
+ Requires-Dist: uvicorn; extra == 'django-ninja'
43
+ Provides-Extra: fastapi
44
+ Requires-Dist: fastapi>=0.110; extra == 'fastapi'
45
+ Requires-Dist: uvicorn; extra == 'fastapi'
46
+ Provides-Extra: litestar
47
+ Requires-Dist: litestar>=2.19.0; extra == 'litestar'
48
+ Requires-Dist: pydantic>=2.0; extra == 'litestar'
49
+ Requires-Dist: uvicorn; extra == 'litestar'
32
50
  Description-Content-Type: text/markdown
33
51
 
34
52
  [![pypi](https://img.shields.io/pypi/v/fastapi-voyager.svg)](https://pypi.python.org/pypi/fastapi-voyager)
@@ -301,8 +319,14 @@ Set `enable_pydantic_resolve_meta=True` in `create_voyager`, then toggle the "py
301
319
  ### Start Server
302
320
 
303
321
  ```bash
304
- # Open in browser (default port 8000)
305
- voyager -m tests.demo --server
322
+ # FastAPI
323
+ voyager -m tests.demo --server --web fastapi
324
+
325
+ # Django Ninja
326
+ voyager -m tests.demo --server --web django-ninja
327
+
328
+ # Litestar
329
+ voyager -m tests.demo --server --web litestar
306
330
 
307
331
  # Custom port
308
332
  voyager -m tests.demo --server --port=8002
@@ -1,27 +1,27 @@
1
1
  fastapi_voyager/__init__.py,sha256=1IdDy6JUMgQqQo33qUe2znX9YeI7S35pjVrbt0QLOzY,228
2
- fastapi_voyager/cli.py,sha256=td3yIIigEomhSdDO-Xkh-CgpEwCafwlwnpvxnT9QsBo,10488
2
+ fastapi_voyager/cli.py,sha256=eD2QmfSVOQjF_Y9WpnQPUjlMpfMU83DfrHp0BYe9xcw,12439
3
3
  fastapi_voyager/er_diagram.py,sha256=4Ba5u-T7XmVmk9MltVMe-m-18mUAHMTybTdE-zNZLrU,7860
4
4
  fastapi_voyager/filter.py,sha256=AN_HIu8-DtKisIq5mFt7CnqRHtxKewedNGyyaI82hSY,11529
5
5
  fastapi_voyager/module.py,sha256=h9YR3BpS-CAcJW9WCdVkF4opqwY32w9T67g9GfdLytk,3425
6
6
  fastapi_voyager/pydantic_resolve_util.py,sha256=0UfAp6Yi6FNpsI1bUu89hRVWFy6keBu1KtcZl-6NYso,3526
7
7
  fastapi_voyager/render.py,sha256=A1jFDraQFOfnFHguYlsvBbGIDJ527VQH0jZ-xgTjqIk,17270
8
8
  fastapi_voyager/render_style.py,sha256=1y3aRhBSJSWU-JuSgjn9il_xFEqjv6mJCoUzImLQT6M,2525
9
- fastapi_voyager/server.py,sha256=sgUUscbt736VNB6CN2J_6rJ6fpWO4vJFW7oP7FmAjvA,6739
9
+ fastapi_voyager/server.py,sha256=qHCN5EqoFwqvIGsMX-ALiogUc4x0xWLpVFpgO06KZvA,7035
10
10
  fastapi_voyager/type.py,sha256=zluWvh5vpnjXJ9aAmyNJTSmXZPjAHCvgRT5oQRAjHrg,2104
11
11
  fastapi_voyager/type_helper.py,sha256=5HYUHdghTISZ44NvVsrMWlRGD5C9-xrGeNKuLYDMA6s,10209
12
- fastapi_voyager/version.py,sha256=0KZ1gUzWpAnbQTYDMdcLc-qEsyVlYlG265SEa7S4Ij4,56
12
+ fastapi_voyager/version.py,sha256=zh4QycBWD2nGHz_cZwy5vHQ1KDWxkhAdF_-JPiTVFzU,56
13
13
  fastapi_voyager/voyager.py,sha256=S0cCMLFv_wPfEMVdp4cYqbc-Y207SjCTSmsYdqzIDHg,15307
14
14
  fastapi_voyager/adapters/__init__.py,sha256=a95rBvV4QVcX_yAzjuJQKOW-EbJ79R--YjUJ3BGkC3k,517
15
- fastapi_voyager/adapters/base.py,sha256=m-E74LlNgYCpj-gqfYsKl2mzWW5iFiaDTjiBri_5cfo,1283
15
+ fastapi_voyager/adapters/base.py,sha256=mTYCnYYR4VtA7a-cTJmhsT_Up0hgxLC0hBzUbYxQCJQ,1039
16
16
  fastapi_voyager/adapters/common.py,sha256=DXVLsjLn65U3RR7YyKL_ELV74mnINWmZ4hQ6zISdV0E,9684
17
- fastapi_voyager/adapters/django_ninja_adapter.py,sha256=tl1rMcotAhOwBxT8poG8rTXm1v3wn_cpaoda79NEFxE,11424
18
- fastapi_voyager/adapters/fastapi_adapter.py,sha256=-VvCXdMRsV_zZzEkplOGK8HjQ9ICoTyapG3ZluQ1Nvk,5870
19
- fastapi_voyager/adapters/litestar_adapter.py,sha256=-ILD8jIb82_JKV87p4IfV9IXvhUJ6RSic6i_iHapo5w,6812
17
+ fastapi_voyager/adapters/django_ninja_adapter.py,sha256=8-BMGxAKr8-ScrYB070cn4Te9caMvJhj5mta_YpOA3k,11573
18
+ fastapi_voyager/adapters/fastapi_adapter.py,sha256=qcbXTpaQYrFJJwCsxDx8LGAGKT_Os1I3ReBvLD39jEI,6038
19
+ fastapi_voyager/adapters/litestar_adapter.py,sha256=XGm-NUoD4a1sHl3zpDAj-Q6kzymANAIaV2GM5pgZ0tM,6963
20
20
  fastapi_voyager/introspectors/__init__.py,sha256=HbmoUyM-BSwd4Rg2ptd9u-qvZSD3UykKyHYVoRg03OM,917
21
21
  fastapi_voyager/introspectors/base.py,sha256=hMfka9gVXr-E8MA1rKSSmYk0OppqgiFPWavfgAkPmQI,2131
22
22
  fastapi_voyager/introspectors/detector.py,sha256=rmlpQARJMrFXPty6OUdjmFRTtBYErGDH4l7Tivnu_FQ,3910
23
23
  fastapi_voyager/introspectors/django_ninja.py,sha256=Ytneh_kpsakTo_Njv1e4nvqfErjT1PrYelLZ-8xX5Gg,4052
24
- fastapi_voyager/introspectors/fastapi.py,sha256=SyWGKAH8cM3CENY-Uu3YY6e8kFM5BE-jEBBZLoIF--A,2696
24
+ fastapi_voyager/introspectors/fastapi.py,sha256=TGr1VPO0f6y-w3ZvQpI15o4Jq5L7VcN_twqzY_33fz4,3085
25
25
  fastapi_voyager/introspectors/litestar.py,sha256=QXnaT0-hCa_0sByKJoUWuu0vIzRpCCKLokCBDTtv_s4,6100
26
26
  fastapi_voyager/templates/dot/cluster.j2,sha256=I2z9KkfCzmAtqXe0gXBnxnOfBXUSpdlATs3uf-O8_B8,307
27
27
  fastapi_voyager/templates/dot/cluster_container.j2,sha256=2tH1mOJvPoVKE_aHVMR3t06TfH_dYa9OeH6DBqSHt_A,204
@@ -55,8 +55,8 @@ fastapi_voyager/web/icon/favicon-16x16.png,sha256=JC07jEzfIYxBIoQn_FHXvyHuxESdhW
55
55
  fastapi_voyager/web/icon/favicon-32x32.png,sha256=C7v1h58cfWOsiLp9yOIZtlx-dLasBcq3NqpHVGRmpt4,1859
56
56
  fastapi_voyager/web/icon/favicon.ico,sha256=tZolYIXkkBcFiYl1A8ksaXN2VjGamzcSdes838dLvNc,15406
57
57
  fastapi_voyager/web/icon/site.webmanifest,sha256=GRozZ5suTykYcPMap1QhjrAB8PLW0mbT_phhzw_utvQ,316
58
- fastapi_voyager-0.16.0a1.dist-info/METADATA,sha256=pGOq7n96lV1BKAXvCKPXUU8BvXN89HzgAMI90dmnH_Y,12943
59
- fastapi_voyager-0.16.0a1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
60
- fastapi_voyager-0.16.0a1.dist-info/entry_points.txt,sha256=pEIKoUnIDXEtdMBq8EmXm70m16vELIu1VPz9-TBUFWM,53
61
- fastapi_voyager-0.16.0a1.dist-info/licenses/LICENSE,sha256=lNVRR3y_bFVoFKuK2JM8N4sFaj3m-7j29kvL3olFi5Y,1067
62
- fastapi_voyager-0.16.0a1.dist-info/RECORD,,
58
+ fastapi_voyager-0.16.0a3.dist-info/METADATA,sha256=v39CCqmxr4ls6J4m9--3R1vPTTBFnAkEBX0BHY9Tgyo,13811
59
+ fastapi_voyager-0.16.0a3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
60
+ fastapi_voyager-0.16.0a3.dist-info/entry_points.txt,sha256=pEIKoUnIDXEtdMBq8EmXm70m16vELIu1VPz9-TBUFWM,53
61
+ fastapi_voyager-0.16.0a3.dist-info/licenses/LICENSE,sha256=lNVRR3y_bFVoFKuK2JM8N4sFaj3m-7j29kvL3olFi5Y,1067
62
+ fastapi_voyager-0.16.0a3.dist-info/RECORD,,