Developing and Testing

Our tests are inside tests/. Tests are implemented using pytest.

make test will create a tmux server on a separate socket_name using $ tmux -L test_case.

Install the latest code from git

Get the source

To begin developing, check out the code from github:

$ git clone [email protected]:tmux-python/tmuxp.git
$ cd tmuxp

Bootstrap

The easiest way to configure a dev environment is through uv. This automatically will manage virtualenv and python dependencies for tmuxp. For information on installing uv visit the uv’s documentation.

To begin developing, check out the code from github:

$ git clone [email protected]:tmux-python/tmuxp.git
$ cd tmuxp

You can create a virtualenv, and install all of the locked packages as listed in uv.lock:

$ uv sync --all-extras --dev

If you ever need to update packages during your development session, the following command can be used to update all packages as per uv settings:

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

Then before any python command in tty / terminal session, run with:

$ uv run [command]

That is it! You are now ready to code!

Advanced: Manual virtualenv

Now create a virtualenv, if you don’t know how to, you can create a virtualenv with:

$ virtualenv .venv

Then activate it to your current tty / terminal session with:

$ source .venv/bin/activate

Good! Now let’s run this:

$ pip install -e .

This has pip, a python package manager install the python package in the current directory. -e means --editable, which means you can adjust the code and the installed software will reflect the changes.

$ tmuxp

Test Runner

pytest is used for tests.

As you seen above, the tmuxp command will now be available to you, since you are in the virtual environment, your {}PATH environment was updated to include a special version of python inside your .venv folder with its own packages.

Rerun on file change

via pytest-watcher (works out of the box):

$ make start

via entr(1) (requires installation):

$ make watch_test

Manual (just the command, please)

$ uv run py.test

or:

$ make test

pytest options

PYTEST_ADDOPTS can be set in the commands below. For more information read docs.pytest.com for the latest documentation.

Verbose:

$ env PYTEST_ADDOPTS="-verbose" make start

Pick a file:

$ env PYTEST_ADDOPTS="tests/workspace/test_builder.py" uv run make start

Drop into test_automatic_rename_option() in tests/workspace/test_builder.py:

$ env PYTEST_ADDOPTS="-s -x -vv tests/workspace/test_builder.py" uv run make start

Drop into test_automatic_rename_option() in tests/workspace/test_builder.py and stop on first error:

$ env PYTEST_ADDOPTS="-s -x -vv tests/workspace/test_builder.py::test_automatic_rename_option" uv run make start

Drop into pdb on first error:

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

If you have ipython installed:

$ env PYTEST_ADDOPTS="--pdbcls=IPython.terminal.debugger:TerminalPdb" make start
$ make test

You probably didn’t see anything but tests scroll by.

If you found a problem or are trying to write a test, you can file an issue on github.

Manual invocation

Test only a file:

$ py.test tests/test_config.py

will test the tests/test_config.py tests.

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

tests test_export_json inside of tests/test_config.py.

Multiple can be separated by spaces:

$ py.test tests/test_{window,pane}.py tests/test_config.py::test_export_json

Visual testing

You can watch tmux testsuite build sessions visually by keeping a client open in a separate terminal.

Create two terminals:

  • Terminal 1: $ tmux -L test_case

  • Terminal 2: $ cd into the tmuxp project and into the virtualenv if you are using one, see details on installing the dev version of tmuxp above. Then:

    $ py.test tests/workspace/test_builder.py
    

Terminal 1 should have flickered and built the session before your eyes. tmuxp hides this building from normal users.

Run tests on save (old method)

You can re-run tests automatically on file edit.

Note

This requires entr(1).

Install entr. Packages are available available on most Linux and BSD variants, including Debian, Ubuntu, FreeBSD, OS X.

To run all tests upon editing any .py file:

$ make watch_test

You can also re-run a specific test file or any other py.test usage argument:

$ make watch_test test=tests/test_config.py
$ make watch_test test='-x tests/test_config.py tests/test_util.py'

Testing options

RETRY_TIMEOUT_SECONDS can be toggled if certain workspace builder tests are being stubborn.

e.g. RETRY_TIMEOUT_SECONDS=10 py.test

name: tests

on: [push, pull_request]

jobs:
  build:
    # Don't run twice for internal PRs from our own repo
    if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository

    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ['3.13']
        tmux-version: ['2.6', '2.7', '2.8', '3.0a', '3.1b', '3.2a', '3.3a', '3.4', '3.5', 'master']
        # balance ci coverage across supported python/tmux versions with CI speed
        include:
          - python-version: '3.9'
            tmux-version: '2.6'
          - python-version: '3.9'
            tmux-version: 'master'
    steps:
      - uses: actions/checkout@v4

      - name: Install uv
        uses: astral-sh/setup-uv@v5
        with:
          enable-cache: true

      - name: Set up Python ${{ matrix.python-version }}
        run: uv python install ${{ matrix.python-version }}

      - name: Install dependencies
        run: uv sync --all-extras --dev

      - name: Setup tmux build cache for tmux ${{ matrix.tmux-version }}
        id: tmux-build-cache
        uses: actions/cache@v4
        with:
          path: ~/tmux-builds/tmux-${{ matrix.tmux-version }}
          key: tmux-${{ matrix.tmux-version }}

      - name: Build tmux ${{ matrix.tmux-version }}
        if: steps.tmux-build-cache.outputs.cache-hit != 'true'
        run: |
          sudo apt install libevent-dev libncurses5-dev libtinfo-dev libutempter-dev bison
          mkdir ~/tmux-builds
          mkdir ~/tmux-src
          git clone https://github.com/tmux/tmux.git ~/tmux-src/tmux-${{ matrix.tmux-version }}
          cd ~/tmux-src/tmux-${{ matrix.tmux-version }}
          git checkout ${{ matrix.tmux-version }}
          sh autogen.sh
          ./configure --prefix=$HOME/tmux-builds/tmux-${{ matrix.tmux-version }} && make && make install
          export PATH=$HOME/tmux-builds/tmux-${{ matrix.tmux-version }}/bin:$PATH
          cd ~
          tmux -V

      - name: Lint with ruff check .
        run: uv run ruff check .

      - name: Format with ruff
        run: uv run ruff format . --check

      - name: Lint with mypy
        run: uv run mypy .

      - name: Print python versions
        run: |
          python -V
          uv run python -V

      - name: Test with pytest
        continue-on-error: ${{ matrix.tmux-version == 'master' }}
        run: |
          sudo apt install libevent-2.1-7
          export PATH=$HOME/tmux-builds/tmux-${{ matrix.tmux-version }}/bin:$PATH
          ls $HOME/tmux-builds/tmux-${{ matrix.tmux-version }}/bin
          tmux -V
          uv run py.test --cov=./ --cov-report=xml

      - uses: codecov/codecov-action@v5
        with:
          token: ${{ secrets.CODECOV_TOKEN }}

  release:
    runs-on: ubuntu-latest
    needs: build
    if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')

    strategy:
      matrix:
        python-version: ['3.13']

    steps:
      - uses: actions/checkout@v4

      - name: Install uv
        uses: astral-sh/setup-uv@v5
        with:
          enable-cache: true

      - name: Set up Python ${{ matrix.python-version }}
        run: uv python install ${{ matrix.python-version }}

      - name: Install dependencies
        run: uv sync --all-extras --dev

      - name: Build package
        run: uv build

      - name: Publish package
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          user: __token__
          password: ${{ secrets.PYPI_API_TOKEN }}
          skip_existing: true

Documentation

Rebuild on save

Rebuild the documentation when an .md file is edited:

$ cd doc
$ make watch
$ make SPHINXBUILD='uv run sphinx-build' watch

tmuxp developer config

_images/tmuxp-dev-screenshot.png

After you Install the latest code from git, when inside the tmuxp checkout:

$ tmuxp load .

this will load the .tmuxp.yaml in the root of the project.

session_name: tmuxp
start_directory: ./ # load session relative to config location (project root).
shell_command_before:
- uv virtualenv --quiet > /dev/null 2>&1 && clear
windows:
- window_name: tmuxp
  focus: True
  layout: main-horizontal
  options:
    main-pane-height: 67%
  panes:
  - focus: true
  - pane 
  - make watch_mypy
  - make watch_test
- window_name: docs
  layout: main-horizontal
  options:
    main-pane-height: 67%
  start_directory: docs/
  panes:
  - focus: true
  - pane
  - pane
  - make start

Formatting

ruff

The project uses ruff to handle formatting, sorting imports and linting.

uv:

$ uv run ruff

If you setup manually:

$ ruff check .
$ make ruff
$ make watch_ruff

requires entr(1).

uv:

$ uv run ruff check . --fix

If you setup manually:

$ ruff check . --fix

ruff format

ruff format is used for formatting.

uv:

$ uv run ruff format .

If you setup manually:

$ ruff format .
$ make ruff_format

mypy

mypy is used for static type checking.

uv:

$ uv run mypy .

If you setup manually:

$ mypy .
$ make mypy
$ make watch_mypy

requires entr(1).

Continuous integration

Github Actions

tmuxp uses github actions for continuous integration / automatic unit testing.

To view the tmux and python versions tested see the .github/workflows/tests.yml. Builds are done on master and pull requests and can be viewed on the gh build site.