Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ jobs:
- name: Install uv and nox
run: pip install uv nox
- name: Run checks (lint, format, typecheck)
run: nox -s check
run: nox -s check -P 3.12

test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
# python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
python-version: ["3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
Expand All @@ -32,7 +33,7 @@ jobs:
- name: Install uv and nox
run: pip install uv nox
- name: Run tests
run: nox -s test -p ${{ matrix.python-version }}
run: nox -s tests -p ${{ matrix.python-version }}

build:
runs-on: ubuntu-latest
Expand Down
14 changes: 10 additions & 4 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `__enter__()` return value is bound to the `as` variable
- `__exit__(None, None, None)` called in `finally` block
- Falls back to `.close()` for objects without context manager methods
- **Class system enhancements**:
- `super()` support with `super_proxy` runtime helper for proper method delegation
- `@staticmethod` decorator - methods with no `this` binding
- `@classmethod` decorator - methods receiving constructor as first argument
- `@property` decorator with full getter/setter/deleter support via `Object.defineProperty()`
- Comparison special methods: `__eq__`, `__lt__`, `__gt__`, `__le__`, `__ge__` dispatch
- Container special methods: `__getitem__`, `__setitem__`, `__delitem__` dispatch
- Runtime helpers: `op_lt`, `op_gt`, `op_le`, `op_ge`, `op_delitem`, `op_delattr`

### Changed

- **Zero runtime dependencies**: Removed `buildstr`, `plum-dispatch`, and `dukpy`
- `buildstr` replaced with simple string operations in list/tuple generation
- `plum-dispatch` was never used
- `dukpy` dev dependency removed (tests use `quickjs`)
- **Removed dead code**: `src/prescrypt/namespace.py` (unused, superseded by binder)
- **Comparison operators**: Now use runtime helpers for non-primitive types to support special methods

### Statistics

- 2381 tests passing
- 2385 tests passing
- Zero runtime dependencies

## [0.9.1] - 2026-01-19
Expand Down
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.PHONY: all develop test lint clean doc format build
.PHONY: clean clean-build clean-pyc clean-test coverage dist docs install lint lint/flake8
.PHONY: clean clean-test coverage dist docs install lint lint/flake8

# The package name
PKG=pywire
Expand Down Expand Up @@ -79,6 +79,7 @@ clean: clean-test
.pytest_cache .pytest .DS_Store docs/_build docs/cache docs/tmp \
dist build pip-wheel-metadata junit-*.xml htmlcov coverage.xml \
.ruff_cache
cd demos && make clean

## Cleanup tests artifacts
clean-test: ## remove test and coverage artifacts
Expand Down
19 changes: 19 additions & 0 deletions demos/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Demos Makefile - Build all Prescrypt demos

DEMOS = browser browser-extension data-dashboard edge-worker python-playground simulation validation-library

.PHONY: all clean $(DEMOS)

all: $(DEMOS)
@echo "All demos built successfully."

$(DEMOS):
@echo "Building $@..."
@$(MAKE) -C $@ build

clean:
@for demo in $(DEMOS); do \
echo "Cleaning $$demo..."; \
$(MAKE) -C $$demo clean; \
done
@echo "All demos cleaned."
161 changes: 161 additions & 0 deletions demos/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# Prescrypt Demos

Example applications showcasing Python-to-JavaScript transpilation with Prescrypt.

## Quick Start

Each demo follows a consistent structure:

```bash
cd demos/<demo-name>
make build # Compile Python to JavaScript
make serve # Start local server (optional)
```

## Available Demos

| Demo | Description | Key Features |
|------|-------------|--------------|
| [python-playground](python-playground/) | Interactive Python learning | Pre-compiled examples, live output |
| [validation-library](validation-library/) | Shared form validation | Backend/frontend code reuse |
| [data-dashboard](data-dashboard/) | Sales analytics dashboard | Canvas charts, data aggregation |
| [simulation](simulation/) | Predator-prey simulation | Lotka-Volterra model, animation |
| [browser-extension](browser-extension/) | Chrome/Firefox extension | Page analysis, price highlighting |
| [edge-worker](edge-worker/) | Cloudflare Workers | Bot detection, geo-blocking |
| [browser](browser/) | Basic browser example | Simple DOM manipulation |

## Demo Summaries

### Python Playground

Interactive environment with runnable Python examples: Fibonacci, factorial, prime numbers, sorting algorithms, classes, comprehensions, and FizzBuzz.

```python
def fibonacci(n: int) -> list:
if n <= 0:
return []
fib = [0, 1]
while len(fib) < n:
fib.append(fib[-1] + fib[-2])
return fib[:n]
```

### Validation Library

Share validation logic between Python backend and JavaScript frontend. Includes email validation with typo detection, password strength checking, phone number formatting, and credit card validation (Luhn algorithm).

```python
class ValidationResult:
@staticmethod
def ok():
return ValidationResult(True, [])

@staticmethod
def error(message: str):
return ValidationResult(False, [message])
```

### Data Dashboard

Sales analytics with KPI cards, bar charts, line charts, and pie charts. All rendering done with Canvas 2D API - no external libraries.

```python
def group_by(data: list, key: str) -> dict:
groups = {}
for item in data:
k = item[key]
if k not in groups:
groups[k] = []
groups[k].append(item)
return groups
```

### Simulation

Predator-prey population dynamics using the Lotka-Volterra equations. Features real-time animation, parameter controls, and phase space visualization.

```python
def lotka_volterra_step(prey, predators, params):
prey_change = (
params.prey_birth_rate * prey
- params.predation_rate * prey * predators
) * params.dt
# ...
```

### Browser Extension

Chrome/Firefox extension written in Python. Analyzes web pages: highlights expensive items, estimates reading time, counts internal/external links, and provides text statistics.

```python
def highlight_prices():
for el in js.document.querySelectorAll(".price"):
price = parse_price(el.textContent)
if price > Config.price_threshold:
el.style.backgroundColor = "#fef3c7"
```

### Edge Worker

Cloudflare Workers edge computing logic. Includes bot detection, geo-blocking, A/B testing, and security header injection.

```python
def is_bot(user_agent: str) -> bool:
ua_lower = user_agent.lower()
for pattern in BOT_PATTERNS:
if pattern in ua_lower:
return True
return False
```

## Common Patterns

### JavaScript FFI

Access browser APIs via `import js`:

```python
import js

# DOM manipulation
el = js.document.getElementById("my-id")
el.textContent = "Hello"

# Canvas
ctx = canvas.getContext("2d")
ctx.fillRect(0, 0, 100, 100)
```

### Event Handlers

Use factory functions to create handlers with closures:

```python
def make_click_handler(item):
def handler(event):
process(item)
return handler

button.addEventListener("click", make_click_handler(data))
```

### Classes

Full class support including `@staticmethod`, `@classmethod`, and `@property`:

```python
class Config:
@staticmethod
def default():
return Config(100, 200)

@property
def total(self):
return self.a + self.b
```

## Requirements

- Python 3.9+
- Prescrypt (`pip install prescrypt` or `uv sync` in repo root)
- Modern browser (Chrome, Firefox, Safari, Edge, Brave)
19 changes: 19 additions & 0 deletions demos/browser-extension/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.PHONY: build package test clean

build:
py2js src/content.py -o dist/content.js

package: build
@echo "Extension ready to load in browser."
@echo ""
@echo "Chrome: chrome://extensions → Enable Developer mode → Load unpacked"
@echo "Firefox: about:debugging → This Firefox → Load Temporary Add-on"

test: build
@echo "Opening test page..."
python -m http.server 8004 &
@sleep 1
open http://localhost:8004/test-page.html || xdg-open http://localhost:8004/test-page.html

clean:
rm -rf dist/*.js dist/*.map
93 changes: 93 additions & 0 deletions demos/browser-extension/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Browser Extension Demo

Write Chrome/Firefox extensions in Python.

## Use Case

Python developers can create browser extensions without learning JavaScript.
The extension logic is written in Python and compiled to JS by Prescrypt.

## Features

This demo extension analyzes web pages:

- **Price Highlighting**: Marks items over $100 with yellow background
- **Reading Time**: Estimates time to read based on word count
- **Link Analysis**: Counts internal vs external links
- **Text Statistics**: Paragraphs, headings, sentences, characters

## Project Structure

```
browser-extension/
├── manifest.json # Extension manifest (Chrome/Firefox)
├── popup.html # Extension popup UI
├── src/
│ └── content.py # Python content script
├── dist/
│ └── content.js # Compiled JavaScript
├── icons/ # Extension icons
└── test-page.html # Test page with sample content
```

## Build

```bash
make build # Compile Python to JavaScript
```

## Install in Browser

### Chrome

1. Go to `chrome://extensions`
2. Enable "Developer mode" (top right)
3. Click "Load unpacked"
4. Select this directory

### Firefox

1. Go to `about:debugging`
2. Click "This Firefox"
3. Click "Load Temporary Add-on"
4. Select `manifest.json`

## Test

```bash
make test # Opens test page in browser
```

Or manually open `test-page.html` with the extension loaded.

## Python Highlights

```python
def parse_price(text: str) -> float:
"""Extract numeric price from '$99.99' or '€199,00'."""
# Handle both US and European formats
...

def highlight_prices():
"""Find and highlight prices above threshold."""
for el in js.document.querySelectorAll(".price"):
price = parse_price(el.textContent)
if price > Config.price_threshold:
el.style.backgroundColor = "#fef3c7"
el.style.border = "2px solid #f59e0b"
```

## Why Prescrypt?

- Write extension logic in familiar Python syntax
- No need to learn JavaScript/TypeScript
- Same analysis code can run on backend
- Tiny output (content script ~15 KB)

## Production Enhancements

- Add options page for configuration
- Persist settings with `chrome.storage`
- Add keyboard shortcuts
- Inject CSS instead of inline styles
- Add i18n support
10 changes: 10 additions & 0 deletions demos/browser-extension/icons/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demos/browser-extension/icons/icon128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demos/browser-extension/icons/icon16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demos/browser-extension/icons/icon48.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading