[build-system] requires = ["hatchling", "uv-dynamic-versioning>=0.11"] build-backend = "hatchling.build" [project] name = "xy" dynamic = ["version"] description = "Experimental Python charting engine with a native Rust core, binary columnar transport, and a GPU render client" readme = "README.md" requires-python = ">=3.11" license = { text = "Apache-2.0" } dependencies = [ "numpy>=1.24", "anywidget>=0.9", ] [project.optional-dependencies] # The adapter source ships in every xy wheel so the framework integration and # render client can never drift. The extra only selects the supported Reflex # floor; importing plain `xy` remains framework-free. reflex = [ "reflex>=0.9.6", ] [project.urls] Repository = "https://github.com/reflex-dev/xy" Issues = "https://github.com/reflex-dev/xy/issues" Changelog = "https://github.com/reflex-dev/xy/blob/main/CHANGELOG.md" Security = "https://github.com/reflex-dev/xy/blob/main/SECURITY.md" [dependency-groups] dev = [ "pytest>=8", "ruff==0.15.8", "ty", "hypothesis>=6", # Optional *input* format, never a runtime dependency: the Arrow ingest # tests importorskip it, so it lives in dev where CI exercises them. "pyarrow>=15", # Decode oracle for the native JPEG/WebP encoders, never a runtime # dependency: the image-export tests importorskip it. "pillow>=10", # The FastAPI drilldown example's live_drilldown module imports Starlette at # load (its callback endpoint), never a runtime dependency of xy: the # drilldown pan-alignment browser test importorskips it, so it lives in dev # where CI exercises that test. "starlette>=0.36", # tests/reflex_adapter exercises the real socket data plane when the # `reflex` extra is installed in the main CI job. "aiohttp>=3.9", "uvicorn>=0.23", # Parses .binder/environment.yml in tests/test_binder_config.py so the # Binder contract is asserted on structure, not string transcription. "pyyaml>=6", ] # CodSpeed's pytest plugin is intentionally separate from the normal dev # environment: it is useful for performance runs, but is not needed for unit # tests or static analysis. codspeed = [ "pytest-codspeed>=5,<6", ] # The distribution version is derived from git tags, never written down here: # `git tag vX.Y.Z && git push --tags` is the whole release action, so a tag can # no longer disagree with a checked-in number (they did drift — pyproject sat at # 0.0.1 while v0.0.2 was live). Dunamai's default tag pattern requires the `v` # prefix, which is exactly what keeps the docs-deploy CalVer tags (2026.WW.N, # disjoint from library versions by design) from being read as library releases. [tool.hatch.version] source = "uv-dynamic-versioning" [tool.uv-dynamic-versioning] vcs = "git" style = "pep440" # Builds off a tagged commit get the bare version; anything else gets # `.devN+`, which is legible locally and unpublishable to PyPI on # purpose — only a tag can produce an uploadable version. bump = true # Only reached by a source tree with neither git metadata nor PKG-INFO (a # `git archive` tarball, say): such a build still succeeds, at an obviously # unreal version rather than a plausible-but-wrong one. It does *not* cover # unpacked sdists — those resolve from their own PKG-INFO, so `pip install # xy-X.Y.Z.tar.gz` keeps the published version without needing a repo. fallback-version = "0.0.0" [tool.hatch.build] # The render-client bundles are a generated artifact — built by hatch_build.py # (`node js/build.mjs`) and git-ignored (§33), never committed. `artifacts` # re-includes them in both the wheel and the sdist even though the VCS ignores # them; the build hook guarantees they exist on disk before file collection. artifacts = [ "python/xy/static/index.js", "python/xy/static/standalone.js", ] [tool.hatch.build.targets.wheel] packages = ["python/xy", "python/reflex_xy"] dev-mode-dirs = ["python"] [tool.hatch.build.targets.wheel.hooks.custom] path = "hatch_build.py" # The sdist runs the same hook (guarded to build only the render client for # this target, never the native core) so the generated bundles exist on disk # for the `artifacts` include — a fresh CI checkout has no committed bundle. [tool.hatch.build.targets.sdist.hooks.custom] path = "hatch_build.py" [tool.hatch.build.targets.sdist] include = [ # Both packages published by the xy distribution. The bundled adapter is # selected by the `reflex` extra; the retired python/reflex-xy project and # its independent lock/release material must not ride in this sdist. "python/xy", "python/reflex_xy", "src", # The whole js/ dir (TS sources + build.mjs + tsconfig) plus the npm # manifests, so the sdist can rebuild the client with `npm ci && # node js/build.mjs` (verify_sdist.py checks the load-bearing files). "js", "package.json", "package-lock.json", "Cargo.toml", "Cargo.lock", "hatch_build.py", "README.md", "LICENSE", "CHANGELOG.md", "SECURITY.md", "CONTRIBUTING.md", ] exclude = [ # Repository-only development, CI, documentation, and evidence material. "/.github/**", "/benchmarks/**", "/docs/**", "/examples/**", "/Makefile", "/scripts/**", "/spec/**", "/tests/**", "/.*-venv/**", "/.venv/**", "/venv/**", "/.tox/**", "/.nox/**", "/.pytest_cache/**", "/.ruff_cache/**", "/node_modules/**", "/target/**", "/dist/**", # Belt and braces for the include narrowing above: hatchling auto-admits # readme-named files, which could smuggle the retired adapter project back in. "/python/reflex-xy/**", ] [tool.ruff] line-length = 100 target-version = "py311" src = ["python", "tests", "scripts", "benchmarks"] [tool.ruff.lint] select = [ "E", "F", "W", # pycodestyle / pyflakes "I", # isort "UP", # pyupgrade "B", # bugbear "SIM", # simplify "RUF", ] ignore = [ "E501", # line length is the formatter's job # Stylistic churn we opt out of (newer-ruff additions; the codebase keeps # explicit typing.Optional/Union in some public-facing modules for clarity, # and unicode ×/– in prose comments is intended): "UP007", # Union[X, Y] -> X | Y "UP037", # quoted annotations "UP045", # Optional[X] -> X | None "RUF002", # ambiguous unicode in docstrings "RUF003", # ambiguous unicode in comments "RUF005", # list concatenation vs unpacking "RUF046", # int() on already-int "RUF059", # unused unpacked variable (tests unpack payloads for clarity) "RUF100", # unused noqa (kept for editors running broader rule sets) ] [tool.ruff.format] # The dual-engine PDSH notebooks mirror each cell verbatim across the two # plotting aliases (mpl_plt/xy_plt). The aliases differ in length, so a line # near the width limit wraps in one copy and not the other — the formatter and # sync_dual_engine_notebooks.py --check can never both pass. The sync script # owns these files' layout. exclude = ["examples/pdsh/*.ipynb"] [tool.ruff.lint.per-file-ignores] # Third-party example code (PDSH notebooks): kept faithful to upstream, so # upstream's import placement and naming style is not ours to lint. F811: the # dual-engine layout mirrors every cell (matplotlib then xy), so imports and # helper defs repeat by design. "examples/pdsh/*.ipynb" = ["E402", "E731", "E741", "F401", "F811", "I001", "UP030", "UP032", "B007"] # Reflex state classes declare mutable defaults by design (reflex deep-copies # them per instance); both showcases use intentional unicode (× ∈ · › ⌄) in # their page prose and chrome. "examples/reflex/**" = ["RUF012", "RUF001"] "examples/fastapi/**" = ["RUF001"] "tests/reflex_adapter/*.py" = ["RUF012"] [tool.ruff.lint.isort] known-first-party = ["reflex_xy", "xy"] [tool.ty.environment] python = ".venv" # Maintenance entry points are executable by path and import sibling helpers by # their local module names. Keep those imports visible when checking scripts. extra-paths = ["scripts"] [tool.ty.src] # Type-check the shippable library; tests/scripts/benchmarks use dynamic # patterns (ctypes, deliberately subscripting known-non-None Optionals) that a # pre-1.0 checker flags as false positives. The example apps live under # examples/ (outside this include) for the same reason: reflex state vars are # Vars at class scope (`.length()` etc.), which a plain annotation can't express. include = ["python"] [tool.pytest.ini_options] testpaths = ["tests"] # Repo root on sys.path so tests can import the `benchmarks` package even when # pytest is invoked as a bare binary (CI does `.venv/bin/pytest`, which — # unlike `python -m pytest` — does not put the CWD on sys.path). pythonpath = ["."]