Skip to content

Commit c32f8e1

Browse files
authored
Merge pull request #24 from PalashChitnavis/sphinx
Create a user documentation using Sphinx
2 parents 4c18d13 + 9d6377b commit c32f8e1

43 files changed

Lines changed: 2864 additions & 117 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/test-pr.yml

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,16 @@ jobs:
1616
# Checkout the repository
1717
- name: Checkout code
1818
uses: actions/checkout@v4
19+
with:
20+
fetch-depth: '0'
21+
fetch-tags: 'true'
1922

20-
# Install Pixi
21-
- name: Install pixi
23+
# Install Pixi and workspace
24+
- name: Install pixi and workspace
2225
uses: prefix-dev/setup-pixi@v0.8.0
2326
with:
2427
cache: false
2528

26-
# Verify Pixi installation
27-
- name: Verify Pixi version
28-
run: pixi --version
29-
30-
# Run pixi install
31-
- name: Install dependencies
32-
run: pixi install --locked
33-
3429
# Run setup
3530
- name: Run setup
3631
run: pixi run setup
@@ -44,6 +39,6 @@ jobs:
4439
- name: Run example and verify output files
4540
run: |
4641
cd examples
47-
python -m brian2wasm brunel_hakim1999.py --no-server
42+
python -m brian2wasm brunel_hakim1999.py --no-server --skip-install
4843
ls brunel_hakim1999/{brian.js,index.html,wasm_module.{js,wasm},worker.js}
4944
shell: pixi run bash -e {0}

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@ examples/output/
66
*.egg-info
77
pixi.lock
88
brian2wasm/_version.py
9-
/dist
9+
/dist
10+
11+
/docs/_build

.readthedocs.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# .readthedocs.yaml
2+
# Read the Docs configuration file
3+
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
4+
5+
# Required
6+
version: 2
7+
8+
# Set the OS, Python version and other tools you might need
9+
build:
10+
os: 'ubuntu-22.04'
11+
tools:
12+
python: "3.12"
13+
14+
# Build documentation in the "docs/" directory with Sphinx
15+
sphinx:
16+
configuration: docs/conf.py
17+
18+
python:
19+
install:
20+
- requirements: rtd-requirements.txt

brian2wasm/__main__.py

Lines changed: 83 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -7,50 +7,45 @@
77

88
def main():
99
"""
10-
Command-line interface for **Brian2Wasm**.
11-
12-
Usage
13-
-----
14-
``python -m brian2wasm <script.py> [--no-server] [--skip-install]``
15-
16-
Parameters
17-
----------
18-
script : str
19-
Path to the user’s Python model. The file **must** end with
20-
``.py`` and must not call ``set_device`` itself – the CLI inserts
21-
the appropriate ``set_device('wasm_standalone', …)`` line
22-
automatically.
23-
--no-server : flag, optional
24-
Generate the WASM/HTML output without starting the local preview
25-
web-server (sets the ``BRIAN2WASM_NO_SERVER`` environment
26-
variable for the subprocess).
27-
--skip-install : flag, optional
28-
Run Brian2WASM without checking or installing EMSDK. Use this if
29-
you are sure EMSDK is already installed and configured in your
30-
environment.
31-
32-
Behaviour
33-
---------
34-
1. Validates that *script* exists and is a ``.py`` file.
35-
2. Looks for an ``<scriptname>.html`` file in the same directory.
36-
* If found, passes the HTML file to ``set_device`` so the custom
37-
template is used.
38-
* Otherwise falls back to the default template.
39-
3. Unless *--skip-install* is given, verifies EMSDK installation
40-
(Pixi/Conda/CONDA_EMSDK_DIR) and attempts to activate it.
41-
4. Prepends the required ``set_device('wasm_standalone', …)`` call to
42-
the script source in-memory.
43-
5. Executes the modified script with its own directory as working
44-
directory, so any relative paths inside the model behave as
45-
expected.
46-
47-
Exit status
48-
-----------
49-
* ``0`` – build finished successfully (and server started unless
50-
*--no-server* was given).
51-
* ``1`` – any error (missing file, not a ``.py`` file, EMSDK not found
52-
or not activated, exception during model execution, etc.).
53-
"""
10+
Command-line interface entry point for Brian2Wasm.
11+
12+
This function validates the given script, injects the required
13+
``set_device('wasm_standalone', …)`` call, ensures EMSDK is
14+
available (unless skipped), and executes the modified script.
15+
16+
Parameters
17+
----------
18+
script : str
19+
Path to the Python model file. The file must exist, end with
20+
``.py``, and must not call ``set_device`` directly, since this
21+
function injects the correct call automatically.
22+
--no-server : bool, optional
23+
If given, generates the WASM/HTML output without starting the
24+
local preview web server. Internally sets the environment
25+
variable ``BRIAN2WASM_NO_SERVER=1``.
26+
--skip-install : bool, optional
27+
If given, skips EMSDK installation and activation checks.
28+
Use this flag when you are certain EMSDK is already installed
29+
and properly configured in your environment.
30+
31+
Raises
32+
------
33+
FileNotFoundError
34+
If the provided script path does not exist.
35+
ValueError
36+
If the provided file is not a Python ``.py`` script.
37+
RuntimeError
38+
If execution of the modified script fails for any reason.
39+
SystemExit
40+
If errors occur during validation or script execution, the
41+
process exits with status code ``1``.
42+
43+
Returns
44+
-------
45+
None
46+
This function is intended as a CLI entry point and does not
47+
return a value.
48+
"""
5449

5550
parser = argparse.ArgumentParser(
5651
description="Brian2WASM CLI"
@@ -76,10 +71,10 @@ def main():
7671
# Check if the script exists and is a Python file
7772
if not os.path.isfile(script_path):
7873
full_path = os.path.abspath(script_path)
79-
print(f"Error: File '{full_path}' does not exist.", file=sys.stderr)
74+
print(f"Error: File '{full_path}' does not exist.", file=sys.stderr)
8075
sys.exit(1)
8176
if not script_path.endswith(".py"):
82-
print(f"Error: File '{script_path}' is not a Python script (.py).", file=sys.stderr)
77+
print(f"Error: File '{script_path}' is not a Python script (.py).", file=sys.stderr)
8378
sys.exit(1)
8479

8580
if not args.skip_install:
@@ -101,14 +96,14 @@ def main():
10196

10297
# Inject required lines at the top
10398
if has_html_file:
104-
print(f"HTML file found: '{html_file_path}'")
99+
print(f"HTML file found: '{html_file_path}'")
105100
injection = (
106101
"from brian2 import set_device\n"
107102
"import brian2wasm\n"
108103
f"set_device('wasm_standalone', directory='{script_name}', html_file='{html_file}')\n"
109104
)
110105
else:
111-
print("ℹ️ HTML file not found: using default HTML template.")
106+
print("HTML file not found: using default HTML template.")
112107
injection = (
113108
"from brian2 import set_device\n"
114109
"import brian2wasm\n"
@@ -125,26 +120,55 @@ def main():
125120
if args.no_server:
126121
os.environ['BRIAN2WASM_NO_SERVER'] = '1'
127122

128-
print(f"📄 Script path: {os.path.abspath(script_path)}")
129-
print(f"📁 Directory: {script_dir}")
123+
print(f"Script path: {os.path.abspath(script_path)}")
124+
print(f"Directory: {script_dir}")
130125
exec_globals = {'__name__': '__main__', '__file__': os.path.abspath(script_path)}
131126
compiled_script = compile(modified_script, script_path, 'exec')
132127
exec(compiled_script, exec_globals)
133128

134129
except Exception as e:
135-
print(f"Error running script: {e}", file=sys.stderr)
130+
print(f"Error running script: {e}", file=sys.stderr)
136131
sys.exit(1)
137132

138133
finally:
139134
os.chdir(original_cwd)
140135

141136

142137
def check_emsdk():
138+
"""
139+
Verify that the Emscripten SDK (EMSDK) is installed and attempt to activate it.
140+
141+
This function checks for EMSDK in the current environment, using either
142+
the system path (``emsdk`` executable) or the ``CONDA_EMSDK_DIR`` variable.
143+
If EMSDK is missing, it prints installation instructions and exits.
144+
If EMSDK is found but not activated, it attempts to activate the latest
145+
version, optionally prompting the user to install and activate it.
146+
147+
Parameters
148+
----------
149+
None
150+
This function takes no arguments.
151+
152+
Raises
153+
------
154+
SystemExit
155+
If EMSDK is not found, not activated, or installation/activation
156+
fails, the process exits with status code ``1``.
157+
RuntimeError
158+
If subprocess execution encounters an unexpected failure during
159+
EMSDK activation.
160+
161+
Returns
162+
-------
163+
None
164+
This function is intended as a setup check and does not
165+
return a value. Its success or failure is indicated by process exit.
166+
"""
143167
emsdk = shutil.which("emsdk")
144168
conda_emsdk_dir = os.environ.get("CONDA_EMSDK_DIR")
145169

146170
if not emsdk and not conda_emsdk_dir:
147-
print("EMSDK and CONDA_EMSDK_DIR not found. That means EMSDK is not installed.")
171+
print("EMSDK and CONDA_EMSDK_DIR not found. That means EMSDK is not installed.")
148172
print(" ➤ If you are using **Pixi**, run:")
149173
print(" pixi add emsdk && pixi install")
150174
print(" ➤ If you are using **Conda**, run:")
@@ -153,20 +177,20 @@ def check_emsdk():
153177
print(" https://emscripten.org/index.html#")
154178
sys.exit(1)
155179

156-
print(f"EMSDK is installed and CONDA_EMSDK_DIR is found")
180+
print(f"EMSDK is installed and CONDA_EMSDK_DIR is found")
157181

158182
try:
159-
print("🔧 Attempting to activate EMSDK with: emsdk activate latest")
183+
print("Attempting to activate EMSDK with: emsdk activate latest")
160184
result = subprocess.run(["./emsdk", "activate", "latest"], cwd=conda_emsdk_dir, check=False, capture_output=True, text=True)
161185
if result.returncode != 0:
162-
print("Failed to activate EMSDK:")
186+
print("Failed to activate EMSDK:")
163187
choice = input("Do you want to install and activate EMSDK now? (y/n) ")
164188
if choice == 'y':
165189
try:
166190
subprocess.run(["./emsdk", "install", "latest"], cwd=conda_emsdk_dir, check=True)
167-
print("EMSDK install & activation succeeded. You can run the script now.")
191+
print("EMSDK install & activation succeeded. You can run the script now.")
168192
except subprocess.CalledProcessError as e:
169-
print("Failed to activate EMSDK:")
193+
print("Failed to activate EMSDK:")
170194
print(" ➤ Please run the following manually in your terminal and try again:")
171195
print(" cd $CONDA_EMSDK_DIR && ./emsdk install latest && ./emsdk activate latest")
172196
else:
@@ -175,9 +199,9 @@ def check_emsdk():
175199

176200
sys.exit(1)
177201
else:
178-
print("EMSDK activation succeeded.")
202+
print("EMSDK activation succeeded.")
179203
except Exception as e:
180-
print(f"Error while running EMSDK activation: {e}")
204+
print(f"Error while running EMSDK activation: {e}")
181205
sys.exit(1)
182206

183207

0 commit comments

Comments
 (0)