svc-infra 0.1.619__py3-none-any.whl → 0.1.620__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.

Potentially problematic release.


This version of svc-infra might be problematic. Click here for more details.

@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import importlib.util
3
4
  import os
4
5
  from importlib.metadata import PackageNotFoundError, distribution
5
6
  from pathlib import Path
@@ -24,31 +25,50 @@ def _discover_fs_topics(docs_dir: Path) -> Dict[str, Path]:
24
25
  def _discover_pkg_topics() -> Dict[str, Path]:
25
26
  """Discover docs packaged under 'docs/' in the installed distribution.
26
27
 
27
- This lets 'svc-infra docs' work from external projects that don't have a
28
- local docs/ directory by falling back to files shipped in the wheel.
28
+ Works in external projects without a local docs/ by inspecting the wheel
29
+ metadata and, as a fallback, searching for a top-level docs/ next to the
30
+ installed package directory in site-packages.
29
31
  """
30
32
  topics: Dict[str, Path] = {}
31
- try:
32
- dist = distribution("svc-infra")
33
- except PackageNotFoundError:
34
- return topics
35
-
36
- files = getattr(dist, "files", None) or []
37
- for f in files:
38
- # f is a PackagePath; string form like 'docs/topic.md'
39
- s = str(f)
40
- if not s.startswith("docs/") or not s.endswith(".md"):
41
- continue
42
- name = Path(s).stem.replace(" ", "-")
33
+
34
+ # 1) Prefer distribution metadata (RECORD) for both hyphen/underscore names
35
+ dist = None
36
+ for name in ("svc-infra", "svc_infra"):
43
37
  try:
44
- abs_path = dist.locate_file(f)
45
- # Ensure it's a file before adding
46
- abs_p = Path(abs_path)
47
- if abs_p.exists() and abs_p.is_file():
48
- topics[name] = abs_p
49
- except Exception:
50
- # best-effort; skip unreadable entries
51
- continue
38
+ dist = distribution(name)
39
+ break
40
+ except PackageNotFoundError:
41
+ dist = None
42
+
43
+ if dist is not None:
44
+ files = getattr(dist, "files", None) or []
45
+ for f in files:
46
+ s = str(f)
47
+ if not s.startswith("docs/") or not s.endswith(".md"):
48
+ continue
49
+ topic_name = Path(s).stem.replace(" ", "-")
50
+ try:
51
+ abs_path = Path(dist.locate_file(f))
52
+ if abs_path.exists() and abs_path.is_file():
53
+ topics[topic_name] = abs_path
54
+ except Exception:
55
+ # Best effort; continue to next
56
+ continue
57
+
58
+ # 2) Fallback: site-packages sibling 'docs/' directory
59
+ try:
60
+ spec = importlib.util.find_spec("svc_infra")
61
+ if spec and spec.submodule_search_locations:
62
+ pkg_dir = Path(next(iter(spec.submodule_search_locations)))
63
+ candidate = pkg_dir.parent / "docs"
64
+ if candidate.exists() and candidate.is_dir():
65
+ for p in sorted(candidate.glob("*.md")):
66
+ if p.is_file():
67
+ topics.setdefault(p.stem.replace(" ", "-"), p)
68
+ except Exception:
69
+ # Optional fallback only
70
+ pass
71
+
52
72
  return topics
53
73
 
54
74
 
@@ -57,19 +77,21 @@ def _resolve_docs_dir(ctx: click.Context) -> Path | None:
57
77
  # executes subcommands in child contexts that do not inherit params.
58
78
  current: click.Context | None = ctx
59
79
  while current is not None:
60
- docs_dir = (current.params or {}).get("docs_dir")
61
- if docs_dir:
62
- path = docs_dir if isinstance(docs_dir, Path) else Path(docs_dir)
80
+ docs_dir_opt = (current.params or {}).get("docs_dir")
81
+ if docs_dir_opt:
82
+ path = docs_dir_opt if isinstance(docs_dir_opt, Path) else Path(docs_dir_opt)
63
83
  path = path.expanduser()
64
84
  if path.exists():
65
85
  return path
66
86
  current = current.parent
87
+
67
88
  # Env var next
68
89
  env_dir = os.getenv("SVC_INFRA_DOCS_DIR")
69
90
  if env_dir:
70
91
  p = Path(env_dir).expanduser()
71
92
  if p.exists():
72
93
  return p
94
+
73
95
  # Project docs
74
96
  root = resolve_project_root()
75
97
  proj_docs = root / "docs"
@@ -88,16 +110,15 @@ class DocsGroup(TyperGroup):
88
110
  names.extend([k for k in fs.keys()])
89
111
  names.extend([k for k in pkg.keys() if k not in fs])
90
112
  # Deduplicate and sort
91
- uniq = sorted({*names})
92
- return uniq
113
+ return sorted({*names})
93
114
 
94
115
  def get_command(self, ctx: click.Context, name: str) -> click.Command | None:
95
- # Built-ins first (e.g., list)
116
+ # Built-ins first (e.g., list, show)
96
117
  cmd = super().get_command(ctx, name)
97
118
  if cmd is not None:
98
119
  return cmd
99
120
 
100
- # Dynamic topic resolution
121
+ # Dynamic topic resolution from FS
101
122
  dir_to_use = _resolve_docs_dir(ctx)
102
123
  fs = _discover_fs_topics(dir_to_use) if dir_to_use else {}
103
124
  if name in fs:
@@ -145,6 +166,10 @@ def register(app: typer.Typer) -> None:
145
166
  if topic in fs:
146
167
  typer.echo(fs[topic].read_text(encoding="utf-8", errors="replace"))
147
168
  raise typer.Exit(code=0)
169
+ pkg = _discover_pkg_topics()
170
+ if topic in pkg:
171
+ typer.echo(pkg[topic].read_text(encoding="utf-8", errors="replace"))
172
+ raise typer.Exit(code=0)
148
173
  raise typer.BadParameter(f"Unknown topic: {topic}")
149
174
 
150
175
  @docs_app.command("list", help="List available documentation topics")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: svc-infra
3
- Version: 0.1.619
3
+ Version: 0.1.620
4
4
  Summary: Infrastructure for building and deploying prod-ready services
5
5
  License: MIT
6
6
  Keywords: fastapi,sqlalchemy,alembic,auth,infra,async,pydantic
@@ -142,7 +142,7 @@ svc_infra/cli/cmds/db/sql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
142
142
  svc_infra/cli/cmds/db/sql/alembic_cmds.py,sha256=uCreHg69Zf6B5gbv9Dm39jCRk6q2KQy_05A-75IP0Fg,9650
143
143
  svc_infra/cli/cmds/db/sql/sql_export_cmds.py,sha256=YpkguUJFeFApMphVkhOJllTi25ejlsQaJarMe6vJD54,2685
144
144
  svc_infra/cli/cmds/db/sql/sql_scaffold_cmds.py,sha256=MKc_T_tY1Y_wQl7XTlq8GhYWMMI1q1_vcFZVPOEcNUg,4601
145
- svc_infra/cli/cmds/docs/docs_cmds.py,sha256=nvqxkqeYNSV79Eeq0Kv8PN1M8CbyS3lnrFNjHNlO53w,6661
145
+ svc_infra/cli/cmds/docs/docs_cmds.py,sha256=vRDhddel_X9MCfCVRvV7_Ns7sB3awfwSVWV0HqKLRKg,7653
146
146
  svc_infra/cli/cmds/dx/__init__.py,sha256=wQtl3-kOgoESlpVkjl3YFtqkOnQSIvVsOdutiaZFejM,197
147
147
  svc_infra/cli/cmds/dx/dx_cmds.py,sha256=XTKUJzS3UIYn6h3CHzDEWKYJaWn0TzGiUCq3OeW27E0,3326
148
148
  svc_infra/cli/cmds/help.py,sha256=wGfZFMYaR2ZPwW2JwKDU7M3m4AtdCd8GRQ412AmEBUM,758
@@ -296,7 +296,7 @@ svc_infra/webhooks/fastapi.py,sha256=BCNvGNxukf6dC2a4i-6en-PrjBGV19YvCWOot5lXWsA
296
296
  svc_infra/webhooks/router.py,sha256=6JvAVPMEth_xxHX-IsIOcyMgHX7g1H0OVxVXKLuMp9w,1596
297
297
  svc_infra/webhooks/service.py,sha256=hWgiJRXKBwKunJOx91C7EcLUkotDtD3Xp0RT6vj2IC0,1797
298
298
  svc_infra/webhooks/signing.py,sha256=NCwdZzmravUe7HVIK_uXK0qqf12FG-_MVsgPvOw6lsM,784
299
- svc_infra-0.1.619.dist-info/METADATA,sha256=yoqsuCYdTjgJVgqMPZDDVIjbd5xDW7KQ7XbNRlbsPfA,8106
300
- svc_infra-0.1.619.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
301
- svc_infra-0.1.619.dist-info/entry_points.txt,sha256=6x_nZOsjvn6hRZsMgZLgTasaCSKCgAjsGhACe_CiP0U,48
302
- svc_infra-0.1.619.dist-info/RECORD,,
299
+ svc_infra-0.1.620.dist-info/METADATA,sha256=Ii8wzmysVqUARGfBUES-2TQIMzhyeAGvGimkSTMIpYg,8106
300
+ svc_infra-0.1.620.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
301
+ svc_infra-0.1.620.dist-info/entry_points.txt,sha256=6x_nZOsjvn6hRZsMgZLgTasaCSKCgAjsGhACe_CiP0U,48
302
+ svc_infra-0.1.620.dist-info/RECORD,,