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.
- sage_docs-0.1.0.2/PKG-INFO +13 -0
- sage_docs-0.1.0.2/README.md +41 -0
- sage_docs-0.1.0.2/pyproject.toml +31 -0
- sage_docs-0.1.0.2/sage_docs.egg-info/PKG-INFO +13 -0
- sage_docs-0.1.0.2/sage_docs.egg-info/SOURCES.txt +9 -0
- sage_docs-0.1.0.2/sage_docs.egg-info/dependency_links.txt +1 -0
- sage_docs-0.1.0.2/sage_docs.egg-info/requires.txt +12 -0
- sage_docs-0.1.0.2/sage_docs.egg-info/top_level.txt +1 -0
- sage_docs-0.1.0.2/setup.cfg +4 -0
- sage_docs-0.1.0.2/tests/test_legacy_terms.py +40 -0
- sage_docs-0.1.0.2/tests/test_links.py +93 -0
|
@@ -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 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -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))
|