pyxle-framework 0.1.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.
- pyxle_framework-0.1.0/.github/pyxle-logo.svg +19 -0
- pyxle_framework-0.1.0/.gitignore +58 -0
- pyxle_framework-0.1.0/CLAUDE.md +386 -0
- pyxle_framework-0.1.0/Makefile +15 -0
- pyxle_framework-0.1.0/PKG-INFO +136 -0
- pyxle_framework-0.1.0/README.md +99 -0
- pyxle_framework-0.1.0/docs/README.md +67 -0
- pyxle_framework-0.1.0/docs/advanced/compiler-internals.md +159 -0
- pyxle_framework-0.1.0/docs/advanced/ssr-pipeline.md +144 -0
- pyxle_framework-0.1.0/docs/core-concepts/data-loading.md +140 -0
- pyxle_framework-0.1.0/docs/core-concepts/layouts.md +103 -0
- pyxle_framework-0.1.0/docs/core-concepts/pyx-files.md +174 -0
- pyxle_framework-0.1.0/docs/core-concepts/routing.md +132 -0
- pyxle_framework-0.1.0/docs/core-concepts/server-actions.md +161 -0
- pyxle_framework-0.1.0/docs/faq.md +203 -0
- pyxle_framework-0.1.0/docs/getting-started/installation.md +58 -0
- pyxle_framework-0.1.0/docs/getting-started/project-structure.md +158 -0
- pyxle_framework-0.1.0/docs/getting-started/quick-start.md +99 -0
- pyxle_framework-0.1.0/docs/guides/api-routes.md +136 -0
- pyxle_framework-0.1.0/docs/guides/client-components.md +146 -0
- pyxle_framework-0.1.0/docs/guides/deployment.md +187 -0
- pyxle_framework-0.1.0/docs/guides/environment-variables.md +124 -0
- pyxle_framework-0.1.0/docs/guides/error-handling.md +136 -0
- pyxle_framework-0.1.0/docs/guides/head-management.md +133 -0
- pyxle_framework-0.1.0/docs/guides/middleware.md +127 -0
- pyxle_framework-0.1.0/docs/guides/security.md +146 -0
- pyxle_framework-0.1.0/docs/guides/styling.md +133 -0
- pyxle_framework-0.1.0/docs/reference/cli.md +212 -0
- pyxle_framework-0.1.0/docs/reference/client-api.md +229 -0
- pyxle_framework-0.1.0/docs/reference/configuration.md +212 -0
- pyxle_framework-0.1.0/docs/reference/runtime-api.md +161 -0
- pyxle_framework-0.1.0/pyproject.toml +74 -0
- pyxle_framework-0.1.0/pyxle/__init__.py +7 -0
- pyxle_framework-0.1.0/pyxle/cli/__init__.py +1176 -0
- pyxle_framework-0.1.0/pyxle/cli/assets.py +17 -0
- pyxle_framework-0.1.0/pyxle/cli/init.py +117 -0
- pyxle_framework-0.1.0/pyxle/cli/logger.py +192 -0
- pyxle_framework-0.1.0/pyxle/cli/scaffold.py +100 -0
- pyxle_framework-0.1.0/pyxle/cli/templates.py +49 -0
- pyxle_framework-0.1.0/pyxle/client/ClientOnly.jsx +18 -0
- pyxle_framework-0.1.0/pyxle/client/Form.jsx +133 -0
- pyxle_framework-0.1.0/pyxle/client/Head.jsx +34 -0
- pyxle_framework-0.1.0/pyxle/client/Image.jsx +27 -0
- pyxle_framework-0.1.0/pyxle/client/Script.jsx +19 -0
- pyxle_framework-0.1.0/pyxle/client/index.js +18 -0
- pyxle_framework-0.1.0/pyxle/client/useAction.jsx +107 -0
- pyxle_framework-0.1.0/pyxle/compiler/__init__.py +7 -0
- pyxle_framework-0.1.0/pyxle/compiler/core.py +68 -0
- pyxle_framework-0.1.0/pyxle/compiler/exceptions.py +18 -0
- pyxle_framework-0.1.0/pyxle/compiler/jsx_imports.py +372 -0
- pyxle_framework-0.1.0/pyxle/compiler/jsx_parser.py +127 -0
- pyxle_framework-0.1.0/pyxle/compiler/model.py +128 -0
- pyxle_framework-0.1.0/pyxle/compiler/parser.py +941 -0
- pyxle_framework-0.1.0/pyxle/compiler/writers.py +306 -0
- pyxle_framework-0.1.0/pyxle/config.py +500 -0
- pyxle_framework-0.1.0/pyxle/devserver/__init__.py +276 -0
- pyxle_framework-0.1.0/pyxle/devserver/build.py +181 -0
- pyxle_framework-0.1.0/pyxle/devserver/builder.py +167 -0
- pyxle_framework-0.1.0/pyxle/devserver/client_files.py +2078 -0
- pyxle_framework-0.1.0/pyxle/devserver/csrf.py +160 -0
- pyxle_framework-0.1.0/pyxle/devserver/error_pages.py +139 -0
- pyxle_framework-0.1.0/pyxle/devserver/layouts.py +295 -0
- pyxle_framework-0.1.0/pyxle/devserver/middleware.py +74 -0
- pyxle_framework-0.1.0/pyxle/devserver/overlay.py +105 -0
- pyxle_framework-0.1.0/pyxle/devserver/path_utils.py +15 -0
- pyxle_framework-0.1.0/pyxle/devserver/proxy.py +157 -0
- pyxle_framework-0.1.0/pyxle/devserver/registry.py +381 -0
- pyxle_framework-0.1.0/pyxle/devserver/route_hooks.py +226 -0
- pyxle_framework-0.1.0/pyxle/devserver/routes.py +245 -0
- pyxle_framework-0.1.0/pyxle/devserver/scanner.py +102 -0
- pyxle_framework-0.1.0/pyxle/devserver/scripts.py +159 -0
- pyxle_framework-0.1.0/pyxle/devserver/settings.py +150 -0
- pyxle_framework-0.1.0/pyxle/devserver/starlette_app.py +658 -0
- pyxle_framework-0.1.0/pyxle/devserver/styles.py +182 -0
- pyxle_framework-0.1.0/pyxle/devserver/tailwind.py +269 -0
- pyxle_framework-0.1.0/pyxle/devserver/vite.py +374 -0
- pyxle_framework-0.1.0/pyxle/devserver/watcher.py +352 -0
- pyxle_framework-0.1.0/pyxle/env.py +224 -0
- pyxle_framework-0.1.0/pyxle/routing/__init__.py +13 -0
- pyxle_framework-0.1.0/pyxle/routing/paths.py +137 -0
- pyxle_framework-0.1.0/pyxle/runtime/ClientOnly.jsx +46 -0
- pyxle_framework-0.1.0/pyxle/runtime/Head.jsx +41 -0
- pyxle_framework-0.1.0/pyxle/runtime/Image.jsx +86 -0
- pyxle_framework-0.1.0/pyxle/runtime/Script.jsx +39 -0
- pyxle_framework-0.1.0/pyxle/runtime.py +83 -0
- pyxle_framework-0.1.0/pyxle/ssr/__init__.py +49 -0
- pyxle_framework-0.1.0/pyxle/ssr/head_merger.py +368 -0
- pyxle_framework-0.1.0/pyxle/ssr/render_component.mjs +256 -0
- pyxle_framework-0.1.0/pyxle/ssr/renderer.py +311 -0
- pyxle_framework-0.1.0/pyxle/ssr/ssr_worker.mjs +297 -0
- pyxle_framework-0.1.0/pyxle/ssr/template.py +399 -0
- pyxle_framework-0.1.0/pyxle/ssr/view.py +691 -0
- pyxle_framework-0.1.0/pyxle/ssr/worker_pool.py +285 -0
- pyxle_framework-0.1.0/pyxle/templates/__init__.py +1 -0
- pyxle_framework-0.1.0/pyxle/templates/scaffold/.gitignore +25 -0
- pyxle_framework-0.1.0/pyxle/templates/scaffold/__init__.py +1 -0
- pyxle_framework-0.1.0/pyxle/templates/scaffold/package.json +26 -0
- pyxle_framework-0.1.0/pyxle/templates/scaffold/pages/api/pulse.py +59 -0
- pyxle_framework-0.1.0/pyxle/templates/scaffold/pages/index.pyx +345 -0
- pyxle_framework-0.1.0/pyxle/templates/scaffold/pages/layout.pyx +8 -0
- pyxle_framework-0.1.0/pyxle/templates/scaffold/pages/styles/tailwind.css +9 -0
- pyxle_framework-0.1.0/pyxle/templates/scaffold/postcss.config.cjs +6 -0
- pyxle_framework-0.1.0/pyxle/templates/scaffold/public/branding/pyxle-grid.svg +15 -0
- pyxle_framework-0.1.0/pyxle/templates/scaffold/public/branding/pyxle-mark.svg +14 -0
- pyxle_framework-0.1.0/pyxle/templates/scaffold/public/branding/pyxle-wordmark-dark.svg +5 -0
- pyxle_framework-0.1.0/pyxle/templates/scaffold/public/branding/pyxle-wordmark-light.svg +5 -0
- pyxle_framework-0.1.0/pyxle/templates/scaffold/public/styles/tailwind.css +1 -0
- pyxle_framework-0.1.0/pyxle/templates/scaffold/pyxle.config.json +3 -0
- pyxle_framework-0.1.0/pyxle/templates/scaffold/requirements.txt +3 -0
- pyxle_framework-0.1.0/pyxle/templates/scaffold/tailwind.config.cjs +19 -0
- pyxle_framework-0.1.0/tests/cli/test_commands.py +1602 -0
- pyxle_framework-0.1.0/tests/cli/test_logger.py +219 -0
- pyxle_framework-0.1.0/tests/cli/test_scaffold.py +51 -0
- pyxle_framework-0.1.0/tests/cli/test_templates.py +44 -0
- pyxle_framework-0.1.0/tests/compiler/test_action_compile.py +140 -0
- pyxle_framework-0.1.0/tests/compiler/test_action_model.py +65 -0
- pyxle_framework-0.1.0/tests/compiler/test_action_parser.py +269 -0
- pyxle_framework-0.1.0/tests/compiler/test_action_writers.py +145 -0
- pyxle_framework-0.1.0/tests/compiler/test_compile.py +487 -0
- pyxle_framework-0.1.0/tests/compiler/test_head_jsx.py +298 -0
- pyxle_framework-0.1.0/tests/compiler/test_jsx_imports.py +237 -0
- pyxle_framework-0.1.0/tests/compiler/test_parser.py +821 -0
- pyxle_framework-0.1.0/tests/compiler/test_parser_hardening.py +683 -0
- pyxle_framework-0.1.0/tests/compiler/test_script_image_detection.py +195 -0
- pyxle_framework-0.1.0/tests/compiler/test_script_image_integration.py +170 -0
- pyxle_framework-0.1.0/tests/devserver/sample_middlewares.py +96 -0
- pyxle_framework-0.1.0/tests/devserver/test_action_routes.py +448 -0
- pyxle_framework-0.1.0/tests/devserver/test_build.py +133 -0
- pyxle_framework-0.1.0/tests/devserver/test_builder.py +213 -0
- pyxle_framework-0.1.0/tests/devserver/test_client_files.py +232 -0
- pyxle_framework-0.1.0/tests/devserver/test_client_only.py +73 -0
- pyxle_framework-0.1.0/tests/devserver/test_csrf.py +156 -0
- pyxle_framework-0.1.0/tests/devserver/test_devserver_start.py +839 -0
- pyxle_framework-0.1.0/tests/devserver/test_error_pages.py +259 -0
- pyxle_framework-0.1.0/tests/devserver/test_layouts.py +108 -0
- pyxle_framework-0.1.0/tests/devserver/test_middleware.py +41 -0
- pyxle_framework-0.1.0/tests/devserver/test_overlay.py +104 -0
- pyxle_framework-0.1.0/tests/devserver/test_proxy.py +189 -0
- pyxle_framework-0.1.0/tests/devserver/test_registry.py +280 -0
- pyxle_framework-0.1.0/tests/devserver/test_route_error_boundary.py +140 -0
- pyxle_framework-0.1.0/tests/devserver/test_route_hooks.py +112 -0
- pyxle_framework-0.1.0/tests/devserver/test_routes.py +173 -0
- pyxle_framework-0.1.0/tests/devserver/test_scanner.py +107 -0
- pyxle_framework-0.1.0/tests/devserver/test_scripts.py +132 -0
- pyxle_framework-0.1.0/tests/devserver/test_settings.py +223 -0
- pyxle_framework-0.1.0/tests/devserver/test_starlette_app.py +628 -0
- pyxle_framework-0.1.0/tests/devserver/test_styles.py +151 -0
- pyxle_framework-0.1.0/tests/devserver/test_tailwind.py +467 -0
- pyxle_framework-0.1.0/tests/devserver/test_vite.py +671 -0
- pyxle_framework-0.1.0/tests/devserver/test_watcher.py +565 -0
- pyxle_framework-0.1.0/tests/ssr/test_dynamic_head.py +23 -0
- pyxle_framework-0.1.0/tests/ssr/test_head_merger_extra.py +404 -0
- pyxle_framework-0.1.0/tests/ssr/test_head_merging.py +389 -0
- pyxle_framework-0.1.0/tests/ssr/test_integration.py +103 -0
- pyxle_framework-0.1.0/tests/ssr/test_renderer.py +399 -0
- pyxle_framework-0.1.0/tests/ssr/test_script_injection.py +212 -0
- pyxle_framework-0.1.0/tests/ssr/test_template.py +344 -0
- pyxle_framework-0.1.0/tests/ssr/test_view.py +1159 -0
- pyxle_framework-0.1.0/tests/ssr/test_view_error_boundaries.py +262 -0
- pyxle_framework-0.1.0/tests/ssr/test_worker_pool.py +868 -0
- pyxle_framework-0.1.0/tests/ssr/utils.py +99 -0
- pyxle_framework-0.1.0/tests/test_config.py +316 -0
- pyxle_framework-0.1.0/tests/test_config_security.py +225 -0
- pyxle_framework-0.1.0/tests/test_env.py +337 -0
- pyxle_framework-0.1.0/tests/test_runtime.py +92 -0
- pyxle_framework-0.1.0/tests/test_runtime_errors.py +76 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<svg width="180" height="48" viewBox="0 0 180 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="pyxle-mark-a" x1="8" y1="5" x2="40" y2="43" gradientUnits="userSpaceOnUse">
|
|
4
|
+
<stop offset="0" stop-color="#6ee7b7" />
|
|
5
|
+
<stop offset="0.5" stop-color="#34d399" />
|
|
6
|
+
<stop offset="1" stop-color="#059669" />
|
|
7
|
+
</linearGradient>
|
|
8
|
+
</defs>
|
|
9
|
+
<path
|
|
10
|
+
d="M14 40 V8 H28 Q38 8 38 18 Q38 28 28 28 H14"
|
|
11
|
+
stroke="url(#pyxle-mark-a)"
|
|
12
|
+
stroke-width="3.5"
|
|
13
|
+
stroke-linecap="round"
|
|
14
|
+
stroke-linejoin="round"
|
|
15
|
+
fill="none"
|
|
16
|
+
/>
|
|
17
|
+
<circle cx="28" cy="18" r="2.8" fill="#6ee7b7" />
|
|
18
|
+
<text x="52" y="34" font-family="system-ui, -apple-system, 'Segoe UI', sans-serif" font-weight="700" font-size="30" fill="#6ee7b7" letter-spacing="0.01em">Pyxle</text>
|
|
19
|
+
</svg>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Pyxle build artifacts
|
|
2
|
+
.pyxle-node-cache/
|
|
3
|
+
.pyxle-build/
|
|
4
|
+
.pyxle/.lang-virtual/
|
|
5
|
+
|
|
6
|
+
# Node
|
|
7
|
+
node_modules/
|
|
8
|
+
|
|
9
|
+
# Python bytecode
|
|
10
|
+
__pycache__/
|
|
11
|
+
*.py[codz]
|
|
12
|
+
*$py.class
|
|
13
|
+
|
|
14
|
+
# Distribution / packaging
|
|
15
|
+
build/
|
|
16
|
+
!docs/build/
|
|
17
|
+
dist/
|
|
18
|
+
*.egg-info/
|
|
19
|
+
*.egg
|
|
20
|
+
.eggs/
|
|
21
|
+
|
|
22
|
+
# Virtual environments
|
|
23
|
+
venv/
|
|
24
|
+
.venv/
|
|
25
|
+
|
|
26
|
+
# Test / coverage
|
|
27
|
+
htmlcov/
|
|
28
|
+
.tox/
|
|
29
|
+
.nox/
|
|
30
|
+
.coverage
|
|
31
|
+
.coverage.*
|
|
32
|
+
.pytest_cache/
|
|
33
|
+
coverage.xml
|
|
34
|
+
*.cover
|
|
35
|
+
*.py.cover
|
|
36
|
+
|
|
37
|
+
# IDE
|
|
38
|
+
.idea/
|
|
39
|
+
.vscode/
|
|
40
|
+
*.swp
|
|
41
|
+
*.swo
|
|
42
|
+
*~
|
|
43
|
+
|
|
44
|
+
# OS
|
|
45
|
+
.DS_Store
|
|
46
|
+
Thumbs.db
|
|
47
|
+
|
|
48
|
+
# Logs
|
|
49
|
+
*.log
|
|
50
|
+
|
|
51
|
+
# Environment
|
|
52
|
+
.env
|
|
53
|
+
.env.*
|
|
54
|
+
!.env.example
|
|
55
|
+
|
|
56
|
+
# Internal docs (kept locally, not published)
|
|
57
|
+
PYXLE_AUDIT.md
|
|
58
|
+
ROADMAP.md
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
# CLAUDE.md — Pyxle Framework Development Guide
|
|
2
|
+
|
|
3
|
+
This file instructs Claude (and any AI agent) on how to work on the Pyxle core framework.
|
|
4
|
+
Every rule here exists to keep Pyxle enterprise-grade, stable, and maintainable.
|
|
5
|
+
|
|
6
|
+
## Related Repositories
|
|
7
|
+
|
|
8
|
+
Pyxle is split across multiple repos. This is the **core framework**. Related repos:
|
|
9
|
+
- `pyxle-langkit` — LSP server, linter, and VS Code extension (depends on this repo)
|
|
10
|
+
- `pyxle-plugins` — Official plugins (auth, etc.) — each plugin depends on this repo
|
|
11
|
+
- `pyxle-dev` — Private repo for the pyxle.dev website (a Pyxle app) (May not be available to external contributors)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Project Overview
|
|
16
|
+
|
|
17
|
+
Pyxle is a Python-first full-stack web framework. `.pyx` files colocate Python server
|
|
18
|
+
logic (`@server` loaders, `@action` mutations) with React/JSX components. The stack is
|
|
19
|
+
Starlette (ASGI), Vite (bundling), React 18 (rendering), and esbuild (SSR transpilation).
|
|
20
|
+
|
|
21
|
+
**Key files to read first:**
|
|
22
|
+
- `ROADMAP.md` — current phase, pending tasks, design principles
|
|
23
|
+
- `PYXLE_AUDIT.md` — architectural strengths, risks, and bottlenecks
|
|
24
|
+
- `pyproject.toml` — dependencies, test config, coverage thresholds
|
|
25
|
+
- `pyxle/runtime.py` — the `@server` and `@action` decorator contracts
|
|
26
|
+
- `pyxle/compiler/parser.py` — the `.pyx` parser (most complex module)
|
|
27
|
+
- `pyxle/devserver/starlette_app.py` — request routing and middleware stack
|
|
28
|
+
- `pyxle/ssr/renderer.py` — SSR rendering pipeline (performance-critical)
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Mandatory Rules
|
|
33
|
+
|
|
34
|
+
### 1. Run Tests After Every Change
|
|
35
|
+
|
|
36
|
+
**This is non-negotiable.** Every code change must be followed by running the test suite.
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Run the full test suite
|
|
40
|
+
pytest
|
|
41
|
+
|
|
42
|
+
# The above command uses pyproject.toml defaults:
|
|
43
|
+
# --strict-markers --strict-config
|
|
44
|
+
# --cov=pyxle.cli --cov=pyxle.compiler --cov=pyxle.devserver --cov=pyxle.ssr
|
|
45
|
+
# --cov-report=term-missing
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
- **Coverage threshold is 95%.** The build fails below this. Do not lower it.
|
|
49
|
+
- **All tests must pass.** Zero test failures are acceptable.
|
|
50
|
+
- **If you break a test, fix it before moving on.** Do not leave failing tests for later.
|
|
51
|
+
- **If you add a feature, add tests for it** in the same change. No feature ships without tests.
|
|
52
|
+
|
|
53
|
+
### 2. Write Tests First When Possible
|
|
54
|
+
|
|
55
|
+
Prefer test-driven development:
|
|
56
|
+
1. Write a failing test that describes the expected behavior
|
|
57
|
+
2. Implement the minimum code to make it pass
|
|
58
|
+
3. Refactor while keeping tests green
|
|
59
|
+
|
|
60
|
+
### 3. Never Skip or Weaken Tests
|
|
61
|
+
|
|
62
|
+
- DO NOT add `@pytest.mark.skip`, `pytest.mark.xfail`, or `# pragma: no cover` to dodge coverage
|
|
63
|
+
- DO NOT delete tests to make the suite pass
|
|
64
|
+
- DO NOT lower `fail_under = 95` in `pyproject.toml`
|
|
65
|
+
- DO NOT remove modules from the `--cov=` list
|
|
66
|
+
|
|
67
|
+
### 4. Test File Location Convention
|
|
68
|
+
|
|
69
|
+
Tests mirror the source tree:
|
|
70
|
+
```
|
|
71
|
+
pyxle/cli/ -> tests/cli/
|
|
72
|
+
pyxle/compiler/ -> tests/compiler/
|
|
73
|
+
pyxle/devserver/ -> tests/devserver/
|
|
74
|
+
pyxle/ssr/ -> tests/ssr/
|
|
75
|
+
pyxle/build/ -> tests/build/
|
|
76
|
+
pyxle/config.py -> tests/test_config.py
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
When creating a new module at `pyxle/foo/bar.py`, create `tests/foo/test_bar.py`.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Architecture Rules
|
|
84
|
+
|
|
85
|
+
### 5. Respect Module Boundaries
|
|
86
|
+
|
|
87
|
+
The codebase has clear separation of concerns:
|
|
88
|
+
|
|
89
|
+
| Module | Responsibility | May Import From |
|
|
90
|
+
|--------|---------------|-----------------|
|
|
91
|
+
| `pyxle/cli/` | CLI commands, user-facing I/O | Everything below |
|
|
92
|
+
| `pyxle/devserver/` | Dev server, Vite proxy, file watcher | compiler, ssr, routing, config |
|
|
93
|
+
| `pyxle/ssr/` | Server-side rendering, head merging | compiler (models only), config |
|
|
94
|
+
| `pyxle/compiler/` | `.pyx` parsing, code generation | Nothing from pyxle (standalone) |
|
|
95
|
+
| `pyxle/routing/` | File-based route calculation | Nothing from pyxle (standalone) |
|
|
96
|
+
| `pyxle/build/` | Production build pipeline | compiler, devserver, config |
|
|
97
|
+
| `pyxle/config.py` | Configuration parsing | Nothing from pyxle (standalone) |
|
|
98
|
+
| `pyxle/runtime.py` | `@server`, `@action` decorators | Nothing from pyxle (standalone) |
|
|
99
|
+
| `pyxle/client/` | Client-side JS/JSX components | N/A (not Python -- JS only) |
|
|
100
|
+
|
|
101
|
+
**DO NOT** introduce circular imports. **DO NOT** have `compiler` depend on `devserver`.
|
|
102
|
+
**DO NOT** have `runtime.py` import anything from the framework -- it must stay zero-dependency
|
|
103
|
+
because it's injected into user code.
|
|
104
|
+
|
|
105
|
+
### 6. Frozen Dataclasses Everywhere
|
|
106
|
+
|
|
107
|
+
All data-carrying classes must be frozen dataclasses:
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
# CORRECT
|
|
111
|
+
@dataclass(frozen=True)
|
|
112
|
+
class PageRoute:
|
|
113
|
+
path: str
|
|
114
|
+
module_key: str
|
|
115
|
+
has_loader: bool
|
|
116
|
+
|
|
117
|
+
# WRONG -- mutable state causes bugs in async code
|
|
118
|
+
@dataclass
|
|
119
|
+
class PageRoute:
|
|
120
|
+
path: str
|
|
121
|
+
module_key: str
|
|
122
|
+
has_loader: bool
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Use `slots=True` for internal-only dataclasses that benefit from memory efficiency.
|
|
126
|
+
|
|
127
|
+
### 7. Use `Sequence` and `tuple` for Immutable Collections
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
# CORRECT -- signals immutability
|
|
131
|
+
def process_routes(routes: Sequence[PageRoute]) -> tuple[str, ...]: ...
|
|
132
|
+
|
|
133
|
+
# WRONG -- signals mutability
|
|
134
|
+
def process_routes(routes: list[PageRoute]) -> list[str]: ...
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Store collection fields as `tuple`, not `list`, in frozen dataclasses.
|
|
138
|
+
|
|
139
|
+
### 8. Async by Default
|
|
140
|
+
|
|
141
|
+
All I/O operations must be async. Never block the event loop.
|
|
142
|
+
|
|
143
|
+
If wrapping a synchronous call (like `subprocess.run`), use `asyncio.to_thread()`.
|
|
144
|
+
|
|
145
|
+
### 9. Use Structured Error Types
|
|
146
|
+
|
|
147
|
+
Every error that a user or developer might see needs a specific exception class.
|
|
148
|
+
Error classes live in the module they belong to (e.g., `pyxle/compiler/exceptions.py`).
|
|
149
|
+
Error messages must be specific, actionable, and include context (file path, line number, etc.).
|
|
150
|
+
|
|
151
|
+
### 10. No Magic, No Hidden Behavior
|
|
152
|
+
|
|
153
|
+
Decorators add metadata. They do NOT wrap, transform, or hide behavior.
|
|
154
|
+
The same principle applies to `@action` and any future decorators.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Code Quality Rules
|
|
159
|
+
|
|
160
|
+
### 11. Type Hints on All Public APIs
|
|
161
|
+
|
|
162
|
+
Every public function, method, and class must have complete type hints.
|
|
163
|
+
Internal helpers may omit return types if the logic is trivial, but parameters must always be typed.
|
|
164
|
+
|
|
165
|
+
### 12. Docstrings on Public APIs
|
|
166
|
+
|
|
167
|
+
Every public function, class, and module needs a docstring. Keep them concise -- describe
|
|
168
|
+
*what* and *why*, not *how*.
|
|
169
|
+
|
|
170
|
+
### 13. No Print Statements
|
|
171
|
+
|
|
172
|
+
Use the CLI logger (`pyxle/cli/logger.py`) for user-facing output. Use Python `logging`
|
|
173
|
+
module for internal diagnostics. Never use `print()`.
|
|
174
|
+
|
|
175
|
+
### 14. Run Ruff Before Committing
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
ruff check pyxle/ tests/
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Fix all lint errors. Do not add `# noqa` unless there's a documented reason.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Performance Rules
|
|
186
|
+
|
|
187
|
+
### 15. SSR is the Hot Path
|
|
188
|
+
|
|
189
|
+
`pyxle/ssr/` is the most performance-critical code. Every millisecond matters.
|
|
190
|
+
|
|
191
|
+
- **DO NOT** add synchronous I/O to the SSR request path
|
|
192
|
+
- **DO NOT** add new imports to modules loaded on every request
|
|
193
|
+
- **DO NOT** grow caches without eviction policies
|
|
194
|
+
- **DO** profile before and after any SSR change
|
|
195
|
+
- **DO** consider the cost at 100 concurrent requests, not just 1
|
|
196
|
+
|
|
197
|
+
### 16. Lazy Imports for Heavy Modules
|
|
198
|
+
|
|
199
|
+
Modules that are only needed in specific code paths should be imported lazily.
|
|
200
|
+
This keeps CLI startup fast and avoids circular import issues.
|
|
201
|
+
|
|
202
|
+
### 17. No Unbounded Caches
|
|
203
|
+
|
|
204
|
+
Every cache must have a max size or TTL. Document the eviction strategy.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Security Rules
|
|
209
|
+
|
|
210
|
+
### 18. Never Trust User Input
|
|
211
|
+
|
|
212
|
+
- Escape all dynamic content injected into HTML (especially HEAD elements)
|
|
213
|
+
- Validate file paths -- never allow path traversal (`../`)
|
|
214
|
+
- Use parameterized queries -- never string-interpolate SQL
|
|
215
|
+
- Sanitize route parameters
|
|
216
|
+
- Never expose stack traces, file paths, or internal state in production error responses
|
|
217
|
+
|
|
218
|
+
### 19. Subprocess Safety
|
|
219
|
+
|
|
220
|
+
- Build command arrays programmatically -- never use `shell=True` with user input
|
|
221
|
+
- Set timeouts on all subprocess calls
|
|
222
|
+
- Capture stderr and handle errors
|
|
223
|
+
- Clean up temp files in `finally` blocks
|
|
224
|
+
|
|
225
|
+
### 20. Secrets Stay Server-Side
|
|
226
|
+
|
|
227
|
+
- Environment variables without `PYXLE_PUBLIC_` prefix must NEVER appear in client bundles
|
|
228
|
+
- Loader and action return values are serialized to JSON and sent to the client -- never
|
|
229
|
+
include secrets, tokens, or internal IDs that shouldn't be exposed
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Development Workflow
|
|
234
|
+
|
|
235
|
+
### 21. Branch and Change Management
|
|
236
|
+
|
|
237
|
+
- Read `ROADMAP.md` before starting work -- find the relevant phase and task
|
|
238
|
+
- Work on one task at a time -- complete it (including tests) before moving to the next
|
|
239
|
+
- Mark completed tasks in `ROADMAP.md` by changing `[ ]` to `[x]`
|
|
240
|
+
- Keep commits focused: one logical change per commit
|
|
241
|
+
|
|
242
|
+
### 22. Adding a New Feature -- Checklist
|
|
243
|
+
|
|
244
|
+
Before writing code:
|
|
245
|
+
1. Identify the relevant `ROADMAP.md` task
|
|
246
|
+
2. Understand how it fits in the architecture (which modules are affected?)
|
|
247
|
+
3. Check for existing patterns to follow (look at similar completed features)
|
|
248
|
+
|
|
249
|
+
While writing code:
|
|
250
|
+
4. Add/update frozen dataclasses in the relevant `model.py`
|
|
251
|
+
5. Implement the logic following module boundary rules
|
|
252
|
+
6. Write tests in the matching `tests/` directory
|
|
253
|
+
7. Run `pytest` -- all tests must pass, coverage must stay above 95%
|
|
254
|
+
8. Run `ruff check` -- zero lint errors
|
|
255
|
+
|
|
256
|
+
After writing code:
|
|
257
|
+
9. Mark the task as `[x]` in `ROADMAP.md`
|
|
258
|
+
10. If you discovered new work needed, add it to the appropriate phase in `ROADMAP.md`
|
|
259
|
+
|
|
260
|
+
### 23. Commit Instructions
|
|
261
|
+
|
|
262
|
+
Follow Conventional Commits: `feat`, `fix`, `refactor`, `test`, `chore`, `docs`.
|
|
263
|
+
Scope is the primary module changed: `compiler`, `devserver`, `ssr`, `cli`, `runtime`,
|
|
264
|
+
`client`, `build`, `routing`, `tests`, `scaffold`.
|
|
265
|
+
|
|
266
|
+
### 24. Modifying the Compiler or Parser
|
|
267
|
+
|
|
268
|
+
The parser (`pyxle/compiler/parser.py`) is the most sensitive code. Changes here can
|
|
269
|
+
break every `.pyx` file in existence.
|
|
270
|
+
|
|
271
|
+
- **Always** add regression tests for the specific input pattern you're handling
|
|
272
|
+
- **Never** remove an existing test
|
|
273
|
+
- **Test edge cases**: empty files, files with only Python, files with only JSX,
|
|
274
|
+
multiline strings, nested brackets, decorator chains, comments that look like code
|
|
275
|
+
- After changes, run the full compiler test suite AND manually compile the scaffold
|
|
276
|
+
templates to verify they still work
|
|
277
|
+
|
|
278
|
+
### 25. Modifying the SSR Pipeline
|
|
279
|
+
|
|
280
|
+
Changes to `pyxle/ssr/` affect every page render.
|
|
281
|
+
|
|
282
|
+
- **Benchmark** before and after: measure render time for simple and complex pages
|
|
283
|
+
- **Test error paths**: loader failure, render failure, head evaluation failure
|
|
284
|
+
- **Test with missing data**: what happens when a loader returns `None`? Empty dict?
|
|
285
|
+
- **Verify the error overlay** still receives correct breadcrumbs after your change
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## Testing Patterns
|
|
290
|
+
|
|
291
|
+
### 26. Use Fixtures, Not Setup Methods
|
|
292
|
+
|
|
293
|
+
### 27. Use Parametrize for Variant Testing
|
|
294
|
+
|
|
295
|
+
### 28. Mock External Dependencies
|
|
296
|
+
|
|
297
|
+
Node.js, npm, Vite, and file system operations should be mocked in unit tests.
|
|
298
|
+
Use `tmp_path` for any test that creates files. Never write to the real filesystem.
|
|
299
|
+
|
|
300
|
+
### 29. Test Error Messages, Not Just Error Types
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## DO NOT List
|
|
305
|
+
|
|
306
|
+
- **DO NOT** lower the coverage threshold below 95%
|
|
307
|
+
- **DO NOT** skip or delete tests to make the suite pass
|
|
308
|
+
- **DO NOT** add `print()` statements (use logger or `logging`)
|
|
309
|
+
- **DO NOT** introduce circular imports between modules
|
|
310
|
+
- **DO NOT** make `runtime.py` import anything from the framework
|
|
311
|
+
- **DO NOT** use mutable dataclasses for data-carrying types
|
|
312
|
+
- **DO NOT** block the async event loop with synchronous I/O
|
|
313
|
+
- **DO NOT** grow caches without eviction policies
|
|
314
|
+
- **DO NOT** use `shell=True` in subprocess calls
|
|
315
|
+
- **DO NOT** expose internal error details in production responses
|
|
316
|
+
- **DO NOT** hardcode host/port/path values (use config or env vars)
|
|
317
|
+
- **DO NOT** commit `.env` files, secrets, or credentials
|
|
318
|
+
- **DO NOT** add dependencies to `pyproject.toml` without explicit need and version pinning
|
|
319
|
+
- **DO NOT** break backward compatibility without updating `ROADMAP.md` migration notes
|
|
320
|
+
- **DO NOT** modify templates without verifying `pyxle init` + `pyxle dev` still works end-to-end
|
|
321
|
+
- **DO NOT** write framework code that only works on macOS/Linux -- support Windows paths
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## Quick Reference
|
|
326
|
+
|
|
327
|
+
### Running Tests
|
|
328
|
+
```bash
|
|
329
|
+
pytest # Full suite with coverage
|
|
330
|
+
pytest tests/compiler/ # Just compiler tests
|
|
331
|
+
pytest -x # Stop on first failure
|
|
332
|
+
pytest -k "test_parser" # Run tests matching pattern
|
|
333
|
+
pytest --no-cov # Skip coverage (faster for iteration)
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Linting
|
|
337
|
+
```bash
|
|
338
|
+
ruff check pyxle/ tests/
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### Key Paths
|
|
342
|
+
```
|
|
343
|
+
pyxle/ # Framework source
|
|
344
|
+
|-- cli/ # CLI commands (Typer)
|
|
345
|
+
|-- compiler/ # .pyx -> .py + .jsx compiler
|
|
346
|
+
| |-- parser.py # State-machine parser (most complex)
|
|
347
|
+
| |-- writers.py # Server/client code emission
|
|
348
|
+
| |-- model.py # Compilation data models
|
|
349
|
+
| |-- jsx_parser.py # Babel-based JSX validation
|
|
350
|
+
| +-- jsx_imports.py # Import specifier rewriter
|
|
351
|
+
|-- devserver/ # Development server
|
|
352
|
+
| |-- starlette_app.py # ASGI app assembly
|
|
353
|
+
| |-- vite.py # Vite subprocess management
|
|
354
|
+
| |-- builder.py # Incremental build orchestration
|
|
355
|
+
| |-- proxy.py # Vite asset proxy
|
|
356
|
+
| |-- routes.py # Route descriptors
|
|
357
|
+
| |-- registry.py # Page metadata registry
|
|
358
|
+
| |-- middleware.py # Middleware loading
|
|
359
|
+
| |-- layouts.py # Layout/template composition
|
|
360
|
+
| |-- scanner.py # Source file discovery
|
|
361
|
+
| +-- overlay.py # WebSocket error overlay
|
|
362
|
+
|-- ssr/ # Server-side rendering
|
|
363
|
+
| |-- renderer.py # Component render orchestration
|
|
364
|
+
| |-- render_component.mjs # Node.js SSR runtime (esbuild + React)
|
|
365
|
+
| |-- view.py # Page response building
|
|
366
|
+
| |-- head_merger.py # Head element deduplication
|
|
367
|
+
| +-- template.py # HTML document assembly
|
|
368
|
+
|-- build/ # Production build pipeline
|
|
369
|
+
|-- routing/ # File-based route calculation
|
|
370
|
+
|-- client/ # Client-side JS components (not Python)
|
|
371
|
+
|-- config.py # Configuration parsing + validation
|
|
372
|
+
|-- runtime.py # @server, @action decorators (zero deps)
|
|
373
|
+
+-- templates/scaffold/ # pyxle init project template
|
|
374
|
+
|
|
375
|
+
tests/ # Mirrors pyxle/ structure
|
|
376
|
+
docs/ # Framework documentation
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### Design Principles (from ROADMAP.md)
|
|
380
|
+
1. **Python-first, not Python-only** -- great Python AND great React
|
|
381
|
+
2. **Convention over configuration** -- zero config for common cases
|
|
382
|
+
3. **Compiler-driven** -- extract metadata at build time, not runtime
|
|
383
|
+
4. **No magic** -- decorators add metadata, not hidden behavior
|
|
384
|
+
5. **Progressive disclosure** -- simple things simple, complex things possible
|
|
385
|
+
6. **Batteries includable** -- ship hooks and integration points, not opinions
|
|
386
|
+
7. **AI-first DX** -- predictable patterns, strong types, clear errors
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
.PHONY: install test lint clean
|
|
2
|
+
|
|
3
|
+
install:
|
|
4
|
+
pip install -e .[dev]
|
|
5
|
+
|
|
6
|
+
test:
|
|
7
|
+
pytest
|
|
8
|
+
|
|
9
|
+
lint:
|
|
10
|
+
ruff check pyxle/ tests/
|
|
11
|
+
|
|
12
|
+
clean:
|
|
13
|
+
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
|
|
14
|
+
find . -type d -name .pytest_cache -exec rm -rf {} + 2>/dev/null || true
|
|
15
|
+
rm -rf .coverage htmlcov/ dist/ *.egg-info/
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyxle-framework
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python-first full-stack framework with an intelligent CLI.
|
|
5
|
+
Project-URL: Homepage, https://pyxle.dev
|
|
6
|
+
Project-URL: Repository, https://github.com/shivamsn97/pyxle
|
|
7
|
+
Project-URL: Documentation, https://github.com/shivamsn97/pyxle/tree/main/docs
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/shivamsn97/pyxle/issues
|
|
9
|
+
Project-URL: PyPI, https://pypi.org/project/pyxle-framework/
|
|
10
|
+
Author-email: Shivam Saini <hello@pyxle.dev>
|
|
11
|
+
License: MIT
|
|
12
|
+
Keywords: full-stack,python,react,ssr,web framework
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Framework :: AsyncIO
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Requires-Dist: httpx<0.28,>=0.27
|
|
27
|
+
Requires-Dist: starlette<0.38,>=0.37
|
|
28
|
+
Requires-Dist: typer>=0.12
|
|
29
|
+
Requires-Dist: uvicorn[standard]<1.0,>=0.29
|
|
30
|
+
Requires-Dist: watchdog<5.0,>=4.0
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: coverage<8.0,>=7.4; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest-cov<5.0,>=4.1; extra == 'dev'
|
|
34
|
+
Requires-Dist: pytest<9.0,>=8.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: ruff<0.7,>=0.6; extra == 'dev'
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
<p align="center">
|
|
39
|
+
<br />
|
|
40
|
+
<a href="https://pyxle.dev">
|
|
41
|
+
<img src=".github/pyxle-logo.svg" alt="Pyxle" height="52" />
|
|
42
|
+
</a>
|
|
43
|
+
<br />
|
|
44
|
+
<br />
|
|
45
|
+
<strong>The Python full-stack framework.</strong>
|
|
46
|
+
<br />
|
|
47
|
+
Server logic in Python. UI in React. One file.
|
|
48
|
+
<br />
|
|
49
|
+
<br />
|
|
50
|
+
<a href="https://pypi.org/project/pyxle-framework/"><img src="https://img.shields.io/pypi/v/pyxle-framework?color=22c55e&labelColor=0a0a0b&label=pypi" alt="PyPI" /></a>
|
|
51
|
+
|
|
52
|
+
<a href="https://pyxle.dev"><img src="https://img.shields.io/badge/pyxle.dev-0a0a0b?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDE2IDE2Ij48Y2lyY2xlIGN4PSI4IiBjeT0iOCIgcj0iNiIgZmlsbD0iIzIyYzU1ZSIvPjwvc3ZnPg==&label=" alt="Website" /></a>
|
|
53
|
+
</p>
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
# pages/index.pyx
|
|
59
|
+
|
|
60
|
+
@server
|
|
61
|
+
async def load(request):
|
|
62
|
+
return {"message": "Hello from Python"}
|
|
63
|
+
|
|
64
|
+
# --- JSX ---
|
|
65
|
+
export default function Home({ data }) {
|
|
66
|
+
return <h1>{data.message}</h1>;
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Pyxle compiles `.pyx` files into Python server modules and React client components.
|
|
71
|
+
`@server` loaders run on the backend, SSR renders the HTML, React hydrates on the client.
|
|
72
|
+
|
|
73
|
+
## Get started
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pip install pyxle-framework
|
|
77
|
+
pyxle init my-app && cd my-app
|
|
78
|
+
pyxle install
|
|
79
|
+
pyxle dev
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Open **http://localhost:8000**.
|
|
83
|
+
|
|
84
|
+
## Features
|
|
85
|
+
|
|
86
|
+
**`.pyx` files** -- Python + React in a single file, split at compile time
|
|
87
|
+
**File-based routing** -- `pages/` maps to URLs, dynamic segments with `[param].pyx`
|
|
88
|
+
**SSR** -- Server-side rendering via esbuild + React 18
|
|
89
|
+
**`@server` / `@action`** -- Typed data loading and form mutations
|
|
90
|
+
**Layouts** -- Nested layouts and templates with slot composition
|
|
91
|
+
**Vite HMR** -- Fast refresh in development
|
|
92
|
+
**Tailwind** -- Pre-configured out of the box
|
|
93
|
+
**Production build** -- `pyxle build` + `pyxle serve` for deployment
|
|
94
|
+
|
|
95
|
+
## Documentation
|
|
96
|
+
|
|
97
|
+
Full docs are in the [`docs/`](docs/README.md) directory:
|
|
98
|
+
|
|
99
|
+
- [Quick Start](docs/getting-started/quick-start.md)
|
|
100
|
+
- [`.pyx` Files](docs/core-concepts/pyx-files.md)
|
|
101
|
+
- [Routing](docs/core-concepts/routing.md)
|
|
102
|
+
- [Data Loading](docs/core-concepts/data-loading.md)
|
|
103
|
+
- [Server Actions](docs/core-concepts/server-actions.md)
|
|
104
|
+
- [Layouts](docs/core-concepts/layouts.md)
|
|
105
|
+
- [Deployment](docs/guides/deployment.md)
|
|
106
|
+
- [CLI Reference](docs/reference/cli.md)
|
|
107
|
+
- [Configuration](docs/reference/configuration.md)
|
|
108
|
+
|
|
109
|
+
## CLI
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
pyxle init <name> Scaffold a new project
|
|
113
|
+
pyxle install Install Python + Node dependencies
|
|
114
|
+
pyxle dev Development server with HMR
|
|
115
|
+
pyxle build Production build
|
|
116
|
+
pyxle serve Serve the production build
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Requirements
|
|
120
|
+
|
|
121
|
+
Python 3.10+ and Node.js 18+.
|
|
122
|
+
|
|
123
|
+
## Contributing
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
git clone https://github.com/shivamsn97/pyxle.git
|
|
127
|
+
cd pyxle
|
|
128
|
+
pip install -e ".[dev]"
|
|
129
|
+
pytest
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Links
|
|
133
|
+
|
|
134
|
+
- [pyxle.dev](https://pyxle.dev)
|
|
135
|
+
- [PyPI](https://pypi.org/project/pyxle-framework/)
|
|
136
|
+
- [Issues](https://github.com/shivamsn97/pyxle/issues)
|