FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

feat(project,cli)!: resolve PyPI deps per target, and tighten API (#73) · pythonnative/pythonnative@8cebee5 · GitHub

feat(project,cli)!: resolve PyPI deps per target, and tighten API (#73) · pythonnative/pythonnative@8cebee5 · GitHub
Skip to content

Navigation Menu

Commit 8cebee5

Browse files
authored
feat(project,cli)!: resolve PyPI deps per target, and tighten API (#73)
1 parent 3780b32 commit 8cebee5

106 files changed

Lines changed: 3766 additions & 1183 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/ci.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
runs-on: ubuntu-latest
1212
strategy:
1313
matrix:
14-
python-version: ['3.10', '3.11', '3.12']
14+
python-version: ['3.13', '3.14']
1515

1616
steps:
1717
- name: Checkout

‎.github/workflows/docs.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
- uses: astral-sh/setup-uv@v10.0.1
1818
with:
1919
enable-cache: true
20-
python-version: '3.11'
20+
python-version: '3.13'
2121
# `uv sync` installs the project itself, which mkdocstrings needs in
2222
# order to import and introspect it.
2323
- name: Install docs deps

‎.github/workflows/e2e.yml‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
- name: Set up Python
1919
uses: actions/setup-python@v5
2020
with:
21-
python-version: '3.11'
21+
python-version: '3.13'
2222
- name: Check E2E coverage of pythonnative.__all__
2323
run: python scripts/check-e2e-coverage.py
2424

@@ -64,14 +64,21 @@ jobs:
6464
uses: astral-sh/setup-uv@v10.0.1
6565
with:
6666
enable-cache: true
67-
python-version: '3.11'
67+
python-version: '3.13'
6868

6969
- name: Set up Java 17
7070
uses: actions/setup-java@v4
7171
with:
7272
distribution: 'temurin'
7373
java-version: '17'
7474

75+
# Restores ~/.gradle (wrapper distribution and dependency caches)
76+
# so the staged project's ./gradlew doesn't download Gradle from
77+
# services.gradle.org on every shard; a reset connection there
78+
# fails the build before Maestro even starts.
79+
- name: Set up Gradle
80+
uses: gradle/actions/setup-gradle@v4
81+
7582
- name: Install PythonNative
7683
run: uv sync --locked
7784

@@ -168,7 +175,7 @@ jobs:
168175
uses: astral-sh/setup-uv@v10.0.1
169176
with:
170177
enable-cache: true
171-
python-version: '3.11'
178+
python-version: '3.13'
172179

173180
- name: Install PythonNative
174181
run: uv sync --locked

‎.github/workflows/packages.yml‎

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
name: Packages
2+
3+
# PyPI package compatibility matrix. Resolves tests/packages/matrix.toml
4+
# for every device target against the live indexes (PyPI, BeeWare,
5+
# Chaquopy) and fails when a package's availability disagrees with the
6+
# manifest, so an index change that breaks or fixes a package shows up
7+
# here rather than in a user's build.
8+
#
9+
# Runs weekly (the indexes change without us), on manual dispatch, and
10+
# on any change to the resolver, the manifest, or the guide's table.
11+
12+
on:
13+
schedule:
14+
- cron: '17 6 * * 1'
15+
workflow_dispatch:
16+
push:
17+
branches: [main]
18+
paths:
19+
- 'src/pythonnative/project/deps.py'
20+
- 'tests/packages/**'
21+
- 'scripts/package-matrix.py'
22+
- '.github/workflows/packages.yml'
23+
pull_request:
24+
paths:
25+
- 'src/pythonnative/project/deps.py'
26+
- 'tests/packages/**'
27+
- 'scripts/package-matrix.py'
28+
- '.github/workflows/packages.yml'
29+
30+
jobs:
31+
matrix:
32+
runs-on: ubuntu-latest
33+
timeout-minutes: 20
34+
steps:
35+
- name: Checkout
36+
uses: actions/checkout@v4
37+
38+
- name: Set up uv
39+
uses: astral-sh/setup-uv@v10.0.1
40+
with:
41+
enable-cache: true
42+
python-version: '3.13'
43+
44+
- name: Install dependencies
45+
run: uv sync --locked --group dev
46+
47+
- name: Resolve the matrix
48+
env:
49+
PN_PACKAGE_MATRIX: '1'
50+
run: uv run pytest tests/packages -q -m network
51+
52+
# The Markdown table the guide embeds; download it from the run
53+
# page and paste it into docs/guides/pypi-packages.md when it
54+
# changes.
55+
- name: Render the Markdown table
56+
if: always()
57+
run: uv run scripts/package-matrix.py > package-matrix.md
58+
59+
- name: Upload the table
60+
if: always()
61+
uses: actions/upload-artifact@v4
62+
with:
63+
name: package-matrix
64+
path: package-matrix.md
65+
retention-days: 30

‎.github/workflows/release.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
uses: astral-sh/setup-uv@v10.0.1
2727
with:
2828
enable-cache: true
29-
python-version: '3.12'
29+
python-version: '3.13'
3030

3131
# TEMPORARY: inlined replacement for the
3232
# python-semantic-release/python-semantic-release@v9 action. That

‎CONTRIBUTING.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Thanks for your interest in contributing. This repository contains the PythonNat
44

55
## Quick start
66

7-
Development uses Python ≥ 3.10 and [uv](https://docs.astral.sh/uv/) as the
7+
Development uses Python ≥ 3.13 (the same versions the apps embed) and [uv](https://docs.astral.sh/uv/) as the
88
only prerequisite. `uv sync` creates and updates `.venv` itself, so there's no
99
virtual environment to make and nothing to activate.
1010

@@ -370,6 +370,7 @@ When you add a new public symbol you must also:
370370

371371
- **CI** (`ci.yml`): runs formatter, linter, type checker, and tests on every push and PR.
372372
- **E2E** (`e2e.yml`): builds the hello-world example on Android (Linux emulator) and iOS (macOS simulator), then runs Maestro flows. Triggers on pushes to `main`, PRs, and manual dispatch.
373+
- **Packages** (`packages.yml`): resolves the PyPI compatibility matrix in `tests/packages/matrix.toml` against the live indexes with `scripts/package-matrix.py --check`, weekly and on changes to the resolver or manifest, and uploads the rendered Markdown table for `docs/guides/pypi-packages.md`.
373374
- **PR Lint** (`pr-lint.yml`): validates the PR title against Conventional Commits format (protects squash merges) and checks individual commit messages via commitlint (protects rebase merges). Recommended: add the **PR title** job as a required status check in branch-protection settings.
374375
- **Release** (`release.yml`): runs on merge to `main`; computes version, generates changelog, tags, creates GitHub Release, and (when `DRAFT_RELEASE` is `"false"`) publishes to PyPI.
375376
- **Docs** (`docs.yml`): builds the MkDocs site in strict mode on every push and pull request, and deploys to GitHub Pages on push to `main`.

‎README.md‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ PythonNative is a cross-platform toolkit for building native Android and iOS app
3636
- **Hooks and function components:** Manage state with `use_state`, side effects with `use_effect` / `use_layout_effect`, refs and imperative handles with `use_ref` / `use_imperative_handle`, and navigation with `use_navigation`, all through one consistent pattern. Components can return a single element, a list of siblings, or `None`, and `Fragment`, `Portal`, and reactive `Provider` context work the way they do in React.
3737
- **Async-first rendering:** One `asyncio` event loop runs the whole framework on the platform's main thread. Components can be `async def` and `await` data directly in the body; `Suspense` boundaries show declarative loading states while they wait. `use_resource` fetches during render, `use_effect` accepts coroutine callbacks (cancelled automatically on unmount), `lazy` code-splits components, and `use_transition` / `use_deferred_value` keep the UI responsive during expensive updates.
3838
- **Developer feedback that finds your bugs:** In dev mode (`pn preview`, hot reload, or `PN_DEV=1`), uncaught errors from renders, effects, and event handlers show a full-screen RedBox with the traceback; unknown style keys and duplicate list keys print "did you mean" warnings; and conditional hooks raise a `HookOrderError` at the source instead of silently cross-wiring state.
39-
- **Typed `style` prop:** Pass all visual and layout properties through a single `style` dict, fully described by the `pn.Style` `TypedDict` and the ergonomic `pn.style(...)` helper for IDE autocomplete and static checking. Compose reusable styles with `StyleSheet`.
39+
- **Typed `style` prop and theme:** Pass all visual and layout properties through a single `style` dict, fully described by the `pn.Style` `TypedDict` and the ergonomic `pn.style(...)` helper for IDE autocomplete and static checking. Compose reusable styles with `StyleSheet`, and read design tokens from a typed, immutable `pn.Theme` via `use_theme()` that follows light and dark mode by default.
4040
- **Cross-platform flexbox engine:** A pure-Python, Yoga-style layout engine computes frames once and applies them to native views, so `flex`, `padding`, `aspect_ratio`, and `position: "absolute"` produce the same geometry on Android and iOS.
4141
- **Virtual view tree + reconciler:** Element trees are diffed and patched with minimal native mutations, similar to React's reconciliation. Each commit lands as **one batched transaction** of mutation ops, and event callbacks are routed through a tag-based registry so re-renders that only change closures cost zero native calls. State updates re-render **locally**: only the component whose state changed (and its subtree) re-runs, and unchanged leaves reuse cached intrinsic measurements, so deep UIs stay responsive instead of re-rendering the whole app from the root on every tap.
4242
- **Native-driven animations:** The `Animated` API (timing / spring / decay / loop / stagger, awaitable or fire-and-forget) hands animations to Core Animation and `ViewPropertyAnimator` whenever possible, so no Python code runs per frame; a pure-Python ticker covers the rest. `interpolate`, arithmetic operators on animated nodes, `Animated.event` scroll binding, and `diff_clamp` cover the scroll-driven patterns (collapsing headers, parallax) that define native feel.
@@ -45,6 +45,7 @@ PythonNative is a cross-platform toolkit for building native Android and iOS app
4545
- **Native rendering core:** each commit is one serialized transaction applied by Swift and Kotlin component managers, the same shape as React Native's Fabric. Device APIs are native modules registered by name (TurboModules-style), callable from thin Python facades.
4646
- **Custom-component SDK:** Wrap any platform widget as a first-class element with type-checked props via `pythonnative.sdk` (`Props`, `@native_component`, `element_factory`). Plugins distributed on PyPI auto-register through the `pythonnative.handlers` entry-point group.
4747
- **CLI scaffolding:** `pn init` creates a ready-to-run project; `pn run android` and `pn run ios` build and launch your app.
48+
- **PyPI packages, binary wheels included:** List requirements in `pythonnative.toml` and the CLI resolves them for the *device* (iOS wheels via PEP 730 and BeeWare's index, Android wheels via PEP 738 and Chaquopy), bundling numpy, Pillow, cryptography, and friends alongside pure-Python packages. `pn deps` reports what each target resolves to before you build, and a weekly-checked compatibility matrix keeps the docs honest.
4849
- **Instant desktop preview:** `pn preview` renders your app in a native desktop window via Tkinter with Fast Refresh on every save: iterate on layout, state, and navigation in milliseconds without booting a simulator or device. The reconciler, hooks, layout engine, and navigation are the same code that ships to the phone.
4950
- **Native-backed navigation:** Declarative `Stack`, `Tab`, and `Drawer` navigators inspired by React Navigation. The root stack drives the platform's native navigation controller (`UINavigationController` on iOS, AndroidX Navigation Component on Android), so transitions, back gestures, and the hardware back button match what users expect; `use_back_handler` intercepts the back action when a screen needs to.
5051
- **Fast Refresh hot reload:** `pn run --hot-reload` watches `app/` and patches edits into the running app on save, preserving component state across most changes.
@@ -54,6 +55,8 @@ PythonNative is a cross-platform toolkit for building native Android and iOS app
5455

5556
### Installation
5657

58+
Requires Python 3.13 or newer.
59+
5760
```bash
5861
pip install pythonnative
5962
```

‎docs/api/appearance.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def SchemeAwareBadge():
1616
theme = pn.use_theme() # built-in theme for the scheme
1717
return pn.Text(
1818
f"Currently {scheme}",
19-
style={"color": theme["text_color"]},
19+
style={"color": theme.text_color},
2020
)
2121
```
2222

‎docs/api/cli.md‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,16 @@ the documented behavior never drifts from the code.
1313
Flag: `--force` to overwrite existing files or scaffold into a
1414
non-empty directory. See [Configuration](../guides/configuration.md).
1515
- `pn doctor [android|ios]`: diagnose the local toolchain and validate
16-
`pythonnative.toml`. Exits non-zero when something will block a build.
16+
`pythonnative.toml`, including that a `python3.X` matching
17+
`[app].python_version` is available for package resolution. Exits
18+
non-zero when something will block a build.
19+
- `pn deps [android|ios]`: resolve `[requirements].packages` for every
20+
device target (iOS device, iOS Simulator, and each Android ABI)
21+
without installing anything, and report the wheel each package would
22+
use, flagging binary wheels, their index, and downgrades. Flags:
23+
`--json` for a machine-readable report, `--python` to pick the
24+
interpreter that runs pip. Exits non-zero when any target can't be
25+
satisfied. See [PyPI packages](../guides/pypi-packages.md).
1726
- `pn preview [component]`: render the app in a desktop (Tkinter) window
1827
with Fast Refresh, the fastest way to iterate on UI. Flags:
1928
`--width`, `--height`, `--title`, `--no-hot-reload`. See the

‎docs/api/component-properties.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ pn.ScrollView(
185185
- `content_container_style`: style for the inner content wrapper (padding,
186186
alignment, spacing), distinct from `style` (the scroll frame)
187187
- `keyboard_dismiss_mode`: `"none"`, `"on_drag"`, or `"interactive"`
188-
- `refresh_control`: pull-to-refresh spec (see [`RefreshControl`](#refreshcontrol))
188+
- `refresh_control`: pull-to-refresh element (see [`RefreshControl`](#refreshcontrol))
189189

190190
## TextInput
191191

@@ -429,7 +429,7 @@ pn.ScrollView(child, refresh_control=pn.RefreshControl(
429429
refreshing=loading, on_refresh=reload, tint_color="#007AFF"))
430430
```
431431

432-
Pull-to-refresh spec (a plain dict) passed to a `ScrollView` or `FlatList`.
432+
Pull-to-refresh control, an element of type `"RefreshControl"`, passed as the `refresh_control=` prop of a `ScrollView`, `FlatList`, or `SectionList`. The scroll container attaches it to its native scroll view rather than rendering it as a child, and raises `TypeError` for anything that isn't a `RefreshControl`.
433433

434434
- `refreshing`: drive the spinner from state
435435
- `on_refresh`: callback `() -> None` when pulled past threshold
@@ -487,4 +487,4 @@ pn.FlatList(data=items, render_item=render_fn, key_extractor=key_fn,
487487
- `on_end_reached`: callback `() -> None` near the end (virtualized)
488488
- `on_end_reached_threshold`: fraction-of-viewport trigger distance
489489
- `content_container_style`: style for the inner content wrapper
490-
- `refresh_control`: pull-to-refresh spec (see [`RefreshControl`](#refreshcontrol))
490+
- `refresh_control`: pull-to-refresh element (see [`RefreshControl`](#refreshcontrol))

0 commit comments

Comments
 (0)

Footer

© 2026 GitHub, Inc.

Back | FazBrowse Home | New Git URL