sage-docs 0.1.0.2__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.
@@ -0,0 +1,13 @@
1
+ Metadata-Version: 2.4
2
+ Name: sage-docs
3
+ Version: 0.1.0.2
4
+ Summary: SAGE public documentation site
5
+ Requires-Python: >=3.11
6
+ Provides-Extra: docs
7
+ Requires-Dist: zensical>=0.0.24; extra == "docs"
8
+ Provides-Extra: test
9
+ Requires-Dist: pytest>=8.0; extra == "test"
10
+ Provides-Extra: full
11
+ Requires-Dist: sage-docs[docs,test]; extra == "full"
12
+ Provides-Extra: dev
13
+ Requires-Dist: sage-docs[full]; extra == "dev"
@@ -0,0 +1,41 @@
1
+ # sage-docs
2
+
3
+ 本仓库用于发布和维护 [SAGE](https://github.com/intellistream/SAGE) 的对外文档。
4
+
5
+ ## 文档入口
6
+
7
+ - 在线文档:https://sage.org.ai/
8
+
9
+ 入口约定:
10
+
11
+ - `sage.org.ai` 是 SAGE 产品与文档主入口
12
+ - `lab.sage.org.ai` 是 IntelliStream 组织门户
13
+ - `github.com/intellistream` 是代码与仓库入口
14
+
15
+ ## 内容范围
16
+
17
+ - 快速开始与教程
18
+ - 架构与模块说明
19
+ - 用户指南与公开 API 文档
20
+ - 社区与贡献信息
21
+
22
+ ## 源码访问说明
23
+
24
+ 本仓库仅包含文档内容,不包含 SAGE 核心源码。
25
+
26
+ ## 本地构建(维护者)
27
+
28
+ ```bash
29
+ # 一键构建
30
+ ./build.sh
31
+
32
+ # 本地预览
33
+ zensical serve -a 127.0.0.1:9000
34
+
35
+ # 手动构建
36
+ zensical build --clean
37
+ ```
38
+
39
+ ## 引用
40
+
41
+ 如需学术引用,请以在线文档中的最新引用说明为准。
@@ -0,0 +1,31 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "sage-docs"
7
+ version = "0.1.0.2"
8
+ description = "SAGE public documentation site"
9
+ requires-python = ">=3.11"
10
+
11
+ dependencies = []
12
+
13
+ [project.optional-dependencies]
14
+ docs = [
15
+ "zensical>=0.0.24",
16
+ ]
17
+
18
+ test = [
19
+ "pytest>=8.0",
20
+ ]
21
+
22
+ full = [
23
+ "sage-docs[docs,test]",
24
+ ]
25
+
26
+ dev = [
27
+ "sage-docs[full]",
28
+ ]
29
+
30
+ [tool.setuptools]
31
+ py-modules = []
@@ -0,0 +1,13 @@
1
+ Metadata-Version: 2.4
2
+ Name: sage-docs
3
+ Version: 0.1.0.2
4
+ Summary: SAGE public documentation site
5
+ Requires-Python: >=3.11
6
+ Provides-Extra: docs
7
+ Requires-Dist: zensical>=0.0.24; extra == "docs"
8
+ Provides-Extra: test
9
+ Requires-Dist: pytest>=8.0; extra == "test"
10
+ Provides-Extra: full
11
+ Requires-Dist: sage-docs[docs,test]; extra == "full"
12
+ Provides-Extra: dev
13
+ Requires-Dist: sage-docs[full]; extra == "dev"
@@ -0,0 +1,9 @@
1
+ README.md
2
+ pyproject.toml
3
+ sage_docs.egg-info/PKG-INFO
4
+ sage_docs.egg-info/SOURCES.txt
5
+ sage_docs.egg-info/dependency_links.txt
6
+ sage_docs.egg-info/requires.txt
7
+ sage_docs.egg-info/top_level.txt
8
+ tests/test_legacy_terms.py
9
+ tests/test_links.py
@@ -0,0 +1,12 @@
1
+
2
+ [dev]
3
+ sage-docs[full]
4
+
5
+ [docs]
6
+ zensical>=0.0.24
7
+
8
+ [full]
9
+ sage-docs[docs,test]
10
+
11
+ [test]
12
+ pytest>=8.0
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,40 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from pathlib import Path
5
+
6
+ DOCS_DIR = Path(__file__).resolve().parents[1] / "docs_src"
7
+
8
+ ALLOWLIST = {
9
+ Path("getting-started/flownet-migration-guide.md"),
10
+ }
11
+
12
+ FORBIDDEN_PATTERNS: dict[str, re.Pattern[str]] = {
13
+ "import ray": re.compile(r"\bimport\s+ray\b"),
14
+ "from ray": re.compile(r"\bfrom\s+ray\b"),
15
+ "ray.init()": re.compile(r"\bray\.init\s*\("),
16
+ "ray.remote": re.compile(r"\bray\.remote\b"),
17
+ "RayQueueDescriptor": re.compile(r"\bRayQueueDescriptor\b"),
18
+ "RayServiceTask": re.compile(r"\bRayServiceTask\b"),
19
+ "ray_task.py": re.compile(r"\bray_task\.py\b"),
20
+ "use_ray=True": re.compile(r"\buse_ray\s*=\s*True\b"),
21
+ }
22
+
23
+
24
+ def test_legacy_ray_terms_are_confined_to_migration_guide() -> None:
25
+ violations: list[str] = []
26
+
27
+ for path in DOCS_DIR.rglob("*.md"):
28
+ rel_path = path.relative_to(DOCS_DIR)
29
+ if rel_path in ALLOWLIST:
30
+ continue
31
+
32
+ content = path.read_text(encoding="utf-8")
33
+ for term_name, pattern in FORBIDDEN_PATTERNS.items():
34
+ for match in pattern.finditer(content):
35
+ line_number = content.count("\n", 0, match.start()) + 1
36
+ violations.append(f"{rel_path}:{line_number}: {term_name}")
37
+
38
+ assert not violations, "Legacy Ray terms found outside migration guide:\n" + "\n".join(
39
+ sorted(violations)
40
+ )
@@ -0,0 +1,93 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from pathlib import Path
5
+
6
+ DOCS_DIR = Path(__file__).resolve().parents[1] / "docs_src"
7
+
8
+ INLINE_LINK_RE = re.compile(r"!?\[[^\]]+\]\(([^)]+)\)")
9
+ REF_DEF_RE = re.compile(r"^\s*\[[^\]]+\]:\s+(\S+)")
10
+
11
+ EXTERNAL_PREFIXES = (
12
+ "http://",
13
+ "https://",
14
+ "mailto:",
15
+ "tel:",
16
+ "javascript:",
17
+ )
18
+
19
+
20
+ def _strip_title(target: str) -> str:
21
+ if target.startswith("<") and target.endswith(">"):
22
+ return target[1:-1].strip()
23
+ return target.split(maxsplit=1)[0].strip()
24
+
25
+
26
+ def _normalize_target(target: str) -> str | None:
27
+ target = target.strip()
28
+ if not target:
29
+ return None
30
+ if target.startswith("#"):
31
+ return None
32
+ if target.startswith(EXTERNAL_PREFIXES):
33
+ return None
34
+ target = target.split("#", 1)[0].split("?", 1)[0].strip()
35
+ if not target:
36
+ return None
37
+ return target
38
+
39
+
40
+ def _resolve_path(source: Path, target: str) -> Path:
41
+ if target.startswith("/"):
42
+ return DOCS_DIR / target.lstrip("/")
43
+ return source.parent / target
44
+
45
+
46
+ def _candidate_paths(path: Path) -> list[Path]:
47
+ if path.suffix:
48
+ if path.suffix == ".html":
49
+ return [path, path.with_suffix(".md")]
50
+ return [path]
51
+
52
+ candidates = [path]
53
+ candidates.append(path.with_suffix(".md"))
54
+ candidates.append(path / "index.md")
55
+ return candidates
56
+
57
+
58
+ def _iter_links(markdown: str) -> list[str]:
59
+ links: list[str] = []
60
+ in_fence = False
61
+ fence_re = re.compile(r"^\s*```")
62
+
63
+ for line in markdown.splitlines():
64
+ if fence_re.match(line):
65
+ in_fence = not in_fence
66
+ continue
67
+ if in_fence:
68
+ continue
69
+ for match in INLINE_LINK_RE.findall(line):
70
+ links.append(_strip_title(match))
71
+ ref_match = REF_DEF_RE.match(line)
72
+ if ref_match:
73
+ links.append(_strip_title(ref_match.group(1)))
74
+
75
+ return links
76
+
77
+
78
+ def test_docs_links_exist() -> None:
79
+ missing: list[str] = []
80
+ for path in DOCS_DIR.rglob("*.md"):
81
+ content = path.read_text(encoding="utf-8")
82
+ for raw_target in _iter_links(content):
83
+ target = _normalize_target(raw_target)
84
+ if not target:
85
+ continue
86
+ resolved = _resolve_path(path, target)
87
+ candidates = _candidate_paths(resolved)
88
+ if any(candidate.exists() for candidate in candidates):
89
+ continue
90
+ display = ", ".join(str(candidate) for candidate in candidates)
91
+ missing.append(f"{path}:{display}")
92
+
93
+ assert not missing, "Missing linked files:\n" + "\n".join(sorted(missing))