fastapi-voyager 0.11.2__tar.gz → 0.11.4__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 (51) hide show
  1. fastapi_voyager-0.11.4/.github/workflows/publish.yml +32 -0
  2. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/PKG-INFO +5 -2
  3. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/README.md +4 -1
  4. fastapi_voyager-0.11.4/release.md +8 -0
  5. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/server.py +9 -2
  6. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/type_helper.py +6 -2
  7. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/version.py +1 -1
  8. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/web/component/schema-code-display.js +39 -13
  9. fastapi_voyager-0.11.4/tests/programatic.py +11 -0
  10. fastapi_voyager-0.11.2/tests/programatic.py +0 -5
  11. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/.gitignore +0 -0
  12. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/.python-version +0 -0
  13. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/LICENSE +0 -0
  14. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/pyproject.toml +0 -0
  15. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/__init__.py +0 -0
  16. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/cli.py +0 -0
  17. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/filter.py +0 -0
  18. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/module.py +0 -0
  19. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/render.py +0 -0
  20. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/type.py +0 -0
  21. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/voyager.py +0 -0
  22. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/web/component/render-graph.js +0 -0
  23. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/web/component/route-code-display.js +0 -0
  24. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/web/component/schema-field-filter.js +0 -0
  25. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/web/graph-ui.js +0 -0
  26. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/web/graphviz.svg.css +0 -0
  27. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/web/graphviz.svg.js +0 -0
  28. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/web/icon/android-chrome-192x192.png +0 -0
  29. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/web/icon/android-chrome-512x512.png +0 -0
  30. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/web/icon/apple-touch-icon.png +0 -0
  31. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/web/icon/favicon-16x16.png +0 -0
  32. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/web/icon/favicon-32x32.png +0 -0
  33. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/web/icon/favicon.ico +0 -0
  34. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/web/icon/site.webmanifest +0 -0
  35. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/web/index.html +0 -0
  36. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/web/quasar.min.css +0 -0
  37. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/web/quasar.min.js +0 -0
  38. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/src/fastapi_voyager/web/vue-main.js +0 -0
  39. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/tests/__init__.py +0 -0
  40. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/tests/demo.py +0 -0
  41. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/tests/demo_anno.py +0 -0
  42. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/tests/service/__init__.py +0 -0
  43. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/tests/service/schema.py +0 -0
  44. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/tests/test_analysis.py +0 -0
  45. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/tests/test_filter.py +0 -0
  46. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/tests/test_generic.py +0 -0
  47. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/tests/test_import.py +0 -0
  48. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/tests/test_module.py +0 -0
  49. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/tests/test_type_helper.py +0 -0
  50. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/uv.lock +0 -0
  51. {fastapi_voyager-0.11.2 → fastapi_voyager-0.11.4}/voyager.jpg +0 -0
@@ -0,0 +1,32 @@
1
+ name: Publish to PyPI via uv
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ push:
6
+ tags:
7
+ - "v*"
8
+
9
+ jobs:
10
+ publish:
11
+ runs-on: ubuntu-latest
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ steps:
17
+ - name: Checkout repository
18
+ uses: actions/checkout@v4
19
+
20
+ - name: Set up uv
21
+ uses: astral-sh/setup-uv@v4
22
+
23
+ - name: Set up Python
24
+ uses: actions/setup-python@v5
25
+ with:
26
+ python-version: "3.13"
27
+
28
+ - name: Build the package
29
+ run: uv build
30
+
31
+ - name: Publish to PyPI
32
+ run: uv publish --token ${{ secrets.PYPI_PUBLISHER }}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-voyager
3
- Version: 0.11.2
3
+ Version: 0.11.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
@@ -270,7 +270,10 @@ or you can open router_viz.dot with vscode extension `graphviz interactive previ
270
270
  - 0.11.2
271
271
  - [x] enable/disable module cluster (to save space)
272
272
  - 0.11.3
273
- - [ ] add loading for field detail panel
273
+ - [x] support online repo url
274
+ - 0.11.4
275
+ - [x] add loading for field detail panel
276
+ - 0.11.5
274
277
  - [ ] logging information
275
278
  - [ ] sort field name
276
279
  - [ ] set max limit for fields
@@ -241,7 +241,10 @@ or you can open router_viz.dot with vscode extension `graphviz interactive previ
241
241
  - 0.11.2
242
242
  - [x] enable/disable module cluster (to save space)
243
243
  - 0.11.3
244
- - [ ] add loading for field detail panel
244
+ - [x] support online repo url
245
+ - 0.11.4
246
+ - [x] add loading for field detail panel
247
+ - 0.11.5
245
248
  - [ ] logging information
246
249
  - [ ] sort field name
247
250
  - [ ] set max limit for fields
@@ -0,0 +1,8 @@
1
+ release by pushing the tag
2
+
3
+ ```shell
4
+ git tag v1.0.0
5
+ git push origin v1.0.0
6
+ ```
7
+
8
+
@@ -41,6 +41,7 @@ def create_route(
41
41
  module_color: dict[str, str] | None = None,
42
42
  swagger_url: Optional[str] = None,
43
43
  module_prefix: Optional[str] = None,
44
+ online_repo_url: Optional[str] = None,
44
45
  ):
45
46
  """
46
47
  module_color: dict mapping module name to color string, e.g. {'models': 'lightblue'}
@@ -183,7 +184,7 @@ def create_route(
183
184
 
184
185
  mod = __import__(module_name, fromlist=[class_name])
185
186
  obj = getattr(mod, class_name)
186
- link = get_vscode_link(obj)
187
+ link = get_vscode_link(obj, online_repo_url=online_repo_url)
187
188
 
188
189
  return JSONResponse(content={"link": link})
189
190
  except ImportError as e:
@@ -211,8 +212,14 @@ def create_voyager(
211
212
  gzip_minimum_size: int | None = 500,
212
213
  module_prefix: Optional[str] = None,
213
214
  swagger_url: Optional[str] = None,
215
+ online_repo_url: Optional[str] = None,
214
216
  ) -> FastAPI:
215
- router = create_route(target_app, module_color=module_color, module_prefix=module_prefix, swagger_url=swagger_url)
217
+ router = create_route(
218
+ target_app,
219
+ module_color=module_color,
220
+ module_prefix=module_prefix,
221
+ swagger_url=swagger_url,
222
+ online_repo_url=online_repo_url)
216
223
 
217
224
  app = FastAPI(title="fastapi-voyager demo server")
218
225
  if gzip_minimum_size is not None and gzip_minimum_size >= 0:
@@ -1,7 +1,7 @@
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, Type, Generic
4
+ from typing import get_origin, get_args, Union, Annotated, Any, Type, Generic, Optional
5
5
  from fastapi_voyager.type import FieldInfo
6
6
  from types import UnionType
7
7
  import pydantic_resolve.constant as const
@@ -182,7 +182,7 @@ def get_pydantic_fields(schema: type[BaseModel], bases_fields: set[str]) -> list
182
182
  return fields
183
183
 
184
184
 
185
- def get_vscode_link(kls):
185
+ def get_vscode_link(kls, online_repo_url: Optional[str] = None) -> str:
186
186
  """Build a VSCode deep link to the class definition.
187
187
 
188
188
  Priority:
@@ -197,6 +197,10 @@ def get_vscode_link(kls):
197
197
  _lines, start_line = inspect.getsourcelines(kls)
198
198
 
199
199
  distro = os.environ.get("WSL_DISTRO_NAME")
200
+ if online_repo_url:
201
+ cwd = os.getcwd()
202
+ relative_path = os.path.relpath(source_file, cwd)
203
+ return f"{online_repo_url}/{relative_path}#L{start_line}"
200
204
  if distro:
201
205
  # Ensure absolute path (it should already be under /) and build remote link
202
206
  return f"vscode://vscode-remote/wsl+{distro}{source_file}:{start_line}"
@@ -1,2 +1,2 @@
1
1
  __all__ = ["__version__"]
2
- __version__ = "0.11.2"
2
+ __version__ = "0.11.4"
@@ -14,6 +14,8 @@ export default defineComponent({
14
14
  props: {
15
15
  schemaName: { type: String, required: true },
16
16
  schemas: { type: Object, default: () => ({}) },
17
+ // visibility from parent (e.g., dialog v-model)
18
+ modelValue: { type: Boolean, default: true },
17
19
  },
18
20
  setup(props, { emit }) {
19
21
  const code = ref("");
@@ -21,6 +23,7 @@ export default defineComponent({
21
23
  const error = ref("");
22
24
  const fields = ref([]); // schema fields list
23
25
  const tab = ref("fields");
26
+ const loading = ref(false);
24
27
 
25
28
 
26
29
  async function highlightLater() {
@@ -45,12 +48,22 @@ export default defineComponent({
45
48
  });
46
49
  }
47
50
 
51
+ function resetState() {
52
+ code.value = "";
53
+ link.value = "";
54
+ error.value = null;
55
+ fields.value = [];
56
+ // tab.value = "fields";
57
+ loading.value = true;
58
+ }
59
+
48
60
  async function loadSource() {
49
61
  if (!props.schemaName) return;
50
62
 
51
63
  error.value = null;
52
64
  code.value = "";
53
65
  link.value = "";
66
+ loading.value = true;
54
67
 
55
68
  // try to fetch from server: /source/{schema_name}
56
69
  const payload = { schema_name: props.schemaName };
@@ -71,13 +84,8 @@ export default defineComponent({
71
84
  } else {
72
85
  error.value = (data && data.error) || "Failed to load source";
73
86
  }
74
- } catch (e) {
75
- error.value = "Failed to load source";
76
- } finally {
77
- }
78
87
 
79
- try {
80
- const resp = await fetch(`vscode-link`, {
88
+ const resp2 = await fetch(`vscode-link`, {
81
89
  method: "POST",
82
90
  headers: {
83
91
  Accept: "application/json",
@@ -85,16 +93,16 @@ export default defineComponent({
85
93
  },
86
94
  body: JSON.stringify(payload),
87
95
  });
88
- // surface server-side validation message for bad request
89
- const data = await resp.json().catch(() => ({}));
90
- if (resp.ok) {
91
- link.value = data.link || "// no vscode link available";
96
+ const data2 = await resp2.json().catch(() => ({}));
97
+ if (resp2.ok) {
98
+ link.value = data2.link || "// no vscode link available";
92
99
  } else {
93
- error.value += (data && data.error) || "Failed to load source";
100
+ error.value = (error.value || "") + ((data2 && data2.error) || "Failed to load source");
94
101
  }
95
102
  } catch (e) {
96
103
  error.value = "Failed to load source";
97
104
  } finally {
105
+ loading.value = false;
98
106
  }
99
107
 
100
108
  const schema = props.schemas && props.schemas[props.schemaName];
@@ -118,18 +126,36 @@ export default defineComponent({
118
126
  watch(
119
127
  () => props.schemaName,
120
128
  () => {
129
+ resetState();
121
130
  loadSource();
122
131
  },
123
132
  );
124
133
 
134
+ // respond to visibility changes: when shown, clear old data and reload
135
+ watch(
136
+ () => props.modelValue,
137
+ (val) => {
138
+ if (val) {
139
+ resetState();
140
+ loadSource();
141
+ }
142
+ }
143
+ );
144
+
125
145
  onMounted(() => {
126
- loadSource();
146
+ if (props.modelValue) {
147
+ resetState();
148
+ loadSource();
149
+ }
127
150
  });
128
151
 
129
- return { link, code, error, fields, tab };
152
+ return { link, code, error, fields, tab, loading };
130
153
  },
131
154
  template: `
132
155
  <div class="frv-code-display" style="border: 1px solid #ccc; border-left: none; position:relative; height:100%; background:#fff;">
156
+ <div v-show="loading" style="position:absolute; top:0; left:0; right:0; z-index:10;">
157
+ <q-linear-progress indeterminate color="primary" size="2px"/>
158
+ </div>
133
159
  <div class="q-ml-lg q-mt-md">
134
160
  <a :href="link" target="_blank" rel="noopener" style="font-size:12px; color:#3b82f6;">
135
161
  Open in VSCode
@@ -0,0 +1,11 @@
1
+ from fastapi_voyager import create_voyager
2
+ from tests.demo import app
3
+
4
+ app.mount(
5
+ '/voyager',
6
+ create_voyager(
7
+ app,
8
+ module_color={"tests.service": "red"},
9
+ module_prefix="tests.service",
10
+ swagger_url="/docs",
11
+ online_repo_url="https://github.com/allmonday/fastapi-voyager/blob/main"))
@@ -1,5 +0,0 @@
1
- from fastapi_voyager import create_voyager
2
- from tests.demo import app
3
-
4
- app.mount('/voyager', create_voyager(app, module_color={"tests.service": "red"}, module_prefix="tests.service", swagger_url='/docs'))
5
- # app.mount('/voyager', create_voyager(app, module_color={"tests.service": "red"}))