From 0ca77493ae1eebfabd5dfccc81510b20e546879e Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Mon, 23 Mar 2026 17:50:15 -0500 Subject: [PATCH 1/2] py(deps[docs]): add sphinx-design, annotate doc dependencies with site URLs why: sphinx-design is needed for grid cards in the documentation landing pages and section indexes. Doc-site URLs added as inline comments for quick reference when managing dependencies. what: - Add sphinx-design to docs and dev dependency groups - Annotate all doc dependencies with their documentation URLs --- pyproject.toml | 46 ++++++++++++++++++++++++---------------------- uv.lock | 43 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 64 insertions(+), 25 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 84c73e066..b1467939b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,17 +52,18 @@ Changes = "https://github.com/tmux-python/libtmux/blob/master/CHANGES" [dependency-groups] dev = [ # Docs - "sphinx<9", - "furo", - "gp-libs", - "sphinx-autobuild", - "sphinx-autodoc-typehints", - "sphinx-inline-tabs", - "sphinxext-opengraph", - "sphinx-copybutton", - "sphinxext-rediraffe", - "myst-parser", - "linkify-it-py", + "sphinx<9", # https://www.sphinx-doc.org/ + "furo", # https://pradyunsg.me/furo/ + "gp-libs", # https://gp-libs.git-pull.com/ + "sphinx-autobuild", # https://sphinx-extensions.readthedocs.io/en/latest/sphinx-autobuild.html + "sphinx-autodoc-typehints", # https://sphinx-autodoc-typehints.readthedocs.io/ + "sphinx-inline-tabs", # https://sphinx-inline-tabs.readthedocs.io/ + "sphinxext-opengraph", # https://sphinxext-opengraph.readthedocs.io/ + "sphinx-copybutton", # https://sphinx-copybutton.readthedocs.io/ + "sphinxext-rediraffe", # https://sphinxext-rediraffe.readthedocs.io/ + "sphinx-design", # https://sphinx-design.readthedocs.io/ + "myst-parser", # https://myst-parser.readthedocs.io/ + "linkify-it-py", # https://github.com/tsutsu3/linkify-it-py # Testing "typing-extensions; python_version < '3.11'", "gp-libs", @@ -81,17 +82,18 @@ dev = [ ] docs = [ - "sphinx<9", - "furo", - "gp-libs", - "sphinx-autobuild", - "sphinx-autodoc-typehints", - "sphinx-inline-tabs", - "sphinxext-opengraph", - "sphinx-copybutton", - "sphinxext-rediraffe", - "myst-parser", - "linkify-it-py", + "sphinx<9", # https://www.sphinx-doc.org/ + "furo", # https://pradyunsg.me/furo/ + "gp-libs", # https://gp-libs.git-pull.com/ + "sphinx-autobuild", # https://sphinx-extensions.readthedocs.io/en/latest/sphinx-autobuild.html + "sphinx-autodoc-typehints", # https://sphinx-autodoc-typehints.readthedocs.io/ + "sphinx-inline-tabs", # https://sphinx-inline-tabs.readthedocs.io/ + "sphinxext-opengraph", # https://sphinxext-opengraph.readthedocs.io/ + "sphinx-copybutton", # https://sphinx-copybutton.readthedocs.io/ + "sphinxext-rediraffe", # https://sphinxext-rediraffe.readthedocs.io/ + "sphinx-design", # https://sphinx-design.readthedocs.io/ + "myst-parser", # https://myst-parser.readthedocs.io/ + "linkify-it-py", # https://github.com/tsutsu3/linkify-it-py ] testing = [ "typing-extensions; python_version < '3.11'", diff --git a/uv.lock b/uv.lock index 79d76aad3..e72e68a0b 100644 --- a/uv.lock +++ b/uv.lock @@ -558,6 +558,8 @@ dev = [ { name = "sphinx-autodoc-typehints", version = "3.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx-autodoc-typehints", version = "3.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinx-copybutton" }, + { name = "sphinx-design", version = "0.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "sphinx-design", version = "0.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinx-inline-tabs" }, { name = "sphinxext-opengraph" }, { name = "sphinxext-rediraffe" }, @@ -576,6 +578,8 @@ docs = [ { name = "sphinx-autodoc-typehints", version = "3.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "sphinx-autodoc-typehints", version = "3.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinx-copybutton" }, + { name = "sphinx-design", version = "0.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "sphinx-design", version = "0.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinx-inline-tabs" }, { name = "sphinxext-opengraph" }, { name = "sphinxext-rediraffe" }, @@ -621,6 +625,7 @@ dev = [ { name = "sphinx-autobuild" }, { name = "sphinx-autodoc-typehints" }, { name = "sphinx-copybutton" }, + { name = "sphinx-design" }, { name = "sphinx-inline-tabs" }, { name = "sphinxext-opengraph" }, { name = "sphinxext-rediraffe" }, @@ -635,6 +640,7 @@ docs = [ { name = "sphinx-autobuild" }, { name = "sphinx-autodoc-typehints" }, { name = "sphinx-copybutton" }, + { name = "sphinx-design" }, { name = "sphinx-inline-tabs" }, { name = "sphinxext-opengraph" }, { name = "sphinxext-rediraffe" }, @@ -1322,6 +1328,37 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9e/48/1ea60e74949eecb12cdd6ac43987f9fd331156388dcc2319b45e2ebb81bf/sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e", size = 13343, upload-time = "2023-04-14T08:10:20.844Z" }, ] +[[package]] +name = "sphinx-design" +version = "0.6.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2b/69/b34e0cb5336f09c6866d53b4a19d76c227cdec1bbc7ac4de63ca7d58c9c7/sphinx_design-0.6.1.tar.gz", hash = "sha256:b44eea3719386d04d765c1a8257caca2b3e6f8421d7b3a5e742c0fd45f84e632", size = 2193689, upload-time = "2024-08-02T13:48:44.277Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/43/65c0acbd8cc6f50195a3a1fc195c404988b15c67090e73c7a41a9f57d6bd/sphinx_design-0.6.1-py3-none-any.whl", hash = "sha256:b11f37db1a802a183d61b159d9a202314d4d2fe29c163437001324fe2f19549c", size = 2215338, upload-time = "2024-08-02T13:48:42.106Z" }, +] + +[[package]] +name = "sphinx-design" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.12'", + "python_full_version == '3.11.*'", +] +dependencies = [ + { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/13/7b/804f311da4663a4aecc6cf7abd83443f3d4ded970826d0c958edc77d4527/sphinx_design-0.7.0.tar.gz", hash = "sha256:d2a3f5b19c24b916adb52f97c5f00efab4009ca337812001109084a740ec9b7a", size = 2203582, upload-time = "2026-01-19T13:12:53.297Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/cf/45dd359f6ca0c3762ce0490f681da242f0530c49c81050c035c016bfdd3a/sphinx_design-0.7.0-py3-none-any.whl", hash = "sha256:f82bf179951d58f55dca78ab3706aeafa496b741a91b1911d371441127d64282", size = 2220350, upload-time = "2026-01-19T13:12:51.077Z" }, +] + [[package]] name = "sphinx-inline-tabs" version = "2025.12.21.14" @@ -1417,15 +1454,15 @@ wheels = [ [[package]] name = "starlette" -version = "0.52.1" +version = "1.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c4/68/79977123bb7be889ad680d79a40f339082c1978b5cfcf62c2d8d196873ac/starlette-0.52.1.tar.gz", hash = "sha256:834edd1b0a23167694292e94f597773bc3f89f362be6effee198165a35d62933", size = 2653702, upload-time = "2026-01-18T13:34:11.062Z" } +sdist = { url = "https://files.pythonhosted.org/packages/81/69/17425771797c36cded50b7fe44e850315d039f28b15901ab44839e70b593/starlette-1.0.0.tar.gz", hash = "sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149", size = 2655289, upload-time = "2026-03-22T18:29:46.779Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/0d/13d1d239a25cbfb19e740db83143e95c772a1fe10202dda4b76792b114dd/starlette-0.52.1-py3-none-any.whl", hash = "sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74", size = 74272, upload-time = "2026-01-18T13:34:09.188Z" }, + { url = "https://files.pythonhosted.org/packages/0b/c9/584bc9651441b4ba60cc4d557d8a547b5aff901af35bda3a4ee30c819b82/starlette-1.0.0-py3-none-any.whl", hash = "sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b", size = 72651, upload-time = "2026-03-22T18:29:45.111Z" }, ] [[package]] From 567111b9930d67715440027aec4a742946ed3e25 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Mon, 23 Mar 2026 17:50:15 -0500 Subject: [PATCH 2/2] docs(redesign): restructure documentation to Library Skeleton pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit why: The documentation had accumulated organically — test helpers and pytest plugin floated as top-level orphans, API reference files used ambiguous names (servers.md vs libtmux.server.md), internal APIs weren't separated from public ones, and the landing page dumped the full README with no navigation affordance. This restructure follows the Python Documentation Skeletons spec where api/ is the primary reference surface for a library package. what: Structure: - Rename api/ reference files to libtmux..md format (10 files) - Move test-helpers/ and pytest-plugin/ under api/ as auxiliary public API surfaces - Restructure internals/ with api/ subdirectory using libtmux._internal.*.md naming - Create project/ directory (contributing, code-style, releasing) - Move developing.md to project/contributing.md - Fold about.md content (ID prefixes, naming conventions) into topics/architecture.md New pages: - api/public-api.md — stability contract, pre-1.0 semver policy, deprecation process - api/compatibility.md — Python/tmux/platform support matrix - api/deprecations.md — active deprecation tracker - topics/architecture.md — module hierarchy, data flow, internal identifiers (merged from about.md) - topics/configuration.md — env vars, format handling - topics/design-decisions.md — ORM rationale, format strings, neo.py - topics/public-vs-internal.md — boundary philosophy, promotion path - project/code-style.md — ruff, mypy, NumPy docstrings - project/releasing.md — git tags, OIDC trusted publishing Landing page: - Compose standalone homepage (no README.md include) - One-sentence intro, 2x2 grid cards, 2-command install with pin tip, 6-line code snippet with hierarchy diagram, pytest fixture example Section indexes: - api/index.md: 2x2 Core Objects + 3x2 Supporting Modules card grids - topics/index.md: 2x4 card grid for substantive topic pages - project/index.md: 2x2 card grid for contributor pages Navigation: - Sidebar: Quickstart, Topics, API Reference, Internals, Project, Changelog, Migration, Glossary - 22 redirects for all renamed/moved files - README.md API URLs updated to new libtmux..html paths Dependencies: - Add sphinx-design to docs and dev dependency groups - Annotate all doc dependencies with documentation site URLs conf.py: - Add sphinx_design extension - Add myst_heading_anchors = 4 --- README.md | 10 +- docs/about.md | 100 ---------------- docs/api/compatibility.md | 29 +++++ docs/api/deprecations.md | 16 +++ docs/api/index.md | 107 ++++++++++++++++-- docs/api/{common.md => libtmux.common.md} | 0 .../{constants.md => libtmux.constants.md} | 0 docs/api/{exceptions.md => libtmux.exc.md} | 0 docs/api/{hooks.md => libtmux.hooks.md} | 0 docs/api/{properties.md => libtmux.neo.md} | 0 docs/api/{options.md => libtmux.options.md} | 0 docs/api/{panes.md => libtmux.pane.md} | 0 docs/api/{servers.md => libtmux.server.md} | 0 docs/api/{sessions.md => libtmux.session.md} | 0 docs/api/{windows.md => libtmux.window.md} | 0 docs/api/public-api.md | 57 ++++++++++ docs/{ => api}/pytest-plugin/index.md | 0 docs/{ => api}/test-helpers/constants.md | 0 docs/{ => api}/test-helpers/environment.md | 0 docs/{ => api}/test-helpers/index.md | 0 docs/{ => api}/test-helpers/random.md | 0 docs/{ => api}/test-helpers/retry.md | 0 docs/{ => api}/test-helpers/temporary.md | 0 docs/conf.py | 3 + docs/index.md | 98 +++++++++++++--- .../libtmux._internal.constants.md} | 0 .../libtmux._internal.dataclasses.md} | 0 .../libtmux._internal.query_list.md} | 0 .../libtmux._internal.sparse_array.md} | 0 docs/internals/index.md | 14 ++- docs/project/code-style.md | 33 ++++++ .../contributing.md} | 0 docs/project/index.md | 36 ++++++ docs/project/releasing.md | 55 +++++++++ docs/redirects.txt | 23 ++++ docs/topics/architecture.md | 85 ++++++++++++++ docs/topics/configuration.md | 17 +++ docs/topics/design-decisions.md | 42 +++++++ docs/topics/index.md | 62 +++++++++- docs/topics/public-vs-internal.md | 48 ++++++++ 40 files changed, 696 insertions(+), 139 deletions(-) delete mode 100644 docs/about.md create mode 100644 docs/api/compatibility.md create mode 100644 docs/api/deprecations.md rename docs/api/{common.md => libtmux.common.md} (100%) rename docs/api/{constants.md => libtmux.constants.md} (100%) rename docs/api/{exceptions.md => libtmux.exc.md} (100%) rename docs/api/{hooks.md => libtmux.hooks.md} (100%) rename docs/api/{properties.md => libtmux.neo.md} (100%) rename docs/api/{options.md => libtmux.options.md} (100%) rename docs/api/{panes.md => libtmux.pane.md} (100%) rename docs/api/{servers.md => libtmux.server.md} (100%) rename docs/api/{sessions.md => libtmux.session.md} (100%) rename docs/api/{windows.md => libtmux.window.md} (100%) create mode 100644 docs/api/public-api.md rename docs/{ => api}/pytest-plugin/index.md (100%) rename docs/{ => api}/test-helpers/constants.md (100%) rename docs/{ => api}/test-helpers/environment.md (100%) rename docs/{ => api}/test-helpers/index.md (100%) rename docs/{ => api}/test-helpers/random.md (100%) rename docs/{ => api}/test-helpers/retry.md (100%) rename docs/{ => api}/test-helpers/temporary.md (100%) rename docs/internals/{constants.md => api/libtmux._internal.constants.md} (100%) rename docs/internals/{dataclasses.md => api/libtmux._internal.dataclasses.md} (100%) rename docs/internals/{query_list.md => api/libtmux._internal.query_list.md} (100%) rename docs/internals/{sparse_array.md => api/libtmux._internal.sparse_array.md} (100%) create mode 100644 docs/project/code-style.md rename docs/{developing.md => project/contributing.md} (100%) create mode 100644 docs/project/index.md create mode 100644 docs/project/releasing.md create mode 100644 docs/topics/architecture.md create mode 100644 docs/topics/configuration.md create mode 100644 docs/topics/design-decisions.md create mode 100644 docs/topics/public-vs-internal.md diff --git a/README.md b/README.md index 0944eea4e..778c218ef 100644 --- a/README.md +++ b/README.md @@ -230,12 +230,12 @@ Session($... ...) | libtmux object | tmux concept | Notes | |----------------|-----------------------------|--------------------------------| -| [`Server`](https://libtmux.git-pull.com/api/servers.html) | tmux server / socket | Entry point; owns sessions | -| [`Session`](https://libtmux.git-pull.com/api/sessions.html) | tmux session (`$0`, `$1`,...) | Owns windows | -| [`Window`](https://libtmux.git-pull.com/api/windows.html) | tmux window (`@1`, `@2`,...) | Owns panes | -| [`Pane`](https://libtmux.git-pull.com/api/panes.html) | tmux pane (`%1`, `%2`,...) | Where commands run | +| [`Server`](https://libtmux.git-pull.com/api/libtmux.server.html) | tmux server / socket | Entry point; owns sessions | +| [`Session`](https://libtmux.git-pull.com/api/libtmux.session.html) | tmux session (`$0`, `$1`,...) | Owns windows | +| [`Window`](https://libtmux.git-pull.com/api/libtmux.window.html) | tmux window (`@1`, `@2`,...) | Owns panes | +| [`Pane`](https://libtmux.git-pull.com/api/libtmux.pane.html) | tmux pane (`%1`, `%2`,...) | Where commands run | -Also available: [`Options`](https://libtmux.git-pull.com/api/options.html) and [`Hooks`](https://libtmux.git-pull.com/api/hooks.html) abstractions for tmux configuration. +Also available: [`Options`](https://libtmux.git-pull.com/api/libtmux.options.html) and [`Hooks`](https://libtmux.git-pull.com/api/libtmux.hooks.html) abstractions for tmux configuration. Collections are live and queryable: diff --git a/docs/about.md b/docs/about.md deleted file mode 100644 index 898d2fa4c..000000000 --- a/docs/about.md +++ /dev/null @@ -1,100 +0,0 @@ -(about)= - -# About - -:::{seealso} - -{ref}`api` - -::: - -```{currentmodule} libtmux - -``` - -libtmux is a [typed](https://docs.python.org/3/library/typing.html) [abstraction layer] for tmux. - -It builds upon the concept of targets `-t`, to direct commands against -individual sessions, windows and panes and `FORMATS`, template variables -exposed by tmux to describe their properties. Think of `-t` analogously -to [scope]. - -{class}`common.TmuxRelationalObject` acts as a container to connect the -relations of {class}`Server`, {class}`Session`, {class}`Window` and -{class}`Pane`. - -| Object | Child | Parent | -| ---------------- | ---------------- | ---------------- | -| {class}`Server` | {class}`Session` | None | -| {class}`Session` | {class}`Window` | {class}`Server` | -| {class}`Window` | {class}`Pane` | {class}`Session` | -| {class}`Pane` | None | {class}`Window` | - -Internally, tmux allows multiple servers to be run on a system. Each one -uses a socket. The server-client architecture is executed so cleanly, -many users don't think about it. tmux automatically connects to a default -socket name and location for you if none (`-L`, `-S`) is specified. -A server will be created automatically upon starting if none exists. - -A server can have multiple sessions. `Ctrl+a s` can be used to switch -between sessions running on the server. - -Sessions, windows and panes all have their own unique identifier for -internal purposes. {class}`common.TmuxMappingObject` will make use of the -unique identifiers (`session_id`, `window_id`, `pane_id`) to look -up the data stored in the {class}`Server` object. - -| Object | Prefix | Example | -| ---------------- | ------ | ----------------------------------------- | -| {class}`Server` | N/A | N/A, uses `socket-name` and `socket-path` | -| {class}`Session` | `$` | `$13` | -| {class}`Window` | `@` | `@3243` | -| {class}`Pane` | `%` | `%5433` | - -## Similarities to tmux and Pythonics - -libtmux was built in the spirit of understanding how tmux operates -and how python objects and tools can abstract the APIs in a pleasant way. - -libtmux uses `FORMATTERS` in tmux to give identity attributes to -{class}`Session`, {class}`Window` and {class}`Pane` objects. See -[format.c]. - -[format.c]: https://github.com/tmux/tmux/blob/master/format.c - -How is libtmux able to keep references to panes, windows and sessions? - -> Tmux has unique IDs for sessions, windows and panes. -> -> panes use `%`, such as `%1234` -> -> windows use `@`, such as `@2345` -> -> sessions use `$`, such as `$13` -> -> How is libtmux able to handle windows with no names? - -> Tmux provides `window_id` as a unique identifier. -> -> What is a {pane,window}\_index vs a {pane,window,session}\_id? - -> Pane index refers to the order of a pane on the screen. -> -> Window index refers to the number of the window in the session. -> -> To assert pane, window and session data, libtmux will use -> {meth}`Server.sessions()`, {meth}`Session.windows()`, -> {meth}`Window.panes()` to update objects. - -## Naming conventions - -Because this is a python abstraction and commands like `new-window` -have dashes (-) replaced with underscores (\_). - -## Reference - -- tmux docs -- tmux source code - -[abstraction layer]: http://en.wikipedia.org/wiki/Abstraction_layer -[scope]: https://en.wikipedia.org/wiki/Variable_(computer_science)#Scope_and_extent diff --git a/docs/api/compatibility.md b/docs/api/compatibility.md new file mode 100644 index 000000000..4dc5347e1 --- /dev/null +++ b/docs/api/compatibility.md @@ -0,0 +1,29 @@ +# Compatibility + +## Python + +- **Minimum**: Python 3.10 +- **Tested**: Python 3.10, 3.11, 3.12, 3.13 +- **Maximum**: Python < 4.0 + +## tmux + +- **Minimum**: tmux 3.2a +- **Tested**: latest stable tmux release +- libtmux uses tmux's format system and control mode -- older tmux versions + may lack required format variables + +## Platforms + +| Platform | Status | +|----------|--------| +| Linux | Fully supported | +| macOS | Fully supported | +| WSL / WSL2 | Supported (tmux runs inside WSL) | +| Windows (native) | Not supported (tmux does not run natively on Windows) | + +## Known Limitations + +- tmux must be running and accessible via the default socket or a specified socket +- Some operations require the tmux server to have at least one session +- Format string availability depends on tmux version diff --git a/docs/api/deprecations.md b/docs/api/deprecations.md new file mode 100644 index 000000000..631102b8c --- /dev/null +++ b/docs/api/deprecations.md @@ -0,0 +1,16 @@ +# Deprecations + +Active deprecations with timeline and migration paths. + +## Active Deprecations + + + +No active deprecations at this time. + +See [history](../history.md) for past changes and the +[migration guide](../migration.md) for upgrading between versions. + +## Deprecation Policy + +See [Public API -- Deprecation Process](public-api.md#deprecation-process). diff --git a/docs/api/index.md b/docs/api/index.md index 49c720a5c..a352d8cdc 100644 --- a/docs/api/index.md +++ b/docs/api/index.md @@ -4,16 +4,103 @@ # API Reference +libtmux's public API mirrors tmux's object hierarchy: + +``` +Server -> Session -> Window -> Pane +``` + +## Core Objects + +::::{grid} 2 +:gutter: 3 + +:::{grid-item-card} Server +:link: libtmux.server +:link-type: doc +Entry point. Manages sessions and executes raw tmux commands. +::: + +:::{grid-item-card} Session +:link: libtmux.session +:link-type: doc +Manages windows within a tmux session. +::: + +:::{grid-item-card} Window +:link: libtmux.window +:link-type: doc +Manages panes, layouts, and window operations. +::: + +:::{grid-item-card} Pane +:link: libtmux.pane +:link-type: doc +Terminal instance. Send keys and capture output. +::: + +:::: + +## Supporting Modules + +::::{grid} 3 +:gutter: 3 + +:::{grid-item-card} Common +:link: libtmux.common +:link-type: doc +Base classes and command execution. +::: + +:::{grid-item-card} Neo +:link: libtmux.neo +:link-type: doc +Dataclass-based query interface. +::: + +:::{grid-item-card} Options +:link: libtmux.options +:link-type: doc +tmux option get/set. +::: + +:::{grid-item-card} Hooks +:link: libtmux.hooks +:link-type: doc +tmux hook management. +::: + +:::{grid-item-card} Constants +:link: libtmux.constants +:link-type: doc +Format strings and constants. +::: + +:::{grid-item-card} Exceptions +:link: libtmux.exc +:link-type: doc +Exception hierarchy. +::: + +:::: + +## Test Utilities + +If you're testing code that uses libtmux, see the test helpers and pytest plugin: + +```{toctree} +:maxdepth: 1 + +test-helpers/index +pytest-plugin/index +``` + +## API Contract + ```{toctree} +:maxdepth: 1 -properties -servers -sessions -windows -panes -options -hooks -constants -common -exceptions +public-api +compatibility +deprecations ``` diff --git a/docs/api/common.md b/docs/api/libtmux.common.md similarity index 100% rename from docs/api/common.md rename to docs/api/libtmux.common.md diff --git a/docs/api/constants.md b/docs/api/libtmux.constants.md similarity index 100% rename from docs/api/constants.md rename to docs/api/libtmux.constants.md diff --git a/docs/api/exceptions.md b/docs/api/libtmux.exc.md similarity index 100% rename from docs/api/exceptions.md rename to docs/api/libtmux.exc.md diff --git a/docs/api/hooks.md b/docs/api/libtmux.hooks.md similarity index 100% rename from docs/api/hooks.md rename to docs/api/libtmux.hooks.md diff --git a/docs/api/properties.md b/docs/api/libtmux.neo.md similarity index 100% rename from docs/api/properties.md rename to docs/api/libtmux.neo.md diff --git a/docs/api/options.md b/docs/api/libtmux.options.md similarity index 100% rename from docs/api/options.md rename to docs/api/libtmux.options.md diff --git a/docs/api/panes.md b/docs/api/libtmux.pane.md similarity index 100% rename from docs/api/panes.md rename to docs/api/libtmux.pane.md diff --git a/docs/api/servers.md b/docs/api/libtmux.server.md similarity index 100% rename from docs/api/servers.md rename to docs/api/libtmux.server.md diff --git a/docs/api/sessions.md b/docs/api/libtmux.session.md similarity index 100% rename from docs/api/sessions.md rename to docs/api/libtmux.session.md diff --git a/docs/api/windows.md b/docs/api/libtmux.window.md similarity index 100% rename from docs/api/windows.md rename to docs/api/libtmux.window.md diff --git a/docs/api/public-api.md b/docs/api/public-api.md new file mode 100644 index 000000000..7fe1367db --- /dev/null +++ b/docs/api/public-api.md @@ -0,0 +1,57 @@ +# Public API + +## What Is Public + +Every module documented under [API Reference](index.md) is public API. +This includes: + +### Core Library + +| Module | Import Path | +|--------|-------------| +| Server | `from libtmux.server import Server` | +| Session | `from libtmux.session import Session` | +| Window | `from libtmux.window import Window` | +| Pane | `from libtmux.pane import Pane` | +| Common | `from libtmux.common import ...` | +| Neo | `from libtmux.neo import ...` | +| Options | `from libtmux.options import ...` | +| Hooks | `from libtmux.hooks import ...` | +| Constants | `from libtmux.constants import ...` | +| Exceptions | `from libtmux.exc import ...` | + +### Test Utilities + +| Module | Import Path | +|--------|-------------| +| Test helpers | `from libtmux.test import ...` | +| Pytest plugin | `libtmux.pytest_plugin` (auto-loaded) | + +## What Is Internal + +Modules under `libtmux._internal` and `libtmux._vendor` are **not public**. +They may change or be removed without notice between any release. + +Do not import from: +- `libtmux._internal.*` +- `libtmux._vendor.*` + +## Pre-1.0 Stability Policy + +libtmux is pre-1.0. This means: + +- **Minor versions** (0.x -> 0.y) may include breaking API changes +- **Patch versions** (0.x.y -> 0.x.z) are bug fixes only +- **Pin your dependency**: use `libtmux>=0.55,<0.56` or `libtmux~=0.55.0` + +Breaking changes are documented in the [changelog](../history.md) and +the [deprecations](deprecations.md) page before removal. + +## Deprecation Process + +Before removing or changing public API: + +1. A deprecation warning is added for at least one minor release +2. The change is documented in [deprecations](deprecations.md) +3. Migration guidance is provided +4. The old API is removed in a subsequent minor release diff --git a/docs/pytest-plugin/index.md b/docs/api/pytest-plugin/index.md similarity index 100% rename from docs/pytest-plugin/index.md rename to docs/api/pytest-plugin/index.md diff --git a/docs/test-helpers/constants.md b/docs/api/test-helpers/constants.md similarity index 100% rename from docs/test-helpers/constants.md rename to docs/api/test-helpers/constants.md diff --git a/docs/test-helpers/environment.md b/docs/api/test-helpers/environment.md similarity index 100% rename from docs/test-helpers/environment.md rename to docs/api/test-helpers/environment.md diff --git a/docs/test-helpers/index.md b/docs/api/test-helpers/index.md similarity index 100% rename from docs/test-helpers/index.md rename to docs/api/test-helpers/index.md diff --git a/docs/test-helpers/random.md b/docs/api/test-helpers/random.md similarity index 100% rename from docs/test-helpers/random.md rename to docs/api/test-helpers/random.md diff --git a/docs/test-helpers/retry.md b/docs/api/test-helpers/retry.md similarity index 100% rename from docs/test-helpers/retry.md rename to docs/api/test-helpers/retry.md diff --git a/docs/test-helpers/temporary.md b/docs/api/test-helpers/temporary.md similarity index 100% rename from docs/test-helpers/temporary.md rename to docs/api/test-helpers/temporary.md diff --git a/docs/conf.py b/docs/conf.py index 2c634281b..fb04d0d28 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -42,6 +42,7 @@ "sphinxext.rediraffe", "myst_parser", "linkify_issues", + "sphinx_design", ] myst_enable_extensions = [ @@ -52,6 +53,8 @@ "linkify", ] +myst_heading_anchors = 4 + templates_path = ["_templates"] source_suffix = {".rst": "restructuredtext", ".md": "markdown"} diff --git a/docs/index.md b/docs/index.md index 341532ee9..9f1983424 100644 --- a/docs/index.md +++ b/docs/index.md @@ -2,30 +2,100 @@ # libtmux -```{include} ../README.md -:start-after: +Typed Python API for [tmux](https://github.com/tmux/tmux). Control +servers, sessions, windows, and panes as Python objects. + +::::{grid} 2 +:gutter: 3 + +:::{grid-item-card} Quickstart +:link: quickstart +:link-type: doc +Install and make your first API call in 5 minutes. +::: + +:::{grid-item-card} Topics +:link: topics/index +:link-type: doc +Architecture, traversal, filtering, and automation patterns. +::: + +:::{grid-item-card} API Reference +:link: api/index +:link-type: doc +Every public class, function, and exception. +::: + +:::{grid-item-card} Contributing +:link: project/index +:link-type: doc +Development setup, code style, release process. +::: + +:::: + +## Install + +```console +$ pip install libtmux ``` -```{toctree} -:maxdepth: 2 -:hidden: +```console +$ uv add libtmux +``` -quickstart -about -topics/index -api/index -pytest-plugin/index -test-helpers/index +Tip: libtmux is pre-1.0. Pin to a range: `libtmux>=0.55,<0.56` + +See [Quickstart](quickstart.md) for all methods and first steps. + +## At a glance + +```python +import libtmux + +server = libtmux.Server() +session = server.sessions.get(session_name="my-project") +window = session.active_window +pane = window.split() +pane.send_keys("echo hello") +``` + +``` +Server → Session → Window → Pane +``` + +Every level of the [tmux hierarchy](topics/architecture.md) is a typed +Python object with traversal, filtering, and command execution. + +| Object | What it wraps | +|--------|---------------| +| {class}`~libtmux.server.Server` | tmux server / socket | +| {class}`~libtmux.session.Session` | tmux session | +| {class}`~libtmux.window.Window` | tmux window | +| {class}`~libtmux.pane.Pane` | tmux pane | + +## Testing + +libtmux ships a [pytest plugin](api/pytest-plugin/index.md) with +isolated tmux fixtures: + +```python +def test_my_tool(session): + window = session.new_window(window_name="test") + pane = window.active_pane + pane.send_keys("echo hello") + assert window.window_name == "test" ``` ```{toctree} -:caption: Project :hidden: -developing +quickstart +topics/index +api/index internals/index +project/index history migration glossary -GitHub ``` diff --git a/docs/internals/constants.md b/docs/internals/api/libtmux._internal.constants.md similarity index 100% rename from docs/internals/constants.md rename to docs/internals/api/libtmux._internal.constants.md diff --git a/docs/internals/dataclasses.md b/docs/internals/api/libtmux._internal.dataclasses.md similarity index 100% rename from docs/internals/dataclasses.md rename to docs/internals/api/libtmux._internal.dataclasses.md diff --git a/docs/internals/query_list.md b/docs/internals/api/libtmux._internal.query_list.md similarity index 100% rename from docs/internals/query_list.md rename to docs/internals/api/libtmux._internal.query_list.md diff --git a/docs/internals/sparse_array.md b/docs/internals/api/libtmux._internal.sparse_array.md similarity index 100% rename from docs/internals/sparse_array.md rename to docs/internals/api/libtmux._internal.sparse_array.md diff --git a/docs/internals/index.md b/docs/internals/index.md index 0d19d3763..82fe50804 100644 --- a/docs/internals/index.md +++ b/docs/internals/index.md @@ -2,17 +2,19 @@ # Internals -:::{warning} -Be careful with these! Internal APIs are **not** covered by version policies. They can break or be removed between minor versions! +:::{danger} +**No stability guarantee.** Internal APIs are **not** covered by version +policies. They can break or be removed between any minor versions without +notice. If you need an internal API stabilized please [file an issue](https://github.com/tmux-python/libtmux/issues). ::: ```{toctree} -dataclasses -query_list -constants -sparse_array +api/libtmux._internal.dataclasses +api/libtmux._internal.query_list +api/libtmux._internal.constants +api/libtmux._internal.sparse_array ``` ## Environmental variables diff --git a/docs/project/code-style.md b/docs/project/code-style.md new file mode 100644 index 000000000..30344fe77 --- /dev/null +++ b/docs/project/code-style.md @@ -0,0 +1,33 @@ +# Code Style + +## Formatting + +libtmux uses [ruff](https://github.com/astral-sh/ruff) for both linting and formatting. + +```console +$ uv run ruff format . +``` + +```console +$ uv run ruff check . --fix --show-fixes +``` + +## Type Checking + +Strict [mypy](https://mypy-lang.org/) is enforced across `src/` and `tests/`. + +```console +$ uv run mypy +``` + +## Docstrings + +All public functions and methods use NumPy-style docstrings. See the +[NumPy docstring guide](https://numpydoc.readthedocs.io/en/latest/format.html). + +## Imports + +- Standard library: namespace imports (`import pathlib`, not `from pathlib import Path`) + - Exception: `from dataclasses import dataclass, field` +- Typing: `import typing as t`, access via `t.Optional`, `t.NamedTuple`, etc. +- All files: `from __future__ import annotations` diff --git a/docs/developing.md b/docs/project/contributing.md similarity index 100% rename from docs/developing.md rename to docs/project/contributing.md diff --git a/docs/project/index.md b/docs/project/index.md new file mode 100644 index 000000000..8c37a15d8 --- /dev/null +++ b/docs/project/index.md @@ -0,0 +1,36 @@ +(project)= + +# Project + +Information for contributors and maintainers. + +::::{grid} 2 +:gutter: 3 + +:::{grid-item-card} Contributing +:link: contributing +:link-type: doc +Development setup, running tests, submitting PRs. +::: + +:::{grid-item-card} Code Style +:link: code-style +:link-type: doc +Ruff, mypy, NumPy docstrings, import conventions. +::: + +:::{grid-item-card} Releasing +:link: releasing +:link-type: doc +Release checklist and version policy. +::: + +:::: + +```{toctree} +:hidden: + +contributing +code-style +releasing +``` diff --git a/docs/project/releasing.md b/docs/project/releasing.md new file mode 100644 index 000000000..5aa55dadf --- /dev/null +++ b/docs/project/releasing.md @@ -0,0 +1,55 @@ +# Releasing + +## Version Policy + +libtmux is pre-1.0. Minor version bumps may include breaking API changes. +Users should pin to `>=0.x,<0.y`. + +## Release Process + +Releases are triggered by git tags and published to PyPI via OIDC trusted publishing. + +1. Update `CHANGES` with the release notes + +2. Bump version in `src/libtmux/__about__.py` + +3. Commit: + + ```console + $ git commit -m "libtmux " + ``` + +4. Tag: + + ```console + $ git tag v + ``` + +5. Push: + + ```console + $ git push && git push --tags + ``` + +6. CI builds and publishes to PyPI automatically via trusted publishing + +## Changelog Format + +The `CHANGES` file uses this format: + +```text +libtmux () +-------------------------- + +### What's new + +- Description of feature (#issue) + +### Bug fixes + +- Description of fix (#issue) + +### Breaking changes + +- Description of break, migration path (#issue) +``` diff --git a/docs/redirects.txt b/docs/redirects.txt index afff787ad..dd952340d 100644 --- a/docs/redirects.txt +++ b/docs/redirects.txt @@ -16,3 +16,26 @@ "reference/sessions.md" "api/sessions.md" "reference/windows.md" "api/windows.md" "pytest-plugin/test.md" "test-helpers/index.md" +"api/servers.md" "api/libtmux.server.md" +"api/sessions.md" "api/libtmux.session.md" +"api/windows.md" "api/libtmux.window.md" +"api/panes.md" "api/libtmux.pane.md" +"api/options.md" "api/libtmux.options.md" +"api/hooks.md" "api/libtmux.hooks.md" +"api/constants.md" "api/libtmux.constants.md" +"api/common.md" "api/libtmux.common.md" +"api/properties.md" "api/libtmux.neo.md" +"api/exceptions.md" "api/libtmux.exc.md" +"test-helpers/index.md" "api/test-helpers/index.md" +"test-helpers/constants.md" "api/test-helpers/constants.md" +"test-helpers/environment.md" "api/test-helpers/environment.md" +"test-helpers/temporary.md" "api/test-helpers/temporary.md" +"test-helpers/random.md" "api/test-helpers/random.md" +"test-helpers/retry.md" "api/test-helpers/retry.md" +"pytest-plugin/index.md" "api/pytest-plugin/index.md" +"developing.md" "project/contributing.md" +"internals/dataclasses.md" "internals/api/libtmux._internal.dataclasses.md" +"internals/constants.md" "internals/api/libtmux._internal.constants.md" +"internals/query_list.md" "internals/api/libtmux._internal.query_list.md" +"internals/sparse_array.md" "internals/api/libtmux._internal.sparse_array.md" +"about.md" "topics/architecture.md" diff --git a/docs/topics/architecture.md b/docs/topics/architecture.md new file mode 100644 index 000000000..df25834d8 --- /dev/null +++ b/docs/topics/architecture.md @@ -0,0 +1,85 @@ +(about)= + +# Architecture + +libtmux is a [typed](https://docs.python.org/3/library/typing.html) +abstraction layer for tmux. It builds upon tmux's concept of targets +(`-t`) to direct commands against individual sessions, windows, and panes, +and `FORMATS` — template variables tmux exposes to describe object +properties. + +## Object Hierarchy + +libtmux mirrors tmux's object hierarchy as a typed Python ORM: + +``` +Server +└── Session + └── Window + └── Pane +``` + +| Object | Child | Parent | +|--------|-------|--------| +| {class}`~libtmux.server.Server` | {class}`~libtmux.session.Session` | None | +| {class}`~libtmux.session.Session` | {class}`~libtmux.window.Window` | {class}`~libtmux.server.Server` | +| {class}`~libtmux.window.Window` | {class}`~libtmux.pane.Pane` | {class}`~libtmux.session.Session` | +| {class}`~libtmux.pane.Pane` | None | {class}`~libtmux.window.Window` | + +{class}`~libtmux.common.TmuxRelationalObject` acts as the base container +connecting these relationships. + +## Internal Identifiers + +tmux assigns unique IDs to sessions, windows, and panes. libtmux uses +these — via {class}`~libtmux.common.TmuxMappingObject` — to track objects +reliably across state refreshes. + +| Object | Prefix | Example | +|--------|--------|---------| +| {class}`~libtmux.server.Server` | N/A | Uses `socket-name` / `socket-path` | +| {class}`~libtmux.session.Session` | `$` | `$13` | +| {class}`~libtmux.window.Window` | `@` | `@3243` | +| {class}`~libtmux.pane.Pane` | `%` | `%5433` | + +## Core Objects + +Each level wraps tmux commands and format queries: + +- {class}`~libtmux.server.Server` — entry point, manages sessions, executes raw tmux commands +- {class}`~libtmux.session.Session` — manages windows within a session +- {class}`~libtmux.window.Window` — manages panes, handles layouts +- {class}`~libtmux.pane.Pane` — terminal instance, sends keys and captures output + +## Data Flow + +1. User creates a `Server` (connects to a running tmux server) +2. Queries use tmux format strings ({mod}`libtmux.constants`) to fetch state +3. Results are parsed into typed Python objects +4. Mutations dispatch tmux commands via the `cmd()` method +5. Objects refresh state from tmux on demand + +## Module Map + +| Module | Role | +|--------|------| +| {mod}`libtmux.server` | Server connection and session management | +| {mod}`libtmux.session` | Session operations | +| {mod}`libtmux.window` | Window operations and pane management | +| {mod}`libtmux.pane` | Pane I/O and capture | +| {mod}`libtmux.common` | Base classes, command execution | +| {mod}`libtmux.neo` | Modern dataclass-based query interface | +| {mod}`libtmux.constants` | Format string constants | +| {mod}`libtmux.options` | tmux option get/set | +| {mod}`libtmux.hooks` | tmux hook management | +| {mod}`libtmux.exc` | Exception hierarchy | + +## Naming Conventions + +tmux commands use dashes (`new-window`). libtmux replaces these with +underscores (`new_window`) to follow Python naming conventions. + +## References + +- [tmux man page](https://man.openbsd.org/tmux.1) +- [tmux source code](https://github.com/tmux/tmux) diff --git a/docs/topics/configuration.md b/docs/topics/configuration.md new file mode 100644 index 000000000..ec51b70ae --- /dev/null +++ b/docs/topics/configuration.md @@ -0,0 +1,17 @@ +# Configuration + +## Environment Variables + +libtmux itself does not read environment variables for configuration. +All configuration is done programmatically through the Python API. + +The tmux server libtmux connects to may be influenced by standard tmux +environment variables (`TMUX`, `TMUX_TMPDIR`). + +## Format Strings + +libtmux uses tmux's format system to query state. Format constants are +defined in {mod}`libtmux.formats` and used internally by all object types. + +See the [tmux man page](http://man.openbsd.org/OpenBSD-current/man1/tmux.1) +for the full list of available formats. diff --git a/docs/topics/design-decisions.md b/docs/topics/design-decisions.md new file mode 100644 index 000000000..700e5e4ab --- /dev/null +++ b/docs/topics/design-decisions.md @@ -0,0 +1,42 @@ +# Design Decisions + +## Why ORM-Style Objects + +tmux organizes terminals in a strict hierarchy: Server → Session → Window → Pane. Each level owns the next. libtmux mirrors this with Python objects that maintain the same parent-child relationships. + +The alternative — a flat command-builder API (`tmux("new-session", "-s", "foo")`) — loses the relational structure. You'd have to track which windows belong to which session manually. The ORM approach lets you write `session.windows` and get a live, filterable collection. + +## Why Format Strings + +tmux exposes object properties through its format system (`-F` flags). For example, `tmux list-sessions -F '#{session_id}:#{session_name}'` returns structured data. + +libtmux uses this instead of parsing human-readable `tmux ls` output because: + +- **Stability**: format variables are part of tmux's documented interface +- **Precision**: no regex fragility from parsing prose output +- **Completeness**: formats expose properties (like `session_id`) that don't appear in default output + +Format constants are defined in {mod}`libtmux.formats`. + +## Why Dataclasses in neo.py + +{mod}`libtmux.neo` provides a modern dataclass-based interface alongside the legacy dict-style objects. The motivation: + +- **Type safety**: dataclass fields have declared types, enabling mypy checks and IDE completion +- **Predictability**: attribute access (`session.session_name`) instead of dict access (`session["session_name"]`) +- **Migration path**: the two interfaces coexist, allowing gradual adoption without breaking existing code + +## Pre-1.0 API Evolution + +libtmux is pre-1.0. This is a deliberate choice — the API is still maturing. What this means in practice: + +- **Minor versions** (0.x → 0.y) may include breaking changes +- **Patch versions** (0.x.y → 0.x.z) are bug fixes only +- **Pin your dependency**: use `libtmux>=0.55,<0.56` or `libtmux~=0.55.0` + +Breaking changes always get: +1. A deprecation warning for at least one minor release +2. Documentation in the [changelog](../history.md) and [deprecations](../api/deprecations.md) +3. Migration guidance + +See [Public API](../api/public-api.md) for the stability contract. diff --git a/docs/topics/index.md b/docs/topics/index.md index f22e7f81b..6a2c4525a 100644 --- a/docs/topics/index.md +++ b/docs/topics/index.md @@ -1,13 +1,67 @@ ---- -orphan: true ---- - # Topics Explore libtmux's core functionalities and underlying principles at a high level, while providing essential context and detailed explanations to help you understand its design and usage. +::::{grid} 2 +:gutter: 3 + +:::{grid-item-card} Architecture +:link: architecture +:link-type: doc +Module hierarchy, data flow, and internal identifiers. +::: + +:::{grid-item-card} Traversal +:link: traversal +:link-type: doc +Navigate the Server, Session, Window, Pane hierarchy. +::: + +:::{grid-item-card} Filtering +:link: filtering +:link-type: doc +Query and filter collections by attributes. +::: + +:::{grid-item-card} Pane Interaction +:link: pane_interaction +:link-type: doc +Send keys, capture output, and interact with panes. +::: + +:::{grid-item-card} Workspace Setup +:link: workspace_setup +:link-type: doc +Create sessions, windows, and panes programmatically. +::: + +:::{grid-item-card} Automation Patterns +:link: automation_patterns +:link-type: doc +Common patterns for scripting and automation. +::: + +:::{grid-item-card} Context Managers +:link: context_managers +:link-type: doc +Automatic cleanup with temporary sessions and windows. +::: + +:::{grid-item-card} Options & Hooks +:link: options_and_hooks +:link-type: doc +Get and set tmux options and hooks. +::: + +:::: + ```{toctree} +:hidden: +architecture +configuration +design-decisions +public-vs-internal traversal filtering pane_interaction diff --git a/docs/topics/public-vs-internal.md b/docs/topics/public-vs-internal.md new file mode 100644 index 000000000..dd93722a4 --- /dev/null +++ b/docs/topics/public-vs-internal.md @@ -0,0 +1,48 @@ +# Public vs Internal API + +## The Boundary + +libtmux draws a clear line between public and internal code: + +| Import path | Status | Stability | +|-------------|--------|-----------| +| `libtmux.*` | Public | Covered by [deprecation policy](../api/public-api.md) | +| `libtmux._internal.*` | Internal | No guarantee — may break between any release | +| `libtmux._vendor.*` | Vendored | Not part of the API at all | + +If you can import it without a leading underscore in the module path, it's public. + +## Why the Split + +Internal modules exist so the library can iterate freely on implementation details without breaking downstream users. A refactor of `libtmux._internal.query_list` doesn't require a deprecation cycle — it's explicitly not part of the contract. + +This separation also keeps the public API surface intentionally small. Every public module is a commitment to maintain. Internal modules earn promotion through proven stability and user demand. + +## What `_internal/` Contains + +The `_internal` package holds implementation details that support the public API: + +- **`query_list`** — the filtering engine behind `.filter()` and `.get()` on collections +- **`dataclasses`** — base dataclass utilities used by the ORM objects +- **`constants`** — internal constants not meaningful to end users +- **`types`** — type aliases used across the codebase + +These are documented in [Internals](../internals/index.md) for contributors, but downstream projects should not import from them. + +## What `_vendor/` Contains + +The `_vendor` package holds vendored third-party code — copies of external libraries included directly to avoid adding dependencies. This code is not written by the libtmux authors and is not part of the API. + +## How Internal APIs Get Promoted + +1. **Internal**: lives in `_internal/`, no stability promise +2. **Experimental**: documented, usable, but explicitly marked as subject to change +3. **Public**: moved to a top-level module, covered by the deprecation policy + +Promotion happens when an internal API proves stable across multiple releases and users request it. If you depend on an internal API, [file an issue](https://github.com/tmux-python/libtmux/issues) — that signal helps prioritize promotion. + +## Reference + +- [Public API](../api/public-api.md) — the authoritative list of what's stable +- [Compatibility](../api/compatibility.md) — platform and version support +- [Deprecations](../api/deprecations.md) — what's changing