Metadata-Version: 2.4
Name: tox-toml-fmt
Version: 1.6.0
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Dist: toml-fmt-common
License-File: LICENSE.txt
Summary: Format your pyproject.toml file
Keywords: format,pyproject
Author-email: Bernat Gabor <gaborjbernat@gmail.com>
Requires-Python: >=3.10
Description-Content-Type: text/x-rst
Project-URL: Bug Tracker, https://github.com/tox-dev/toml-fmt/issues
Project-URL: Changelog, https://github.com/tox-dev/toml-fmt/blob/main/tox-toml-fmt/CHANGELOG.md
Project-URL: Documentation, https://tox-toml-fmt.readthedocs.io/en/latest/
Project-URL: Source Code, https://github.com/tox-dev/toml-fmt/tree/main/tox-toml-fmt

Overview
========

Apply a consistent format to your ``tox.toml`` file with comment support. See
`changelog here <https://github.com/tox-dev/toml-fmt/blob/main/tox-toml-fmt/CHANGELOG.md>`_.


Recent Changes
~~~~~~~~~~~~~~~~

- 🐛 fix(key): normalize literal key quotes to basic (#217) by `@gaborbernat <https://github.com/gaborbernat>`_ in
  `#219 <https://github.com/tox-dev/toml-fmt/pull/219>`_
- Update Rust dependencies by `@gaborbernat <https://github.com/gaborbernat>`_ in
  `#218 <https://github.com/tox-dev/toml-fmt/pull/218>`_
- ✨ feat(string): add skip_wrap_for_keys config to preserve specific strings by
  `@gaborbernat <https://github.com/gaborbernat>`_ in `#216 <https://github.com/tox-dev/toml-fmt/pull/216>`_
- 🐛 fix(table): normalize quote styles in key sorting by `@gaborbernat <https://github.com/gaborbernat>`_ in
  `#215 <https://github.com/tox-dev/toml-fmt/pull/215>`_
- Update Python dependencies by `@gaborbernat <https://github.com/gaborbernat>`_ in
  `#210 <https://github.com/tox-dev/toml-fmt/pull/210>`_
- Update Rust dependencies by `@gaborbernat <https://github.com/gaborbernat>`_ in
  `#209 <https://github.com/tox-dev/toml-fmt/pull/209>`_ <a id="1.5.4"></a>

Philosophy
----------
This tool aims to be an *opinionated formatter*, with similar objectives to `black <https://github.com/psf/black>`_.
This means it deliberately does not support a wide variety of configuration settings. In return, you get consistency,
predictability, and smaller diffs.

Use
---

Via ``CLI``
~~~~~~~~~~~

`tox-toml-fmt <https://pypi.org/project/tox-toml-fmt>`_ is a CLI tool that needs a Python interpreter (version 3.10 or higher) to run. We recommend
either `pipx <https://pypi.org/project/pipx>`_ or `uv <https://pypi.org/project/uv>`_ to install tox-toml-fmt into an isolated environment. This has the added benefit that
later you will be able to upgrade tox-toml-fmt without affecting other parts of the system. We provide a method for
``pip`` too here, but we discourage that path if you can:


    .. code-block:: bash

        # install uv per https://docs.astral.sh/uv/#getting-started
        uv tool install tox-toml-fmt
        tox-toml-fmt --help


Via ``pre-commit`` hook
~~~~~~~~~~~~~~~~~~~~~~~

See `pre-commit/pre-commit <https://github.com/pre-commit/pre-commit>`_ for instructions, sample ``.pre-commit-config.yaml``:

.. code-block:: yaml

    - repo: https://github.com/tox-dev/tox-toml-fmt
      rev: "v1.0.0"
      hooks:
        - id: tox-toml-fmt

Via Python
~~~~~~~~~~

You can use ``tox-toml-fmt`` as a Python module to format TOML content programmatically.

.. code-block:: python

    from tox_toml_fmt import run

    # Format a tox.toml file and return the exit code
    exit_code = run(["path/to/tox.toml"])

The ``run`` function accepts command-line arguments as a list and returns an exit code (0 for success, non-zero for
failure).


The ``[tox-toml-fmt]`` table is used when present in the ``tox.toml`` file:

.. code-block:: toml

    [tox-toml-fmt]

    # After how many columns split arrays/dicts into multiple lines (1 forces always)
    column_width = 120

    # Number of spaces for indentation
    indent = 2

If not set they will default to values from the CLI. The example above shows the defaults.

``tox-toml-fmt`` is an opinionated formatter, much like `black <https://github.com/psf/black>`_ is for Python code.
The tool intentionally provides minimal configuration options because the goal is to establish a single standard format
that all ``tox.toml`` files follow.

**Benefits of this approach:**

- Less time configuring tools
- Smaller diffs when committing changes
- Easier code reviews since formatting is never a question

While a few key options exist (``column_width``, ``indent``, ``table_format``), the tool does not expose dozens of
toggles. You get what the maintainers have chosen to be the right balance of readability, consistency, and usability.

General Formatting
------------------

These rules apply uniformly across the entire ``tox.toml`` file.

String Quotes
~~~~~~~~~~~~~

All strings use double quotes by default. Single quotes are only used when the value contains double quotes:

.. code-block:: toml

    # Before
    description = 'Run tests'
    commands = ["echo \"hello\""]

    # After
    description = "Run tests"
    commands = ['echo "hello"']

Key Quotes
~~~~~~~~~~

TOML keys using single-quoted (literal) strings are normalized to double-quoted (basic) strings with proper escaping.
This ensures consistent formatting and deterministic key sorting regardless of the original quote style:

.. code-block:: toml

    # Before
    [env.'my-env']
    deps = ["pytest"]

    # After
    [env."my-env"]
    deps = ["pytest"]

Backslashes and double quotes within literal keys are escaped during conversion.

Array Formatting
~~~~~~~~~~~~~~~~

Arrays are formatted based on line length, trailing comma presence, and comments:

.. code-block:: toml

    # Short arrays stay on one line
    env_list = ["py312", "py313", "lint"]

    # Long arrays that exceed column_width are expanded and get a trailing comma
    deps = [
        "pytest>=7",
        "pytest-cov>=4",
        "pytest-mock>=3",
    ]

    # Trailing commas signal intent to keep multiline format
    deps = [
        "pytest>=7",
    ]

    # Arrays with comments are always multiline
    deps = [
        "pytest>=7",  # testing framework
        "coverage>=7",
    ]

**Multiline formatting rules:**

An array becomes multiline when any of these conditions are met:

1. **Trailing comma present** - A trailing comma signals intent to keep multiline format
2. **Exceeds column width** - Arrays longer than ``column_width`` are expanded (and get a trailing comma added)
3. **Contains comments** - Arrays with inline or leading comments are always multiline

String Wrapping
~~~~~~~~~~~~~~~

Long strings that exceed ``column_width`` are wrapped using TOML multiline basic strings with line-ending backslashes:

.. code-block:: toml

    # Before
    description = "A very long description string that exceeds the column width limit set for this project"

    # After (with column_width = 40)
    description = """\
      A very long description \
      string that exceeds the \
      column width limit set \
      for this project\
      """

Specific keys can be excluded from wrapping using ``skip_wrap_for_keys``. Patterns support wildcards
(e.g. ``*.commands`` skips wrapping for ``commands`` under any table).

Table Formatting
~~~~~~~~~~~~~~~~

Sub-tables can be formatted in two styles controlled by ``table_format``:

**Short format** (default, collapsed to dotted keys):

.. code-block:: toml

    [env.test]
    description = "run tests"
    sub.value = 1

**Long format** (expanded to table headers):

.. code-block:: toml

    [env.test]
    description = "run tests"

    [env.test.sub]
    value = 1

Individual tables can override the default using ``expand_tables`` and ``collapse_tables``.

Comment Preservation
~~~~~~~~~~~~~~~~~~~~

All comments are preserved during formatting:

- **Inline comments** - Comments after a value on the same line stay with that value
- **Leading comments** - Comments on the line before an entry stay with the entry below
- **Block comments** - Multi-line comment blocks are preserved

**Inline comment alignment:**

Inline comments within arrays are aligned independently per array, based on that array's longest value:

.. code-block:: toml

    # Before - comments at inconsistent positions
    deps = [
      "pytest", # testing
      "pytest-cov",  # coverage
      "pytest-mock", # mocking
    ]

    # After - comments align to longest value in this array
    deps = [
      "pytest",       # testing
      "pytest-cov",   # coverage
      "pytest-mock",  # mocking
    ]

Table-Specific Handling
-----------------------

Table Ordering
~~~~~~~~~~~~~~

Tables are reordered into a consistent structure:

1. Root-level keys (``env_list``, ``min_version``, ``skip_missing_interpreters``)
2. ``[env_run_base]``
3. ``[env.NAME]`` sections ordered by ``env_list`` if specified
4. Any remaining ``[env.*]`` sections not in ``env_list``

.. code-block:: toml

    # env_list determines the order of [env.*] sections
    env_list = ["lint", "type", "py312", "py313"]

    [env_run_base]
    deps = ["pytest>=7"]

    # Environments appear in env_list order:
    [env.lint]
    # ...

    [env.type]
    # ...

    [env.py312]
    # ...

    [env.py313]
    # ...

Environments not listed in ``env_list`` are placed at the end.

Other Tables
~~~~~~~~~~~~

Any unrecognized tables are preserved and reordered according to standard table ordering rules. Keys within tables
are not reordered.
