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

tmuxp/.github/CONTRIBUTING.md at master · tmux-python/tmuxp · GitHub

Latest commit

 

History

History
335 lines (234 loc) · 8.48 KB

File metadata and controls

335 lines (234 loc) · 8.48 KB

Contributing

Thanks for looking. Bug reports with a reproduction, and notes on where the documentation misled you, are the most useful thing you can bring right now.

How this project writes prose — README, CHANGES, commit messages, docstrings, and source comments — is set out separately in WRITING.md. Read that before changing any of it. The constraints every change is held to, and the map of what is where, are in AGENTS.md.

Getting set up

Check out the code from GitHub:

$ git clone git@github.com:tmux-python/tmuxp.git
$ cd tmuxp

The easiest way to set up a dev environment is with uv, which manages the virtualenv and Python dependencies for you.

Create the virtualenv and install everything locked in uv.lock:

$ uv sync --all-extras --dev

To refresh those packages later:

$ uv sync --all-extras --dev --upgrade

Then prefix any Python command with uv run:

$ uv run [command]

Advanced: manual virtualenv

Prefer to manage the virtualenv yourself? Create one:

$ virtualenv .venv

Activate it in your current shell:

$ source .venv/bin/activate

Install tmuxp in editable mode, so your edits take effect immediately:

$ pip install -e .

With a uv-managed project, add the checkout as an editable dev dependency instead:

$ uv add --dev --editable .

Prefer a one-off, pipx-style run while you hack? Call tmuxp through uvx:

$ uvx tmuxp

The gates

CI is the order of record; every gate it runs has to pass before a change is done.

Format:

$ uv run ruff format .

CI checks formatting without writing:

$ uv run ruff format --check .

Lint:

$ uv run ruff check .

Autofix what ruff can:

$ uv run ruff check . --fix --show-fixes

Type-check:

$ uv run mypy

Test:

$ uv run py.test

Documentation is a gate, not a courtesy. [tool.pytest.ini_options] in pyproject.toml sets testpaths = ["src/tmuxp", "tests", "docs"] and addopts includes --doctest-modules, so every >>> example under src/tmuxp/** and every doctest in a .py file under docs/ runs as part of uv run py.test — there is no separate doctest step, and a green test run is the proof. README.md is not in testpaths and is never executed; hold its examples correct by review. Which blocks qualify, and the one mistake that silently removes a test, are in WRITING.md.

Before claiming a test or a gate works, show it failing. A gate that has never been red is an assumption.

Imports and typing

  • Namespace imports for the standard library: import pathlib, then pathlib.Path, not from pathlib import Path. Exception: from dataclasses import dataclass, field, since both are used as decorators/defaults, not namespaced. Third-party packages may use from X import Y.
  • Typing: import typing as t, access via the namespace — t.Optional, t.NamedTuple.
  • Every file starts with from __future__ import annotations; ruff's isort config (required-imports in pyproject.toml) enforces it.

Ruff's select is deliberately unset in pyproject.toml — 0.16's curated default rule set stays enabled, and extend-select layers this project's additional linters (pydocstyle, flake8-bugbear, and the rest) on top rather than replacing the defaults.

Tests

The suite lives in tests/, written with pytest. It runs against a real tmux server on a separate socket (tmux -L test_case), so it never disturbs your own sessions.

Write new tests as standalone functions, not class TestFoo: groupings — descriptive function names and file organization carry the structure instead. A couple of older suites still use classes; match the file you are in, prefer functions in a new one.

  • Prefer the server, session, window, pane fixtures from tests/fixtures/ over manual setup, and real tmux fixtures over MagicMock.
  • Use retry_until (from libtmux.test.retry) for anything that waits on an async tmux operation instead of a bare sleep.
  • Use the tmp_path fixture instead of Python's tempfile, and monkeypatch instead of unittest.mock.
  • Plugin tests import mock packages from tests/fixtures/pluginsystem/plugins/ — six fixture plugins (tmuxp_test_plugin_bwb, _bs, _r, _owc, _awf, _fail) exercising each plugin hook and a deliberate failure path.
  • Assert on caplog.records attributes, not string matching on caplog.text: scope capture with caplog.at_level(logging.DEBUG, logger="libtmux.common"), filter records rather than index by position, and assert on schema (record.tmux_exit_code == 0, not "exit code 0" in caplog.text). caplog.record_tuples cannot access extra fields.

Rerun on file change

$ just start

Runs the suite once, then watches for changes via pytest-watcher.

pytest options

Pass extra arguments through PYTEST_ADDOPTS:

$ env PYTEST_ADDOPTS="--verbose" just start

Pick a file:

$ env PYTEST_ADDOPTS="tests/workspace/test_builder.py" just start

Drop into a single test and stop on the first error:

$ env PYTEST_ADDOPTS="-s -x -vv tests/workspace/test_builder.py::test_automatic_rename_option" \
    just start

Drop into pdb on the first error:

$ env PYTEST_ADDOPTS="-x -s --pdb" just start

Set RETRY_TIMEOUT_SECONDS if a workspace-builder test is stubborn on your machine:

$ env RETRY_TIMEOUT_SECONDS=10 uv run py.test

Manual invocation

A single file:

$ uv run py.test tests/workspace/test_builder.py

A single test inside it:

$ uv run py.test tests/test_config.py::test_export_json

Visual testing

Watch the suite build sessions in real time by keeping a client open in a second terminal.

Terminal 1 — start a server on the test socket:

$ tmux -L test_case

Terminal 2 — from the checkout, run the builder tests:

$ uv run py.test tests/workspace/test_builder.py

Terminal 1 flickers as sessions build before your eyes — the building tmuxp normally hides from users.

Documentation

Rebuild the docs whenever a source file changes:

$ just watch-docs

Or build once:

$ just build-docs

Serve the built docs locally:

$ just serve-docs

just dev-docs runs the watcher and the server together; just design-docs adds a static-file watch for theme work. docs/_build/ is generated — never hand-edit it.

After you set up your environment, load the project's own workspace from the checkout root to see a real multi-pane dev layout:

$ tmuxp load .

That loads .tmuxp.yaml at the project root.

Releasing

Never create tags. Never push tags. The owner handles tagging and tag pushes, because a tag triggers the publish workflow. See Release commits.

The full release process — updating CHANGES, bumping the version, tagging, and the CI publish to PyPI — is in Releasing.

Pull requests

One subject per pull request. Unrelated cleanup found along the way belongs in its own commit, and usually in its own pull request.

Discuss a substantial change via an issue before making it.

Run the gates above before opening a pull request; update documentation if your change affects the public interface. A pull request merges once it has the sign-off of one other developer — if you cannot merge it yourself, request a reviewer to do so.

Commit format is in WRITING.md.

Decorum

  • Participants will be tolerant of opposing views.
  • Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks.
  • When interpreting the words and actions of others, participants should always assume good intentions.
  • Behaviour which can be reasonably considered harassment will not be tolerated.

Based on Ruby's Community Conduct Guideline.

Security

Please do not open a public issue for a vulnerability. Use GitHub's private vulnerability reporting (the repository's Security tab → "Report a vulnerability"), or contact the maintainer listed in pyproject.toml.


Back | FazBrowse Home | New Git URL