snackapp 0.1.1__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.
- snackapp-0.1.1/.github/workflows/publish.yml +22 -0
- snackapp-0.1.1/.github/workflows/test.yml +74 -0
- snackapp-0.1.1/.gitignore +17 -0
- snackapp-0.1.1/.python-version +1 -0
- snackapp-0.1.1/Dockerfile +6 -0
- snackapp-0.1.1/Makefile +11 -0
- snackapp-0.1.1/PKG-INFO +60 -0
- snackapp-0.1.1/README.md +46 -0
- snackapp-0.1.1/docs/DEPLOY.md +46 -0
- snackapp-0.1.1/docs/quickstart.png +0 -0
- snackapp-0.1.1/pyproject.toml +79 -0
- snackapp-0.1.1/src/snackapp/__init__.py +26 -0
- snackapp-0.1.1/src/snackapp/__main__.py +6 -0
- snackapp-0.1.1/src/snackapp/app.py +494 -0
- snackapp-0.1.1/src/snackapp/cli.py +177 -0
- snackapp-0.1.1/src/snackapp/config.py +76 -0
- snackapp-0.1.1/src/snackapp/discovery.py +162 -0
- snackapp-0.1.1/src/snackapp/exceptions.py +24 -0
- snackapp-0.1.1/src/snackapp/fields/__init__.py +15 -0
- snackapp-0.1.1/src/snackapp/fields/filters.py +126 -0
- snackapp-0.1.1/src/snackapp/fields/registry.py +236 -0
- snackapp-0.1.1/src/snackapp/fields/validate.py +50 -0
- snackapp-0.1.1/src/snackapp/provision.py +123 -0
- snackapp-0.1.1/src/snackapp/py.typed +0 -0
- snackapp-0.1.1/src/snackapp/schema.py +286 -0
- snackapp-0.1.1/src/snackapp/schema_sync.py +96 -0
- snackapp-0.1.1/src/snackapp/static/.gitkeep +0 -0
- snackapp-0.1.1/src/snackapp/static/assets/index-DiBbpiCA.css +1 -0
- snackapp-0.1.1/src/snackapp/static/assets/index-DoUWBAjL.js +40 -0
- snackapp-0.1.1/src/snackapp/static/index.html +13 -0
- snackapp-0.1.1/src/snackapp/ui.py +271 -0
- snackapp-0.1.1/uv.lock +2083 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Publish snackapp to PyPI via trusted publishing (OIDC). No API token.
|
|
2
|
+
# Publish snackbase first. Configure the PyPI trusted publisher for this
|
|
3
|
+
# workflow before pushing a v* tag.
|
|
4
|
+
name: publish
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
tags: ["v*"]
|
|
8
|
+
jobs:
|
|
9
|
+
pypi:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
environment: pypi
|
|
12
|
+
permissions:
|
|
13
|
+
id-token: write
|
|
14
|
+
contents: read
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: astral-sh/setup-uv@v4
|
|
18
|
+
- run: uv build
|
|
19
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
20
|
+
with:
|
|
21
|
+
packages-dir: dist
|
|
22
|
+
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
on:
|
|
3
|
+
pull_request:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
jobs:
|
|
7
|
+
test:
|
|
8
|
+
runs-on: ${{ matrix.os }}
|
|
9
|
+
strategy:
|
|
10
|
+
fail-fast: false
|
|
11
|
+
matrix:
|
|
12
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
13
|
+
python-version: ["3.12", "3.13"]
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
with:
|
|
17
|
+
path: snackapp
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
with:
|
|
20
|
+
repository: lalitgehani/SnackBase
|
|
21
|
+
path: SnackBase
|
|
22
|
+
- uses: astral-sh/setup-uv@v4
|
|
23
|
+
with:
|
|
24
|
+
python-version: ${{ matrix.python-version }}
|
|
25
|
+
- name: Test
|
|
26
|
+
working-directory: snackapp
|
|
27
|
+
run: |
|
|
28
|
+
uv sync --dev
|
|
29
|
+
uv run ruff check .
|
|
30
|
+
uv run pytest --cov --cov-fail-under=80
|
|
31
|
+
uv build
|
|
32
|
+
- name: Build snackbase wheel
|
|
33
|
+
working-directory: SnackBase
|
|
34
|
+
run: uv build
|
|
35
|
+
- name: pip-install wheels without uv
|
|
36
|
+
working-directory: snackapp
|
|
37
|
+
shell: bash
|
|
38
|
+
run: |
|
|
39
|
+
python -m venv .ci-venv
|
|
40
|
+
if [ -x .ci-venv/bin/python ]; then
|
|
41
|
+
PY=.ci-venv/bin/python
|
|
42
|
+
else
|
|
43
|
+
PY=.ci-venv/Scripts/python
|
|
44
|
+
fi
|
|
45
|
+
"$PY" -m pip install ../SnackBase/dist/snackbase-*.whl dist/snackapp-*.whl
|
|
46
|
+
"$PY" -m snackapp --help
|
|
47
|
+
"$PY" -m snackapp --version
|
|
48
|
+
|
|
49
|
+
install-testpypi:
|
|
50
|
+
runs-on: ${{ matrix.os }}
|
|
51
|
+
strategy:
|
|
52
|
+
fail-fast: false
|
|
53
|
+
matrix:
|
|
54
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
55
|
+
python-version: ["3.12", "3.13"]
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/setup-python@v5
|
|
58
|
+
with:
|
|
59
|
+
python-version: ${{ matrix.python-version }}
|
|
60
|
+
- name: pip install snackapp from TestPyPI
|
|
61
|
+
shell: bash
|
|
62
|
+
run: |
|
|
63
|
+
python -m venv .ci-venv
|
|
64
|
+
if [ -x .ci-venv/bin/python ]; then
|
|
65
|
+
PY=.ci-venv/bin/python
|
|
66
|
+
else
|
|
67
|
+
PY=.ci-venv/Scripts/python
|
|
68
|
+
fi
|
|
69
|
+
"$PY" -m pip install -U pip
|
|
70
|
+
"$PY" -m pip install --only-binary=:all: \
|
|
71
|
+
--extra-index-url https://test.pypi.org/simple/ \
|
|
72
|
+
snackapp==0.1.1 snackbase==0.12.1
|
|
73
|
+
"$PY" -m snackapp --help
|
|
74
|
+
"$PY" -m snackapp --version
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
.snackapp/
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
.pytest_cache/
|
|
5
|
+
.mypy_cache/
|
|
6
|
+
.ruff_cache/
|
|
7
|
+
.coverage
|
|
8
|
+
htmlcov/
|
|
9
|
+
dist/
|
|
10
|
+
*.egg-info/
|
|
11
|
+
node_modules/
|
|
12
|
+
frontend/dist/
|
|
13
|
+
.DS_Store
|
|
14
|
+
.venv/
|
|
15
|
+
.pip-verify-venv/
|
|
16
|
+
.pip-verify-venv-*/
|
|
17
|
+
.pip-verify-demo/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.14
|
snackapp-0.1.1/Makefile
ADDED
snackapp-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: snackapp
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Python application framework with SnackBase embedded
|
|
5
|
+
License: MIT
|
|
6
|
+
Requires-Python: >=3.12
|
|
7
|
+
Requires-Dist: click>=8.1.0
|
|
8
|
+
Requires-Dist: fastapi>=0.127.0
|
|
9
|
+
Requires-Dist: httpx>=0.28.1
|
|
10
|
+
Requires-Dist: python-dotenv>=1.0.1
|
|
11
|
+
Requires-Dist: snackbase>=0.12.1
|
|
12
|
+
Requires-Dist: uvicorn[standard]>=0.50.0
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# SnackApp
|
|
16
|
+
|
|
17
|
+
A Python application framework with SnackBase embedded. One install, one command, a working multi-user app.
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
python -m pip install --only-binary=:all: \
|
|
21
|
+
--extra-index-url https://test.pypi.org/simple/ \
|
|
22
|
+
snackapp==0.1.1 snackbase==0.12.1
|
|
23
|
+
snackapp init demo
|
|
24
|
+
cd demo
|
|
25
|
+
snackapp run
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`--only-binary=:all:` keeps pip from building TestPyPI source distributions (TestPyPI hosts a broken FastAPI sdist). Dependencies that are not on TestPyPI still resolve from PyPI.
|
|
29
|
+
|
|
30
|
+
When `snackapp` is on [pypi.org](https://pypi.org/project/snackapp/), `pip install snackapp` is enough.
|
|
31
|
+
|
|
32
|
+
Open http://localhost:8000 and log in with the credentials printed on first boot.
|
|
33
|
+
|
|
34
|
+

|
|
35
|
+
|
|
36
|
+
See `examples/crm` for a Twenty-inspired CRM built only on the public API.
|
|
37
|
+
|
|
38
|
+
## Versioning
|
|
39
|
+
|
|
40
|
+
SnackApp is `0.1.1`. Before `1.0`, breaking changes may appear in minor releases. Pin `snackapp~=0.1` if you need a freeze.
|
|
41
|
+
|
|
42
|
+
## Publishing
|
|
43
|
+
|
|
44
|
+
`snackbase 0.12.1` and `snackapp 0.1.1` are on [TestPyPI](https://test.pypi.org/project/snackapp/). CI publishes to PyPI from a `v*` tag using [trusted publishing](https://docs.pypi.org/trusted-publishers/) (`id-token: write`, no API token in the repo). Publish `snackbase` first, then `snackapp`.
|
|
45
|
+
|
|
46
|
+
PyPI trusted publisher settings (once the production PyPI projects exist):
|
|
47
|
+
|
|
48
|
+
| Field | snackbase | snackapp |
|
|
49
|
+
| --- | --- | --- |
|
|
50
|
+
| Owner | `lalitgehani` | `lalitgehani` |
|
|
51
|
+
| Repository | `SnackBase` | `snackapp` |
|
|
52
|
+
| Workflow | `publish.yml` | `publish.yml` |
|
|
53
|
+
| Environment | `pypi` | `pypi` |
|
|
54
|
+
|
|
55
|
+
Do not push a `v*` tag until those publishers are saved on pypi.org.
|
|
56
|
+
|
|
57
|
+
## Docs
|
|
58
|
+
|
|
59
|
+
- Install, tutorial, `ui.*`, field types: `docs/` in this repo and [SnackApp in the SnackBase docs](https://docs.snackbase.dev/guides/snackapp)
|
|
60
|
+
- Deployment: [`docs/DEPLOY.md`](docs/DEPLOY.md)
|
snackapp-0.1.1/README.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# SnackApp
|
|
2
|
+
|
|
3
|
+
A Python application framework with SnackBase embedded. One install, one command, a working multi-user app.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
python -m pip install --only-binary=:all: \
|
|
7
|
+
--extra-index-url https://test.pypi.org/simple/ \
|
|
8
|
+
snackapp==0.1.1 snackbase==0.12.1
|
|
9
|
+
snackapp init demo
|
|
10
|
+
cd demo
|
|
11
|
+
snackapp run
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`--only-binary=:all:` keeps pip from building TestPyPI source distributions (TestPyPI hosts a broken FastAPI sdist). Dependencies that are not on TestPyPI still resolve from PyPI.
|
|
15
|
+
|
|
16
|
+
When `snackapp` is on [pypi.org](https://pypi.org/project/snackapp/), `pip install snackapp` is enough.
|
|
17
|
+
|
|
18
|
+
Open http://localhost:8000 and log in with the credentials printed on first boot.
|
|
19
|
+
|
|
20
|
+

|
|
21
|
+
|
|
22
|
+
See `examples/crm` for a Twenty-inspired CRM built only on the public API.
|
|
23
|
+
|
|
24
|
+
## Versioning
|
|
25
|
+
|
|
26
|
+
SnackApp is `0.1.1`. Before `1.0`, breaking changes may appear in minor releases. Pin `snackapp~=0.1` if you need a freeze.
|
|
27
|
+
|
|
28
|
+
## Publishing
|
|
29
|
+
|
|
30
|
+
`snackbase 0.12.1` and `snackapp 0.1.1` are on [TestPyPI](https://test.pypi.org/project/snackapp/). CI publishes to PyPI from a `v*` tag using [trusted publishing](https://docs.pypi.org/trusted-publishers/) (`id-token: write`, no API token in the repo). Publish `snackbase` first, then `snackapp`.
|
|
31
|
+
|
|
32
|
+
PyPI trusted publisher settings (once the production PyPI projects exist):
|
|
33
|
+
|
|
34
|
+
| Field | snackbase | snackapp |
|
|
35
|
+
| --- | --- | --- |
|
|
36
|
+
| Owner | `lalitgehani` | `lalitgehani` |
|
|
37
|
+
| Repository | `SnackBase` | `snackapp` |
|
|
38
|
+
| Workflow | `publish.yml` | `publish.yml` |
|
|
39
|
+
| Environment | `pypi` | `pypi` |
|
|
40
|
+
|
|
41
|
+
Do not push a `v*` tag until those publishers are saved on pypi.org.
|
|
42
|
+
|
|
43
|
+
## Docs
|
|
44
|
+
|
|
45
|
+
- Install, tutorial, `ui.*`, field types: `docs/` in this repo and [SnackApp in the SnackBase docs](https://docs.snackbase.dev/guides/snackapp)
|
|
46
|
+
- Deployment: [`docs/DEPLOY.md`](docs/DEPLOY.md)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Deploying SnackApp
|
|
2
|
+
|
|
3
|
+
## Install
|
|
4
|
+
|
|
5
|
+
Until the packages are on pypi.org, install the TestPyPI wheels and take
|
|
6
|
+
dependencies from PyPI:
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
python -m pip install --only-binary=:all: \
|
|
10
|
+
--extra-index-url https://test.pypi.org/simple/ \
|
|
11
|
+
snackapp==0.1.1 snackbase==0.12.1
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`--only-binary=:all:` is required: TestPyPI has a broken FastAPI sdist that
|
|
15
|
+
pip otherwise tries to build. After the production PyPI projects exist,
|
|
16
|
+
`pip install snackapp` is enough.
|
|
17
|
+
|
|
18
|
+
## Single process with SQLite
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
snackapp init myapp && cd myapp
|
|
22
|
+
snackapp run --host 0.0.0.0 --port 8000
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
## PostgreSQL
|
|
27
|
+
|
|
28
|
+
Set `SNACKBASE_DATABASE_URL=postgresql+asyncpg://user:pass@host/db` before
|
|
29
|
+
`snackapp run`. Keep `.snackapp/secret.key` mode 0600.
|
|
30
|
+
|
|
31
|
+
## Docker
|
|
32
|
+
|
|
33
|
+
See the repository `Dockerfile`. Build the wheel first (`uv build`), then
|
|
34
|
+
`docker build`.
|
|
35
|
+
|
|
36
|
+
## Trusted publishing
|
|
37
|
+
|
|
38
|
+
Neither repository stores a PyPI token. On tag `v*`, GitHub Actions uses
|
|
39
|
+
OIDC (`id-token: write`) with `pypa/gh-action-pypi-publish`.
|
|
40
|
+
|
|
41
|
+
Before the first tag, an owner must:
|
|
42
|
+
|
|
43
|
+
1. Create the PyPI projects `snackbase` then `snackapp` (or claim the names).
|
|
44
|
+
2. Add a trusted publisher: GitHub org/user, repository, workflow `publish.yml`,
|
|
45
|
+
environment `pypi`.
|
|
46
|
+
3. Tag and push `v0.12.1` on SnackBase, then `v0.1.1` on snackapp.
|
|
Binary file
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "snackapp"
|
|
3
|
+
version = "0.1.1"
|
|
4
|
+
description = "Python application framework with SnackBase embedded"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
license = { text = "MIT" }
|
|
8
|
+
dependencies = [
|
|
9
|
+
"snackbase>=0.12.1",
|
|
10
|
+
"fastapi>=0.127.0",
|
|
11
|
+
"uvicorn[standard]>=0.50.0",
|
|
12
|
+
"httpx>=0.28.1",
|
|
13
|
+
"click>=8.1.0",
|
|
14
|
+
"python-dotenv>=1.0.1",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.scripts]
|
|
18
|
+
snackapp = "snackapp.cli:main"
|
|
19
|
+
|
|
20
|
+
[build-system]
|
|
21
|
+
requires = ["hatchling"]
|
|
22
|
+
build-backend = "hatchling.build"
|
|
23
|
+
|
|
24
|
+
[tool.hatch.build.targets.wheel]
|
|
25
|
+
packages = ["src/snackapp"]
|
|
26
|
+
|
|
27
|
+
[tool.hatch.build.targets.sdist]
|
|
28
|
+
exclude = ["/tests", "/frontend", "/examples"]
|
|
29
|
+
|
|
30
|
+
[tool.uv.sources]
|
|
31
|
+
snackbase = { path = "../SnackBase", editable = true }
|
|
32
|
+
|
|
33
|
+
[dependency-groups]
|
|
34
|
+
dev = [
|
|
35
|
+
"pytest>=8.0.0",
|
|
36
|
+
"pytest-asyncio>=0.24.0",
|
|
37
|
+
"pytest-cov>=5.0.0",
|
|
38
|
+
"ruff>=0.8.0",
|
|
39
|
+
"mypy>=1.13.0",
|
|
40
|
+
"httpx>=0.28.0",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[tool.ruff]
|
|
44
|
+
target-version = "py314"
|
|
45
|
+
line-length = 100
|
|
46
|
+
src = ["src"]
|
|
47
|
+
|
|
48
|
+
[tool.ruff.lint]
|
|
49
|
+
select = ["E", "F", "I", "N", "W", "UP"]
|
|
50
|
+
ignore = ["E501"]
|
|
51
|
+
|
|
52
|
+
[tool.ruff.lint.isort]
|
|
53
|
+
known-first-party = ["snackapp"]
|
|
54
|
+
|
|
55
|
+
[tool.ruff.lint.per-file-ignores]
|
|
56
|
+
"examples/**/\\[id\\].py" = ["N999"]
|
|
57
|
+
|
|
58
|
+
[tool.mypy]
|
|
59
|
+
python_version = "3.12"
|
|
60
|
+
ignore_missing_imports = true
|
|
61
|
+
warn_unused_ignores = false
|
|
62
|
+
disallow_untyped_defs = false
|
|
63
|
+
check_untyped_defs = false
|
|
64
|
+
mypy_path = "src"
|
|
65
|
+
|
|
66
|
+
[tool.coverage.run]
|
|
67
|
+
source = ["src/snackapp"]
|
|
68
|
+
omit = ["src/snackapp/__main__.py"]
|
|
69
|
+
branch = false
|
|
70
|
+
|
|
71
|
+
[tool.pytest.ini_options]
|
|
72
|
+
asyncio_mode = "auto"
|
|
73
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
74
|
+
testpaths = ["tests"]
|
|
75
|
+
addopts = "--cov=snackapp --cov-report=term-missing"
|
|
76
|
+
filterwarnings = [
|
|
77
|
+
"ignore:Using `httpx` with `starlette.testclient` is deprecated.*",
|
|
78
|
+
"ignore:The anyio.abc.BlockingPortal alias is deprecated.*",
|
|
79
|
+
]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""SnackApp — Python application framework with SnackBase embedded."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
4
|
+
|
|
5
|
+
from snackapp import ui
|
|
6
|
+
from snackapp.app import App, Ctx, Table
|
|
7
|
+
from snackapp.discovery import action, page
|
|
8
|
+
from snackapp.schema import Field, Schema, rules_for
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
__version__ = version("snackapp")
|
|
12
|
+
except PackageNotFoundError:
|
|
13
|
+
__version__ = "0.1.1"
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"App",
|
|
17
|
+
"Ctx",
|
|
18
|
+
"Field",
|
|
19
|
+
"Schema",
|
|
20
|
+
"Table",
|
|
21
|
+
"action",
|
|
22
|
+
"page",
|
|
23
|
+
"rules_for",
|
|
24
|
+
"ui",
|
|
25
|
+
"__version__",
|
|
26
|
+
]
|