Skip to content

Commit c8863b5

Browse files
committed
updated docs build process for "hub"-based plan using furo theme
1 parent 2ed43c1 commit c8863b5

6 files changed

Lines changed: 230 additions & 35 deletions

File tree

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,4 @@ tags
8383
.tags
8484

8585
# sphinx
86-
documentation/_build/
86+
docs-test/

Makefile

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,14 @@ $(generatedcode): VERSION
3737
ls $(generatedcode)*
3838

3939
# generating jsonschema depends on mmif-python and pydantic
40-
docs: mmif := $(shell grep mmif-python requirements.txt)
41-
docs: pydantic := $(shell grep pydantic requirements.txt)
42-
docs: VERSION $(generatedcode)
43-
pip install --upgrade --no-input "$(mmif)" "$(pydantic)"
44-
rm -rf docs
45-
mkdir -p docs
46-
python3 -m clams.appmetadata.__init__ > documentation/appmetadata.jsonschema
47-
sphinx-build -a -b html documentation/ docs
48-
mv documentation/appmetadata.jsonschema docs/
49-
touch docs/.nojekyll
50-
echo 'sdk.clams.ai' > docs/CNAME
40+
docs:
41+
@echo "WARNING: The 'docs' target is deprecated and will be removed."
42+
@echo "The 'docs' directory is no longer used. Documentation is now hosted in the central CLAMS documentation hub."
43+
@echo "Use 'make doc' for local builds."
44+
@echo "Nothing is done."
45+
46+
doc: VERSION
47+
python3 build-tools/docs.py
5148

5249
package: VERSION
5350
pip install --upgrade -r requirements.dev

build-tools/docs.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import argparse
2+
import subprocess
3+
import sys
4+
import os
5+
import shutil
6+
from pathlib import Path
7+
8+
def run_command(command, cwd=None, check=True, env=None):
9+
"""Helper to run a shell command."""
10+
print(f"Running: {' '.join(str(c) for c in command)}")
11+
result = subprocess.run(command, cwd=cwd, env=env)
12+
if check and result.returncode != 0:
13+
print(f"Error: Command failed with exit code {result.returncode}")
14+
sys.exit(result.returncode)
15+
return result
16+
17+
def build_docs_local(source_dir: Path):
18+
"""
19+
Builds documentation for the provided source directory.
20+
Assumes it's running in an environment with necessary tools.
21+
"""
22+
print("--- Running in Local Build Mode ---")
23+
24+
# 1. Generate source code and install in editable mode.
25+
print("\n--- Step 1: Installing in editable mode ---")
26+
try:
27+
run_command([sys.executable, "-m", "pip", "install", "-e", "."], cwd=source_dir)
28+
# Explicitly run schema generation to be sure
29+
run_command([sys.executable, "setup.py", "generate_schema"], cwd=source_dir)
30+
except SystemExit:
31+
print("Warning: 'pip install -e .' failed. This might be due to an externally managed environment.")
32+
print("Attempting to proceed with documentation build assuming dependencies are met...")
33+
34+
# 2. Install documentation-specific dependencies.
35+
print("\n--- Step 2: Installing documentation dependencies ---")
36+
doc_reqs = source_dir / "build-tools" / "requirements.docs.txt"
37+
if not doc_reqs.exists():
38+
print(f"Error: Documentation requirements not found at {doc_reqs}")
39+
sys.exit(1)
40+
try:
41+
run_command([sys.executable, "-m", "pip", "install", "-r", str(doc_reqs)])
42+
except SystemExit:
43+
print("Warning: Failed to install documentation dependencies.")
44+
# Check if sphinx-build is available
45+
if shutil.which("sphinx-build") is None:
46+
print("Error: 'sphinx-build' not found and installation failed.")
47+
print("Please install dependencies manually or run this script inside a virtual environment.")
48+
sys.exit(1)
49+
print("Assuming dependencies are already installed...")
50+
51+
# 3. Build the documentation using Sphinx.
52+
print("\n--- Step 3: Building Sphinx documentation ---")
53+
docs_source_dir = source_dir / "documentation"
54+
docs_build_dir = source_dir / "docs-test"
55+
56+
# Schema generation is now handled in conf.py
57+
# schema_src = source_dir / "clams" / "appmetadata.jsonschema"
58+
# schema_dst = docs_source_dir / "appmetadata.jsonschema"
59+
# if schema_src.exists():
60+
# shutil.copy(schema_src, schema_dst)
61+
62+
sphinx_command = [
63+
sys.executable, "-m", "sphinx.cmd.build",
64+
str(docs_source_dir),
65+
str(docs_build_dir),
66+
"-b", "html", # build html
67+
"-a", # write all files (rebuild everything)
68+
"-E", # don't use a saved environment, reread all files
69+
]
70+
run_command(sphinx_command)
71+
72+
print(f"\nDocumentation build complete. Output in: {docs_build_dir}")
73+
return docs_build_dir
74+
75+
def main():
76+
parser = argparse.ArgumentParser(
77+
description="Build documentation for the clams-python project."
78+
)
79+
args = parser.parse_args()
80+
81+
build_docs_local(Path.cwd())
82+
83+
if __name__ == "__main__":
84+
main()

build-tools/requirements.docs.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
sphinx>=7.0,<8.0
2+
furo
3+
m2r2
4+
sphinx-jsonschema

documentation/conf.py

Lines changed: 127 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
from pathlib import Path
1515
import shutil
1616
import sys
17+
import inspect
18+
import os
1719

1820
import mmif
1921

@@ -24,9 +26,14 @@
2426
# -- Project information -----------------------------------------------------
2527

2628
project = proj_root_dir.name
29+
blob_base_url = f'https://github.com/clamsproject/{project}/blob'
2730
copyright = f'{datetime.date.today().year}, Brandeis LLC'
2831
author = 'Brandeis LLC'
29-
version = open(proj_root_dir / 'VERSION').read().strip()
32+
try:
33+
version = open(proj_root_dir / 'VERSION').read().strip()
34+
except FileNotFoundError:
35+
print("WARNING: VERSION file not found, using 'dev' as version.")
36+
version = 'dev'
3037
root_doc = 'index'
3138

3239

@@ -39,7 +46,6 @@
3946
'sphinx.ext.autodoc',
4047
'sphinx.ext.linkcode',
4148
'sphinx.ext.intersphinx',
42-
'sphinx_rtd_theme',
4349
'sphinx-jsonschema',
4450
'm2r2'
4551
]
@@ -53,44 +59,138 @@
5359
}
5460

5561

56-
# Add any paths that contain templates here, relative to this directory.
5762
templates_path = ['_templates']
58-
59-
# List of patterns, relative to source directory, that match files and
60-
# directories to ignore when looking for source files.
61-
# This pattern also affects html_static_path and html_extra_path.
6263
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
64+
# dynamically generated files
65+
exclude_patterns.extend(['cli_help.rst', 'whatsnew.md'])
6366

6467

6568
# -- Options for HTML output -------------------------------------------------
6669

6770
# The theme to use for HTML and HTML Help pages. See the documentation for
6871
# a list of builtin themes.
6972
#
70-
html_theme = 'sphinx_rtd_theme'
71-
72-
# Add any paths that contain custom static files (such as style sheets) here,
73-
# relative to this directory. They are copied after the builtin static files,
74-
# so a file named "default.css" will overwrite the builtin "default.css".
75-
# html_static_path = ['_static']
76-
77-
# hide document source view link at the top
78-
html_show_sourcelink = False
73+
html_theme = 'furo'
74+
html_extra_path = ['appmetadata.jsonschema']
75+
html_static_path = [] # No static path for now, can be created if needed
76+
html_show_sourcelink = True # Furo handles this well, no need to hide
77+
78+
# Theme options for visual consistency with CLAMS branding
79+
html_theme_options = {
80+
# "light_logo": "logo.png", # TODO: Add logo files if available
81+
# "dark_logo": "logo.png",
82+
"sidebar_hide_name": False,
83+
"navigation_with_keys": True,
84+
"source_repository": "https://github.com/clamsproject/clams-python",
85+
"source_branch": "main", # Default branch for "Edit on GitHub" links
86+
"source_directory": "documentation/",
87+
88+
# CLAMS brand colors
89+
"light_css_variables": {
90+
"color-brand-primary": "#008AFF",
91+
"color-brand-content": "#0085A1",
92+
"color-link": "#008AFF",
93+
"color-link-hover": "#0085A1",
94+
},
95+
# Dark mode variables can be added here if needed
96+
}
7997

8098

8199
# function used by `linkcode` extension
82100
def linkcode_resolve(domain, info):
83-
if domain != 'py':
101+
if domain != 'py' or not info.get('module'):
84102
return None
85-
if not info['module']:
103+
104+
try:
105+
# Find the Python object
106+
obj = sys.modules.get(info['module'])
107+
if obj is None: return None
108+
for part in info['fullname'].split('.'):
109+
obj = getattr(obj, part)
110+
111+
# Get the source file and line numbers
112+
# Use inspect.unwrap to handle decorated objects
113+
unwrapped_obj = inspect.unwrap(obj)
114+
filename = inspect.getsourcefile(unwrapped_obj)
115+
if not filename: return None
116+
117+
lines, start_lineno = inspect.getsourcelines(unwrapped_obj)
118+
end_lineno = start_lineno + len(lines) - 1
119+
120+
# clams-python docs are single-version, always pointing to main
121+
git_ref = 'main'
122+
123+
# Get file path relative to repository root
124+
repo_root = Path(__file__).parent.parent
125+
rel_path = Path(filename).relative_to(repo_root)
126+
127+
return f"{blob_base_url}/{git_ref}/{rel_path}#L{start_lineno}-L{end_lineno}"
128+
129+
except Exception:
130+
# Don't fail the entire build if one link fails, just return None
86131
return None
87-
filename = info['module'].replace('.', '/')
88-
return f"https://github.com/clamsproject/clams-python/tree/main/{filename}/__init__.py"
89132

90133

91-
def update_target_spec():
134+
def generate_whatsnew_rst(app):
135+
changelog_path = proj_root_dir / 'CHANGELOG.md'
136+
output_path = proj_root_dir / 'documentation' / 'whatsnew.md'
137+
if not changelog_path.exists():
138+
print(f"WARNING: CHANGELOG.md not found at {changelog_path}")
139+
with open(output_path, 'w') as f:
140+
f.write("")
141+
return
142+
143+
import re
144+
145+
content = []
146+
found_version = False
147+
version_header_re = re.compile(r'^## releasing\s+([^\s]+)\s*(\(.*\))?')
148+
149+
print(f"DEBUG: Looking for version '{version}' in CHANGELOG.md")
150+
151+
with open(changelog_path, 'r') as f:
152+
lines = f.readlines()
153+
154+
for line in lines:
155+
match = version_header_re.match(line)
156+
if match:
157+
header_version = match.group(1)
158+
if header_version == version:
159+
found_version = True
160+
# We don't include the header line itself in the content we want to wrap
161+
continue
162+
elif found_version:
163+
break
164+
165+
if found_version:
166+
content.append(line)
167+
168+
if not found_version:
169+
print(f"NOTE: No changelog entry found for version {version}")
170+
with open(output_path, 'w') as f:
171+
f.write("")
172+
else:
173+
# Dump matched markdown content directly to whatsnew.md
174+
with open(output_path, 'w') as f:
175+
f.write(f"## What's New in {version}\n\n(Full changelog available in the [CHANGELOG.md]({blob_base_url}/main/CHANGELOG.md))\n")
176+
f.writelines(content)
177+
178+
179+
def generate_jsonschema(app):
180+
import json
181+
from clams.appmetadata import AppMetadata
182+
183+
# Generate schema using Pydantic v2 API
184+
schema_dict = AppMetadata.model_json_schema()
185+
186+
output_path = Path(app.srcdir) / 'appmetadata.jsonschema'
187+
with open(output_path, 'w') as f:
188+
json.dump(schema_dict, f, indent=2)
189+
190+
191+
def update_target_spec(app):
92192
target_vers_csv = Path(__file__).parent / 'target-versions.csv'
93-
with open("../VERSION", 'r') as version_f:
193+
with open(proj_root_dir / "VERSION", 'r') as version_f:
94194
version = version_f.read().strip()
95195
mmifver = mmif.__version__
96196
specver = mmif.__specver__
@@ -102,4 +202,8 @@ def update_target_spec():
102202
out_f.write(line)
103203
shutil.move(out_f.name, in_f.name)
104204

105-
update_target_spec()
205+
206+
def setup(app):
207+
app.connect('builder-inited', generate_whatsnew_rst)
208+
app.connect('builder-inited', generate_jsonschema)
209+
app.connect('builder-inited', update_target_spec)

documentation/index.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ Welcome to CLAMS Python SDK documentation!
33

44
.. mdinclude:: ../README.md
55

6+
----
7+
8+
.. mdinclude:: whatsnew.md
9+
10+
----
11+
612
.. toctree::
713
:maxdepth: 2
814
:caption: Contents

0 commit comments

Comments
 (0)