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.
@@ -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
@@ -0,0 +1,6 @@
1
+ FROM python:3.12-slim
2
+ WORKDIR /app
3
+ COPY dist/*.whl /tmp/
4
+ RUN pip install --no-cache-dir /tmp/*.whl
5
+ EXPOSE 8000
6
+ CMD ["snackapp", "run", "--host", "0.0.0.0", "--port", "8000"]
@@ -0,0 +1,11 @@
1
+ .PHONY: build-frontend test
2
+
3
+ build-frontend:
4
+ rm -rf src/snackapp/static/assets
5
+ mkdir -p src/snackapp/static/assets
6
+ cd frontend && npm ci && npm run build
7
+
8
+ test:
9
+ uv run ruff check .
10
+ uv run mypy src/
11
+ uv run pytest --cov
@@ -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
+ ![SnackApp home after first login](docs/quickstart.png)
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)
@@ -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
+ ![SnackApp home after first login](docs/quickstart.png)
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
+ ]
@@ -0,0 +1,6 @@
1
+ """`python -m snackapp` is equivalent to the `snackapp` console script."""
2
+
3
+ from snackapp.cli import main
4
+
5
+ if __name__ == "__main__":
6
+ main()