--- applyTo: "**/*.py" --- # Python Development Guide - Supported Python versions: 3.10 - 3.14 - Docstrings: Numpy style - Linter: Ruff 0.x (config in root `pyproject.toml`) - Formatter: Ruff 0.x (config in root `pyproject.toml`) - Type Checker: mypy 2.x + ty 0.x (config in root `pyproject.toml`) - Testing: pytest 9.x (config in root `pyproject.toml`) ## Key Principles - PEP 8 Compliance: Adhere to PEP 8 guidelines for code style, with Ruff as the primary linter and formatter. - Elegance and Readability: Strive for elegant and Pythonic code that is easy to understand and maintain. - Zen of Python: Keep the Zen of Python in mind when making design decisions. - Avoid inheritance (prefer composition). - Avoid methods (prefer non-class functions, or static). - Name functions and variables in such a way that you don't need comments to explain the code. - Python folder and filenames should all be snake_cased regardless of what they contain. - Prefer importing entire modules instead of single functions: `from streamlit import mymodule` over `from streamlit.mymodule import internal_function` - Prefer keyword arguments, use positional values only for required values that frame the API. Enhancing arguments should be keyword-only. - Capitalize comments, use proper grammar and punctuation, and no cursing. - Inside a module, anything that is declared at the root level MUST be prefixed with a _ if it's only used inside that module (anything private). - Prioritize new features in Python 3.10+. ## Docstrings - Use Numpydoc style. - Docstrings are meant for users of a function, not developers who may edit the internals of that function in the future. If you want to talk to future developers, use comments. - All modules that we expect users to interact with must have top-level docstrings. If a user is not meant to interact with a module, docstrings are optional. ## Package Structure - `streamlit/`: The main Streamlit library package. - `streamlit/elements`: Backend code of elements and widgets. - `streamlit/runtime`: App runtime and execution logic. - `streamlit/web`: Web server and CLI implementation - `streamlit/commands`: `st` commands that don't add UI elements. - `streamlit/components`: Backend-implementation of custom components. - `streamlit/connections`: `st.connection` backends (SQL, Snowflake, and callers-rights variants). - `streamlit/hello`: `streamlit hello` app implementation. - `streamlit/navigation`: Multi-page app implementation. - `streamlit/proto`: Generated protobuf definitions for client-server communication. - `streamlit/testing`: AppTest v1 implementation. - `streamlit/vendor`: Vendored dependencies. - `streamlit/watcher`: File-watcher implementations. - `streamlit/__init__.py`: Defines all commands in the `st` namespace. - `pyproject.toml`: Package configuration of the Streamlit library. - `tests`: Python unit tests (pytest). ## Dependencies - Add a dependency only when it provides meaningful value that cannot easily be replicated with an in-house implementation. Each dependency increases the risk of supply-chain attacks, breakage from incompatible new versions, and conflicts with other dependencies in users' environments. - Runtime dependencies of the published Streamlit library in `lib/pyproject.toml` must include a lower bound and an upper bound pinned to the next unreleased major version, for example `package>=1.2.3,<2`. These ranges are the published package's contract with users and feed the min-version CI job, so they minimize potential breaks from new major versions. Exemptions are allowed, but must include a clear comment explaining why the dependency should not be capped. - This bounded-range rule does NOT apply to the dev/CI-only `[dependency-groups]` in the root `pyproject.toml`. Those use bare package names because `uv.lock` owns the exact versions; add a constraint only when functionally required (an exact `==` pin for a deliberately held-back tool, or an upper cap `<` for a known-broken version, mirrored by an `ignore` entry in `.github/dependabot.yml`; a single-release `!=` exclusion needs no `ignore` entry), and do not add lower-bound floors. ## Typing - Add typing annotations to every new function, method or class member. - Use `typing_extensions` for back-porting newer typing features. - Use future annotations via `from __future__ import annotations`. - `make python-types` runs both `ty` and `mypy`. `ty` resolves first-party `streamlit.*` imports (config in root `pyproject.toml`); prefer real narrowing/annotation fixes over suppressions. When a checker-specific suppression is needed, use a rule-specific comment such as `# ty: ignore[redundant-cast]` (and keep `# type: ignore[...]` for mypy when both apply). ## Relevant `make` commands Run from the repo root: - `make python-lint`: Lint and check formatting of Python files (ruff). - `make python-tests`: Run all Python unit tests (pytest). - `make python-types`: Run the Python type checker (mypy & ty). - `make python-format`: Format Python files (ruff).