hoodscript 1.1.0__tar.gz → 1.2.0__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.
Files changed (94) hide show
  1. {hoodscript-1.1.0 → hoodscript-1.2.0}/CHANGELOG.md +27 -0
  2. {hoodscript-1.1.0 → hoodscript-1.2.0}/PKG-INFO +10 -1
  3. {hoodscript-1.1.0 → hoodscript-1.2.0}/README.md +2 -0
  4. {hoodscript-1.1.0 → hoodscript-1.2.0}/SECURITY.md +4 -2
  5. {hoodscript-1.1.0 → hoodscript-1.2.0}/docs/onboarding.md +30 -2
  6. hoodscript-1.2.0/examples/quickstart.ipynb +142 -0
  7. {hoodscript-1.1.0 → hoodscript-1.2.0}/pyproject.toml +5 -1
  8. hoodscript-1.2.0/src/hoodscript/__init__.py +16 -0
  9. {hoodscript-1.1.0 → hoodscript-1.2.0}/src/hoodscript/_version.py +2 -2
  10. {hoodscript-1.1.0 → hoodscript-1.2.0}/src/hoodscript/cli.py +31 -1
  11. {hoodscript-1.1.0 → hoodscript-1.2.0}/src/hoodscript/errors/translator.py +4 -1
  12. hoodscript-1.2.0/src/hoodscript/kernel/__init__.py +12 -0
  13. hoodscript-1.2.0/src/hoodscript/kernel/__main__.py +13 -0
  14. hoodscript-1.2.0/src/hoodscript/kernel/installer.py +54 -0
  15. hoodscript-1.2.0/src/hoodscript/kernel/kernel.py +292 -0
  16. hoodscript-1.2.0/src/hoodscript/kernel/magics.py +104 -0
  17. {hoodscript-1.1.0 → hoodscript-1.2.0}/src/hoodscript/traceback_handler.py +1 -1
  18. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/security/test_exec_sites.py +1 -1
  19. hoodscript-1.2.0/tests/test_kernel.py +159 -0
  20. hoodscript-1.2.0/tests/test_kernel_zmq.py +88 -0
  21. hoodscript-1.1.0/src/hoodscript/__init__.py +0 -9
  22. {hoodscript-1.1.0 → hoodscript-1.2.0}/.gitignore +0 -0
  23. {hoodscript-1.1.0 → hoodscript-1.2.0}/CODE_OF_CONDUCT.md +0 -0
  24. {hoodscript-1.1.0 → hoodscript-1.2.0}/CONTRIBUTING.md +0 -0
  25. {hoodscript-1.1.0 → hoodscript-1.2.0}/LICENSE +0 -0
  26. {hoodscript-1.1.0 → hoodscript-1.2.0}/SPRINT_PLAN.md +0 -0
  27. {hoodscript-1.1.0 → hoodscript-1.2.0}/docs/adr/0001-token-pipeline-over-parser.md +0 -0
  28. {hoodscript-1.1.0 → hoodscript-1.2.0}/docs/adr/0002-keyword-reconciliation.md +0 -0
  29. {hoodscript-1.1.0 → hoodscript-1.2.0}/docs/adr/0003-inclusive-ranges.md +0 -0
  30. {hoodscript-1.1.0 → hoodscript-1.2.0}/docs/adr/README.md +0 -0
  31. {hoodscript-1.1.0 → hoodscript-1.2.0}/docs/archive/HOODSCRIPT_COMPLETE_MASTER_SYSTEM.md +0 -0
  32. {hoodscript-1.1.0 → hoodscript-1.2.0}/docs/archive/HOODSCRIPT_FULL_ENGINEERING_SUITE.md +0 -0
  33. {hoodscript-1.1.0 → hoodscript-1.2.0}/docs/archive/HOODSCRIPT_UNSPRED_FEATURES_ROADMAP.md +0 -0
  34. {hoodscript-1.1.0 → hoodscript-1.2.0}/docs/curriculum.md +0 -0
  35. {hoodscript-1.1.0 → hoodscript-1.2.0}/docs/keywords.md +0 -0
  36. {hoodscript-1.1.0 → hoodscript-1.2.0}/out.txt +0 -0
  37. {hoodscript-1.1.0 → hoodscript-1.2.0}/playground/app.js +0 -0
  38. {hoodscript-1.1.0 → hoodscript-1.2.0}/playground/bundle.js +0 -0
  39. {hoodscript-1.1.0 → hoodscript-1.2.0}/playground/examples.js +0 -0
  40. {hoodscript-1.1.0 → hoodscript-1.2.0}/playground/hoodscript-latest-py3-none-any.whl +0 -0
  41. {hoodscript-1.1.0 → hoodscript-1.2.0}/playground/index.html +0 -0
  42. {hoodscript-1.1.0 → hoodscript-1.2.0}/playground/style.css +0 -0
  43. {hoodscript-1.1.0 → hoodscript-1.2.0}/playground/worker.js +0 -0
  44. {hoodscript-1.1.0 → hoodscript-1.2.0}/scripts/build_playground.py +0 -0
  45. {hoodscript-1.1.0 → hoodscript-1.2.0}/scripts/gen_keywords.py +0 -0
  46. {hoodscript-1.1.0 → hoodscript-1.2.0}/src/hoodscript/__main__.py +0 -0
  47. {hoodscript-1.1.0 → hoodscript-1.2.0}/src/hoodscript/cache.py +0 -0
  48. {hoodscript-1.1.0 → hoodscript-1.2.0}/src/hoodscript/constants.py +0 -0
  49. {hoodscript-1.1.0 → hoodscript-1.2.0}/src/hoodscript/debugger.py +0 -0
  50. {hoodscript-1.1.0 → hoodscript-1.2.0}/src/hoodscript/errors/__init__.py +0 -0
  51. {hoodscript-1.1.0 → hoodscript-1.2.0}/src/hoodscript/errors/catalog.py +0 -0
  52. {hoodscript-1.1.0 → hoodscript-1.2.0}/src/hoodscript/errors/renderer.py +0 -0
  53. {hoodscript-1.1.0 → hoodscript-1.2.0}/src/hoodscript/formatter.py +0 -0
  54. {hoodscript-1.1.0 → hoodscript-1.2.0}/src/hoodscript/importer.py +0 -0
  55. {hoodscript-1.1.0 → hoodscript-1.2.0}/src/hoodscript/linter.py +0 -0
  56. {hoodscript-1.1.0 → hoodscript-1.2.0}/src/hoodscript/lsp.py +0 -0
  57. {hoodscript-1.1.0 → hoodscript-1.2.0}/src/hoodscript/migrator.py +0 -0
  58. {hoodscript-1.1.0 → hoodscript-1.2.0}/src/hoodscript/patterns.py +0 -0
  59. {hoodscript-1.1.0 → hoodscript-1.2.0}/src/hoodscript/repl.py +0 -0
  60. {hoodscript-1.1.0 → hoodscript-1.2.0}/src/hoodscript/sandbox.py +0 -0
  61. {hoodscript-1.1.0 → hoodscript-1.2.0}/src/hoodscript/stubs.py +0 -0
  62. {hoodscript-1.1.0 → hoodscript-1.2.0}/src/hoodscript/transpiler.py +0 -0
  63. {hoodscript-1.1.0 → hoodscript-1.2.0}/src/hoodscript/tutor.py +0 -0
  64. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/security/conftest.py +0 -0
  65. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/security/corpus/deep_nesting.hs +0 -0
  66. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/security/corpus/fork_bomb.hs +0 -0
  67. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/security/corpus/huge_literal.hs +0 -0
  68. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/security/corpus/import_escapes.hs +0 -0
  69. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/security/corpus/infinite_recursion.hs +0 -0
  70. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/security/corpus/open_file.hs +0 -0
  71. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/security/corpus/pathological_indent.hs +0 -0
  72. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/security/corpus/runaway_output.hs +0 -0
  73. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/security/corpus/runaway_time.hs +0 -0
  74. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/security/test_corpus.py +0 -0
  75. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/security/test_fuzz.py +0 -0
  76. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/security/test_sandbox.py +0 -0
  77. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/test_cli.py +0 -0
  78. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/test_conformance.py +0 -0
  79. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/test_debugger.py +0 -0
  80. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/test_docs.py +0 -0
  81. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/test_errors.py +0 -0
  82. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/test_fmt.py +0 -0
  83. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/test_grammar_canonical.py +0 -0
  84. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/test_importer.py +0 -0
  85. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/test_keywords_doc.py +0 -0
  86. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/test_learn.py +0 -0
  87. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/test_lint.py +0 -0
  88. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/test_lsp_features.py +0 -0
  89. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/test_patterns.py +0 -0
  90. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/test_playground.py +0 -0
  91. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/test_release.py +0 -0
  92. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/test_repl.py +0 -0
  93. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/test_transpiler.py +0 -0
  94. {hoodscript-1.1.0 → hoodscript-1.2.0}/tests/test_vscode_extension.py +0 -0
@@ -3,6 +3,32 @@
3
3
  All notable changes to HoodScript. Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
4
4
  Versioning: the keyword set is the public API; changing it is at least a minor bump.
5
5
 
6
+ ## [1.2.0] — 2026-09-11
7
+
8
+ ### Added
9
+
10
+ - **Interactive Jupyter Notebook Kernel & IPython Magics** (`src/hoodscript/kernel/`, `examples/quickstart.ipynb`):
11
+ - Dedicated standalone Jupyter kernel (`hoodscript-kernel` / `HoodScriptKernel`) subclassing `ipykernel.kernelbase.Kernel`.
12
+ - Automatic 1:1 transpilation of notebook cells to Python prior to execution in the kernel's persistent namespace.
13
+ - Rich HTML plain-English JPE error cards rendered in-cell with collapsible raw Python tracebacks.
14
+ - Dynamic tab autocompletion over HoodScript keywords, patterns, exception names, and namespace variables.
15
+ - Cell and line inspection magics: `%hood_transpile` (preview generated Python without executing) and `%hood_tb` (toggle raw traceback view).
16
+ - IPython extension (`%load_ext hoodscript`) providing `%%hood` and `%%hood_transpile` for hybrid Python/HoodScript notebooks and Google Colab.
17
+ - CLI installer subcommand: `hoodscript kernel install [--user] [--prefix PREFIX]`.
18
+ - Interactive starter notebook: `examples/quickstart.ipynb`.
19
+ - Optional dependency extra: `pip install hoodscript[jupyter]`.
20
+ - QA (audited end to end over ZMQ with `jupyter_client` before acceptance; four
21
+ defects fixed): `holla` output never reached the cell (the base `Kernel`
22
+ never tells ipykernel's stdout stream which cell is running — `set_parent`
23
+ now does); a trailing bare expression displayed nothing (now shown as the
24
+ result, like IPython); crash cards showed the *Python* line (`def f()`) and,
25
+ for syntax errors, a stdlib `ast.py` frame (cells are registered in
26
+ `linecache` as `<notebook>`, a program pseudo-file; syntax errors carry no
27
+ frames); `%load_ext hoodscript` as documented did not work (only
28
+ `hoodscript.kernel` did) and `examples/quickstart.ipynb` had no kernelspec
29
+ metadata. `tests/test_kernel_zmq.py` drives the real kernel process.
30
+
31
+
6
32
  ## [1.1.0] — 2026-09-11
7
33
 
8
34
  The reconciled language. Every keyword is now either cited in
@@ -179,5 +205,6 @@ PEP 552 bytecode cache, CLI, REPL, crash reports with `*Trip` names,
179
205
  `py2hood` / `hood2py`, stub generator, pygls language server (diagnostics,
180
206
  hover, completion). Keyword set later found to be uncited — see v1.1.0.
181
207
 
208
+ [1.2.0]: https://github.com/khaoticdev62/hoodscript/compare/v1.1.0...v1.2.0
182
209
  [1.1.0]: https://github.com/khaoticdev62/hoodscript/compare/bf801ab...v1.1.0
183
210
  [1.0.0]: https://github.com/khaoticdev62/hoodscript/commits/bf801ab
@@ -1,11 +1,13 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: hoodscript
3
- Version: 1.1.0
3
+ Version: 1.2.0
4
4
  Summary: A production-grade Python 3 conlang based on AAVE with 1:1 AST parity.
5
5
  License: MIT
6
6
  License-File: LICENSE
7
7
  Requires-Python: >=3.10
8
8
  Provides-Extra: all
9
+ Requires-Dist: ipykernel>=6.0.0; extra == 'all'
10
+ Requires-Dist: ipython>=8.0.0; extra == 'all'
9
11
  Requires-Dist: jedi>=0.19.1; extra == 'all'
10
12
  Requires-Dist: mypy>=1.9.0; extra == 'all'
11
13
  Requires-Dist: prompt-toolkit>=3.0.40; extra == 'all'
@@ -16,6 +18,8 @@ Provides-Extra: dev
16
18
  Requires-Dist: build>=1.1.0; extra == 'dev'
17
19
  Requires-Dist: debugpy>=1.8.0; extra == 'dev'
18
20
  Requires-Dist: hatch-vcs>=0.4.0; extra == 'dev'
21
+ Requires-Dist: ipykernel>=6.0.0; extra == 'dev'
22
+ Requires-Dist: ipython>=8.0.0; extra == 'dev'
19
23
  Requires-Dist: jedi>=0.19.1; extra == 'dev'
20
24
  Requires-Dist: mypy>=1.9.0; extra == 'dev'
21
25
  Requires-Dist: prompt-toolkit>=3.0.40; extra == 'dev'
@@ -24,6 +28,9 @@ Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
24
28
  Requires-Dist: pytest>=8.0.0; extra == 'dev'
25
29
  Requires-Dist: rich>=13.7; extra == 'dev'
26
30
  Requires-Dist: ruff>=0.3.0; extra == 'dev'
31
+ Provides-Extra: jupyter
32
+ Requires-Dist: ipykernel>=6.0.0; extra == 'jupyter'
33
+ Requires-Dist: ipython>=8.0.0; extra == 'jupyter'
27
34
  Provides-Extra: lsp
28
35
  Requires-Dist: jedi>=0.19.1; extra == 'lsp'
29
36
  Requires-Dist: pygls<2,>=1.3.1; extra == 'lsp'
@@ -96,6 +103,8 @@ with `PYTHONPATH=src` and use `python3 -m hoodscript` instead of `hoodscript`.
96
103
  hoodscript hello.hs # transpile + execute
97
104
  hoodscript -c hello.hs # print the Python it becomes
98
105
  hoodscript repl --mirror # interactive REPL (hood> prompt; chill to exit; --mirror shows the Python)
106
+ hoodscript playground # launch in-browser Pyodide WebAssembly playground
107
+ hoodscript kernel install # Jupyter kernel (pip install "hoodscript[jupyter]"); or %load_ext hoodscript
99
108
  hoodscript fmt|lint <file.hs> # formatter / linter (pip install "hoodscript[tools]")
100
109
  hoodscript learn [topic] # interactive tutor driving docs/curriculum.md
101
110
  hoodscript # a short tour of all commands
@@ -58,6 +58,8 @@ with `PYTHONPATH=src` and use `python3 -m hoodscript` instead of `hoodscript`.
58
58
  hoodscript hello.hs # transpile + execute
59
59
  hoodscript -c hello.hs # print the Python it becomes
60
60
  hoodscript repl --mirror # interactive REPL (hood> prompt; chill to exit; --mirror shows the Python)
61
+ hoodscript playground # launch in-browser Pyodide WebAssembly playground
62
+ hoodscript kernel install # Jupyter kernel (pip install "hoodscript[jupyter]"); or %load_ext hoodscript
61
63
  hoodscript fmt|lint <file.hs> # formatter / linter (pip install "hoodscript[tools]")
62
64
  hoodscript learn [topic] # interactive tutor driving docs/curriculum.md
63
65
  hoodscript # a short tour of all commands
@@ -50,14 +50,16 @@ To protect host environments against runaway workloads, the sandbox enforces thr
50
50
 
51
51
  ## 4. Code Execution Audit
52
52
 
53
- `exec`/`eval` appear in exactly four places, all on code objects produced by
53
+ `exec`/`eval` appear in exactly six places, all on code objects produced by
54
54
  `compile()` from the transpiler's output of the user's own source — never on
55
55
  text from anywhere else:
56
56
  1. `src/hoodscript/cli.py` — running a file
57
57
  2. `src/hoodscript/repl.py` — the interactive session (`eval` first, `exec` fallback)
58
58
  3. `src/hoodscript/debugger.py` — running a file under debugpy
59
59
  4. `src/hoodscript/tutor.py` — running a curriculum lesson
60
- `tests/security/test_exec_sites.py` fails if a fifth appears.
60
+ 5. `src/hoodscript/kernel/kernel.py` — executing notebook cells in the HoodScript kernel
61
+ 6. `src/hoodscript/kernel/magics.py` — executing `%%hood` cells in IPython / Google Colab
62
+ `tests/security/test_exec_sites.py` fails if a seventh appears.
61
63
 
62
64
  ### Why this is safe and equivalent to `compile()`:
63
65
  - `transpile()` performs strictly lexical substitution over Python's `tokenize.tokenize` stream and deterministic character-preserving pattern transforms (Rules R1–R12).
@@ -219,14 +219,42 @@ Speaks the Language Server Protocol over stdio. Provides:
219
219
  Point any LSP-capable editor at that command for the `hoodscript` language ID.
220
220
  A VS Code extension manifest lives in `editors/vscode/`.
221
221
 
222
- ## 11. Run the tests
222
+ ## 11. Jupyter Notebooks & Google Colab
223
+
224
+ Use HoodScript interactively inside JupyterLab, classic Notebooks, or Google Colab:
225
+
226
+ ### Option A: Dedicated Jupyter Kernel
227
+ ```bash
228
+ pip install hoodscript[jupyter]
229
+ hoodscript kernel install
230
+ jupyter lab
231
+ ```
232
+ Select the **HoodScript** kernel from the launcher. Every cell executes native HoodScript with tab completion and rich plain-English JPE error cards.
233
+
234
+ ### Option B: Google Colab & Existing Python Notebooks
235
+ In the first cell:
236
+ ```python
237
+ !pip install hoodscript[jupyter]
238
+ %load_ext hoodscript
239
+ ```
240
+ Now write HoodScript in any cell using `%%hood`:
241
+ ```ipython
242
+ %%hood
243
+ bet calculate(x):
244
+ dip x * 10
245
+
246
+ holla "Result:", calculate(4.2)
247
+ ```
248
+ Preview the generated Python anytime with `%%hood_transpile`.
249
+
250
+ ## 12. Run the tests
223
251
 
224
252
  ```bash
225
253
  pytest # with the venv
226
254
  PYTHONPATH=src pytest tests/ # without an editable install
227
255
  ```
228
256
 
229
- ## 12. Where to go next
257
+ ## 13. Where to go next
230
258
 
231
259
  - [`curriculum.md`](curriculum.md) — ten lessons covering the whole language
232
260
  - [`../agents.md`](../agents.md) — how the transpiler, importer, cache, and
@@ -0,0 +1,142 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# HoodScript Interactive Quickstart Notebook\n",
8
+ "\n",
9
+ "Welcome to **HoodScript** in Jupyter! HoodScript maps authentic AAVE keywords **1:1 to Python 3** and explains every error in **plain conversational English**.\n",
10
+ "\n",
11
+ "### Two Ways to Use HoodScript in Notebooks:\n",
12
+ "1. **Native HoodScript Kernel**: Select `HoodScript` from the kernel dropdown.\n",
13
+ "2. **IPython Magic**: In a standard Python notebook or Google Colab, run `%load_ext hoodscript` and use `%%hood` at the top of cells."
14
+ ]
15
+ },
16
+ {
17
+ "cell_type": "code",
18
+ "execution_count": null,
19
+ "metadata": {},
20
+ "outputs": [],
21
+ "source": [
22
+ "# Lesson 1: Values, Booleans, and holla\n",
23
+ "name = \"Khris\"\n",
24
+ "age = 28\n",
25
+ "verified = no cap # True\n",
26
+ "fake = cap # False\n",
27
+ "\n",
28
+ "holla \"User:\", name, \"Age:\", age, \"Verified:\", verified"
29
+ ]
30
+ },
31
+ {
32
+ "cell_type": "markdown",
33
+ "metadata": {},
34
+ "source": [
35
+ "### Live Transpilation Inspection\n",
36
+ "HoodScript adheres strictly to the **1:1 Law**: every line of HoodScript corresponds to valid Python with preserved line numbers.\n",
37
+ "Use `%hood_transpile` to inspect the generated Python code:"
38
+ ]
39
+ },
40
+ {
41
+ "cell_type": "code",
42
+ "execution_count": null,
43
+ "metadata": {},
44
+ "outputs": [],
45
+ "source": [
46
+ "%hood_transpile\n",
47
+ "be i in 1..5:\n",
48
+ " if i == 3:\n",
49
+ " skip\n",
50
+ " holla \"Lap:\", i"
51
+ ]
52
+ },
53
+ {
54
+ "cell_type": "markdown",
55
+ "metadata": {},
56
+ "source": [
57
+ "### Loops and Inclusive Ranges: `be` and `1..n`\n",
58
+ "`be x in xs` represents habitual action (Python's `for x in xs`). `1..5` is an inclusive range (1, 2, 3, 4, 5)."
59
+ ]
60
+ },
61
+ {
62
+ "cell_type": "code",
63
+ "execution_count": null,
64
+ "metadata": {},
65
+ "outputs": [],
66
+ "source": [
67
+ "total = 0\n",
68
+ "be i in 1..10:\n",
69
+ " total = total + i\n",
70
+ "\n",
71
+ "holla \"Sum of 1..10 is:\", total"
72
+ ]
73
+ },
74
+ {
75
+ "cell_type": "markdown",
76
+ "metadata": {},
77
+ "source": [
78
+ "### Functions and Classes: `bet`, `dip`, and `fam`\n",
79
+ "`bet` defines a function; `dip` returns with a value; `fam` defines a class."
80
+ ]
81
+ },
82
+ {
83
+ "cell_type": "code",
84
+ "execution_count": null,
85
+ "metadata": {},
86
+ "outputs": [],
87
+ "source": [
88
+ "bet apply_discount(price, pct=0.15):\n",
89
+ " dip price * (1.0 - pct)\n",
90
+ "\n",
91
+ "fam Shopper:\n",
92
+ " bet __init__(self, name, wallet):\n",
93
+ " self.name = name\n",
94
+ " self.wallet = wallet\n",
95
+ "\n",
96
+ " bet purchase(self, item_name, original_price):\n",
97
+ " final_price = apply_discount(original_price)\n",
98
+ " self.wallet = self.wallet - final_price\n",
99
+ " holla self.name, \"bought\", item_name, \"for\", final_price, \"| Balance remaining:\", self.wallet\n",
100
+ "\n",
101
+ "buyer = Shopper(\"Sam\", 150.0)\n",
102
+ "buyer.purchase(\"Sneakers\", 80.0)"
103
+ ]
104
+ },
105
+ {
106
+ "cell_type": "markdown",
107
+ "metadata": {},
108
+ "source": [
109
+ "### Plain-English Error Handling: `tryna`, `catch`, and `ZeroSplitTrip`\n",
110
+ "Exceptions are never dumped as raw Python tracebacks; they are rendered with plain-English explanations, causes, and suggestions."
111
+ ]
112
+ },
113
+ {
114
+ "cell_type": "code",
115
+ "execution_count": null,
116
+ "metadata": {},
117
+ "outputs": [],
118
+ "source": [
119
+ "tryna:\n",
120
+ " result = 100 / 0\n",
121
+ "catch ZeroSplitTrip as err:\n",
122
+ " holla \"Caught expected error:\", err\n",
123
+ "regardless:\n",
124
+ " holla \"Execution completed clean.\""
125
+ ]
126
+ }
127
+ ],
128
+ "metadata": {
129
+ "language_info": {
130
+ "name": "hoodscript",
131
+ "file_extension": ".hs",
132
+ "mimetype": "text/x-hoodscript"
133
+ },
134
+ "kernelspec": {
135
+ "name": "hoodscript",
136
+ "display_name": "HoodScript",
137
+ "language": "hoodscript"
138
+ }
139
+ },
140
+ "nbformat": 4,
141
+ "nbformat_minor": 2
142
+ }
@@ -16,7 +16,8 @@ repl = ["prompt-toolkit>=3.0.40"]
16
16
  lsp = ["pygls>=1.3.1,<2", "jedi>=0.19.1"]
17
17
  tools = ["ruff>=0.3.0", "mypy>=1.9.0"]
18
18
  pretty = ["rich>=13.7"]
19
- all = ["prompt-toolkit>=3.0.40", "pygls>=1.3.1,<2", "jedi>=0.19.1", "rich>=13.7", "ruff>=0.3.0", "mypy>=1.9.0"]
19
+ jupyter = ["ipykernel>=6.0.0", "ipython>=8.0.0"]
20
+ all = ["prompt-toolkit>=3.0.40", "pygls>=1.3.1,<2", "jedi>=0.19.1", "rich>=13.7", "ruff>=0.3.0", "mypy>=1.9.0", "ipykernel>=6.0.0", "ipython>=8.0.0"]
20
21
  dev = [
21
22
  "rich>=13.7",
22
23
  "pytest>=8.0.0",
@@ -29,11 +30,14 @@ dev = [
29
30
  "pygls>=1.3.1,<2",
30
31
  "jedi>=0.19.1",
31
32
  "hatch-vcs>=0.4.0",
33
+ "ipykernel>=6.0.0",
34
+ "ipython>=8.0.0",
32
35
  ]
33
36
 
34
37
  [project.scripts]
35
38
  hoodscript = "hoodscript.cli:main"
36
39
  hoodscript-lsp = "hoodscript.lsp:run_lsp"
40
+ hoodscript-kernel = "hoodscript.kernel.__main__:main"
37
41
  py2hood = "hoodscript.migrator:py2hood_main"
38
42
  hood2py = "hoodscript.migrator:hood2py_main"
39
43
 
@@ -0,0 +1,16 @@
1
+ from hoodscript.importer import install, uninstall
2
+ from hoodscript.transpiler import transpile
3
+
4
+ try:
5
+ from hoodscript._version import __version__
6
+ except ImportError:
7
+ __version__ = "1.1.0"
8
+
9
+ __all__ = ["transpile", "install", "uninstall", "__version__", "load_ipython_extension"]
10
+
11
+
12
+ def load_ipython_extension(ipython):
13
+ """`%load_ext hoodscript` in IPython/Jupyter: registers the %%hood and
14
+ %hood_transpile magics. Imported lazily so the core stays dependency-free."""
15
+ from hoodscript.kernel.magics import load_ipython_extension as _load
16
+ _load(ipython)
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '1.1.0'
22
- __version_tuple__ = version_tuple = (1, 1, 0)
21
+ __version__ = version = '1.2.0'
22
+ __version_tuple__ = version_tuple = (1, 2, 0)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -25,6 +25,7 @@ Usage:
25
25
  hoodscript debug <file.hs> Debug program under debugpy / DAP
26
26
  hoodscript tokens Show all keywords and mappings
27
27
  hoodscript playground [--port] Launch Web Playground in browser
28
+ hoodscript kernel install Register HoodScript Jupyter kernel
28
29
  hoodscript lsp Start Language Server
29
30
  hoodscript version Show version information
30
31
  hoodscript completion [bash|zsh] Generate shell autocompletion
@@ -86,7 +87,8 @@ _hoodscript_completion() {
86
87
  local cur prev words cword
87
88
  _init_completion || return
88
89
 
89
- local commands="run build check fmt lint repl tokens learn lsp version completion make-stubs debug playground"
90
+ local commands="run build check fmt lint repl tokens learn lsp version"
91
+ commands="$commands completion make-stubs debug playground kernel"
90
92
  local topics="basics flow functions classes errors async mirror all"
91
93
 
92
94
  if [[ $cword -eq 1 ]]; then
@@ -348,6 +350,32 @@ def playground_command(args: list[str]) -> int:
348
350
  return 1
349
351
 
350
352
 
353
+ def kernel_command(args: list[str]) -> int:
354
+ parser = argparse.ArgumentParser(prog="hoodscript kernel", description="Manage HoodScript Jupyter kernel")
355
+ subparsers = parser.add_subparsers(dest="subcommand")
356
+ install_parser = subparsers.add_parser("install", help="Install Jupyter kernelspec")
357
+ install_parser.add_argument("--user", action="store_true", default=True, help="Install for current user (default)")
358
+ install_parser.add_argument("--sys-prefix", action="store_true", help="Install to sys.prefix")
359
+ install_parser.add_argument("--prefix", default=None, help="Install to specified prefix")
360
+ pargs = parser.parse_args(args)
361
+
362
+ if pargs.subcommand == "install":
363
+ try:
364
+ from hoodscript.kernel.installer import install_kernelspec
365
+ except ImportError as exc:
366
+ sys.stderr.write(f"Jupyter dependencies not installed: {exc}\nRun: pip install hoodscript[jupyter]\n")
367
+ return 1
368
+
369
+ prefix = sys.prefix if pargs.sys_prefix else pargs.prefix
370
+ user = not pargs.sys_prefix and pargs.prefix is None
371
+ path = install_kernelspec(user=user, prefix=prefix)
372
+ sys.stdout.write(f"Installed HoodScript kernel spec to {path}\n")
373
+ return 0
374
+
375
+ parser.print_help()
376
+ return 0
377
+
378
+
351
379
  def main(argv=None) -> int:
352
380
  if argv is None:
353
381
  argv = sys.argv[1:]
@@ -397,6 +425,8 @@ def main(argv=None) -> int:
397
425
  return debug_command(argv[1:])
398
426
  if cmd == "playground":
399
427
  return playground_command(argv[1:])
428
+ if cmd == "kernel":
429
+ return kernel_command(argv[1:])
400
430
  if cmd == "build":
401
431
  # build <file> transpiles to stdout (alias for -c)
402
432
  if len(argv) < 2:
@@ -167,7 +167,8 @@ def _fill(template, facts):
167
167
  # --------------------------------------------------------------------------- entry point
168
168
 
169
169
  def diagnose(exc_type, exc, tb, filename=None) -> Diagnostic:
170
- frames = user_frames(tb) if tb is not None else []
170
+ # a SyntaxError is compile-time: the frames belong to whoever called compile(), never to the program
171
+ frames = user_frames(tb) if tb is not None and not isinstance(exc, SyntaxError) else []
171
172
  entry, caught = find(exc)
172
173
  suggestion = _suggest(exc, tb, filename or (frames[-1].filename if frames else None))
173
174
 
@@ -176,6 +177,8 @@ def diagnose(exc_type, exc, tb, filename=None) -> Diagnostic:
176
177
  col = (exc.offset - 1) if exc.offset else None
177
178
  end_col = (exc.end_offset - 1) if getattr(exc, "end_offset", None) else None
178
179
  source_line = (exc.text or "").rstrip("\n")
180
+ if fname and fname.startswith("<") and lineno: # pseudo-file: exc.text is the Python; show the HoodScript
181
+ source_line = linecache.getline(fname, lineno).rstrip("\n") or source_line
179
182
  elif frames:
180
183
  last = frames[-1]
181
184
  fname, lineno = last.filename, last.lineno
@@ -0,0 +1,12 @@
1
+ """HoodScript Jupyter and IPython Kernel Integration.
2
+
3
+ Provides:
4
+ - HoodScriptKernel: Dedicated standalone Jupyter kernel.
5
+ - HoodScriptMagics: IPython extension for %%hood and %%hood_transpile.
6
+ - install_kernelspec: Programmatic and CLI installer for the kernelspec.
7
+ """
8
+
9
+ from hoodscript.kernel.kernel import HoodScriptKernel
10
+ from hoodscript.kernel.magics import load_ipython_extension
11
+
12
+ __all__ = ["HoodScriptKernel", "load_ipython_extension"]
@@ -0,0 +1,13 @@
1
+ """Entry point for launching the HoodScript Jupyter Kernel."""
2
+
3
+ from ipykernel.kernelapp import IPKernelApp
4
+
5
+ from hoodscript.kernel.kernel import HoodScriptKernel
6
+
7
+
8
+ def main():
9
+ IPKernelApp.launch_instance(kernel_class=HoodScriptKernel)
10
+
11
+
12
+ if __name__ == "__main__":
13
+ main()
@@ -0,0 +1,54 @@
1
+ """Kernelspec installer for the HoodScript Jupyter Kernel."""
2
+
3
+ import json
4
+ import shutil
5
+ import sys
6
+ import tempfile
7
+ from pathlib import Path
8
+
9
+ from jupyter_client.kernelspec import KernelSpecManager
10
+
11
+ ROOT = Path(__file__).resolve().parents[3]
12
+ ICON_PATH = ROOT / "editors" / "vscode" / "icon.png"
13
+
14
+ KERNEL_SPEC = {
15
+ "argv": [
16
+ sys.executable,
17
+ "-m",
18
+ "hoodscript.kernel",
19
+ "-f",
20
+ "{connection_file}",
21
+ ],
22
+ "display_name": "HoodScript",
23
+ "language": "hoodscript",
24
+ "metadata": {
25
+ "debugger": False,
26
+ },
27
+ }
28
+
29
+
30
+ def install_kernelspec(user: bool = True, prefix: str | None = None) -> str:
31
+ """Install the HoodScript kernelspec into Jupyter.
32
+
33
+ Returns the directory path where the kernelspec was installed.
34
+ """
35
+ with tempfile.TemporaryDirectory() as td:
36
+ dest_dir = Path(td) / "hoodscript"
37
+ dest_dir.mkdir(parents=True, exist_ok=True)
38
+
39
+ # Write kernel.json
40
+ (dest_dir / "kernel.json").write_text(json.dumps(KERNEL_SPEC, indent=2), encoding="utf-8")
41
+
42
+ # Copy icons if available
43
+ if ICON_PATH.exists():
44
+ shutil.copy2(ICON_PATH, dest_dir / "logo-64x64.png")
45
+ shutil.copy2(ICON_PATH, dest_dir / "logo-32x32.png")
46
+
47
+ manager = KernelSpecManager()
48
+ installed_path = manager.install_kernel_spec(
49
+ str(dest_dir),
50
+ kernel_name="hoodscript",
51
+ user=user,
52
+ prefix=prefix,
53
+ )
54
+ return installed_path