[build-system]
requires = ["setuptools>=68", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "pythonnative"
version = "0.48.0"
description = "Cross-platform native UI toolkit for Android and iOS"
authors = [
{ name = "Owen Carey" }
]
readme = "README.md"
# The host interpreter must match the embedded runtime (app.python_version)
# for device builds, and the embedded runtimes are 3.13 and 3.14: CPython
# gained official iOS support (PEP 730) and Android support (PEP 738) in
# 3.13, which is what makes binary wheels from PyPI installable at all.
requires-python = ">=3.13"
license = { file = "LICENSE" }
classifiers = [
"Development Status :: 2 - Pre-Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Topic :: Software Development :: User Interfaces",
]
dependencies = []
[project.optional-dependencies]
build = [
"Pillow>=10.0",
]
# Contributor tooling, not published as package extras (PEP 735). Install
# with `uv sync --group dev` or `--group docs`; `uv sync` alone gets `dev`.
[dependency-groups]
dev = [
"black>=24.0",
"ruff>=0.5",
"mypy>=1.10",
"pytest>=8.0",
"pytest-asyncio>=0.23",
"PyYAML>=6.0",
"Pillow>=10.0",
# `pn deps` and `pn run ios` shell out to `sys.executable -m pip` to
# resolve and vendor the app's [requirements].packages for the device
# platform tags (project/deps.py), and the test suite exercises that
# path. `uv sync` does not seed pip the way `uv venv --seed` did.
"pip>=24.0",
"websockets>=15",
]
docs = [
"mkdocs>=1.5",
"mkdocs-material[imaging]>=9.5",
"mkdocstrings[python]>=0.24",
"mkdocs-autorefs>=1.0",
"pymdown-extensions>=10.7",
]
[project.scripts]
pn = "pythonnative.cli.pn:main"
[project.entry-points.pytest11]
pythonnative = "pythonnative.testing.pytest_plugin"
[project.urls]
Homepage = "https://github.com/pythonnative/pythonnative"
Repository = "https://github.com/pythonnative/pythonnative"
Issues = "https://github.com/pythonnative/pythonnative/issues"
Documentation = "https://pythonnative.com/"
[tool.setuptools.packages.find]
where = ["src"]
[tool.setuptools]
license-files = ["LICENSE*"]
# Include template directories and the browser preview's page assets inside
# the package so importlib.resources can find them
[tool.setuptools.package-data]
pythonnative = [
"py.typed",
"templates/**", "native/**", "sdk/templates/**",
"devserver/static/**",
"icons/_lucide.json", "icons/LICENSE-lucide.txt",
]
[tool.setuptools.exclude-package-data]
pythonnative = [
"**/build/**", "**/.build/**", "**/.gradle/**",
"**/.cxx/**", "**/.swiftpm/**", "**/.kotlin/**",
"**/__pycache__/**", "**/.DS_Store",
]
[tool.ruff]
target-version = "py313"
line-length = 120
extend-exclude = [
"templates",
"docs",
]
[tool.ruff.lint]
# Docstring (D) rules use the Google convention; see [tool.ruff.lint.pydocstyle].
# Selectively enabled so tests/examples/templates aren't penalized.
select = ["E", "F", "I", "D"]
# D107 (missing __init__ docstring): mkdocstrings is configured to merge
# __init__ into the class docstring, so the class docstring is the source of truth.
# D105 (magic method docstring): most are self-explanatory (__repr__, __eq__, etc.).
# D203/D213: conflict with Google convention defaults; ruff auto-suppresses
# these via `convention = "google"`, but we list them defensively.
ignore = ["D107", "D105", "D203", "D213"]
[tool.ruff.lint.per-file-ignores]
"tests/**/*.py" = ["D"]
"examples/**/*.py" = ["D"]
"src/pythonnative/templates/**/*.py" = ["D"]
"setup.py" = ["D"]
"conftest.py" = ["D"]
# Platform handler subclasses implement the ViewHandler ABC defined in
# native_views/base.py, which carries the canonical docstrings for the
# protocol. The concrete classes are internal (registered by name in
# NativeViewRegistry, never imported by users), so requiring per-method
# docstrings would only add boilerplate that repeats the ABC.
"src/pythonnative/native_views/android.py" = ["D101", "D102"]
"src/pythonnative/native_views/ios.py" = ["D101", "D102"]
# ios.py embeds a LaunchScreen storyboard as an XML string literal whose
# lines exceed the limit; the XML must stay on single lines for Xcode.
"src/pythonnative/project/ios.py" = ["E501"]
[tool.ruff.lint.pydocstyle]
convention = "google"
[tool.black]
line-length = 120
target-version = ['py313']
# Semantic Release
[tool.semantic_release]
# PSR stamps the new version into pyproject.toml before running this, so
# `--upgrade-package` re-syncs only this project's own version in the lock
# (never dependency upgrades), and the staged uv.lock lands in the release
# commit. The release workflow builds distributions from that tagged commit
# using the same cibuildwheel matrix as PR CI.
build_command = """
uv lock --upgrade-package pythonnative
git add uv.lock
"""
version_toml = ["pyproject.toml:project.version"]
version_variables = ["src/pythonnative/__init__.py:__version__"]
commit_message = "chore(release): v{version}"
tag_format = "v{version}"
major_on_zero = false
[tool.semantic_release.branches.main]
match = "main"
prerelease = false
[tool.semantic_release.changelog]
changelog_file = "CHANGELOG.md"
exclude_commit_patterns = [
"^chore\\(release\\):",
]
[tool.semantic_release.commit_parser_options]
allowed_tags = [
"build", "chore", "ci", "docs", "feat", "fix",
"perf", "refactor", "revert", "style", "test",
]
minor_tags = ["feat"]
patch_tags = ["fix", "perf"]