Aside from running tach mod and tach sync, you can configure Tach by creating or modifying the configuration file as described below.

tach.toml

This is the project-level configuration file which should be in the root of your project.

modules defines the modules in your project, and accepts the keys described below.

exclude accepts a list of directory patterns to exclude from checking. These are treated as regex/glob paths (depending on use_regex_matching) which match from the beginning of a given file path. For example: when using glob matching project/*.tests would match any path beginning with project/ and ending with .tests.

ignore_type_checking_imports is a boolean which, when enabled, silences tach check failures caused by imports under a TYPE_CHECKING conditional block

exact is a boolean which causes tach check to fail if any declared dependencies are found to be unused

forbid_circular_dependencies is a boolean which, when enabled, causes tach check to fail if any circular dependencies are detected

use_regex_matching is a boolean which, when enabled, uses regex (default) matching to exclude patterns else uses globs matching

exclude = [
    ".*__pycache__",
    "build/",
    "dist/",
    "docs/",
    "python/tests/",
    "tach.egg-info/",
    "venv/",
]
source_roots = ["python"]
exact = true
ignore_type_checking_imports = true
forbid_circular_dependencies = true

[[modules]]
path = "tach"
depends_on = []
strict = true

[[modules]]
path = "tach.__main__"
depends_on = [{ path = "tach.start" }]
strict = true

[[modules]]
path = "tach.cache"
depends_on = [{ path = "tach" }, { path = "tach.filesystem" }]
strict = true

[[modules]]
path = "tach.check"
depends_on = [
    { path = "tach.errors" },
    { path = "tach.extension" },
    { path = "tach.filesystem" },
    { path = "tach.parsing" },
]
strict = true

...

[cache]
file_dependencies = ["python/tests/**", "src/*.rs"]

[external]
exclude = ["pytest"]

Modules

Each module listed under the modules key above can accept the following attributes:

  • path: the Python import path to the module (e.g. a.b for <root>/a/b.py)
  • depends_on: a list of the other modules which the module is allowed to import from, using their ‘paths’ to identify them
  • strict: enables strict mode for the module

Tach also supports deprecating individual dependencies.

Source Roots

The source_roots key is required for Tach to understand the imports within your project. If it is not set explicitly, source_roots defaults to your project root path: ['.']. This means Tach will expect that your Python imports are resolved relative to the directory in which tach.toml exists.

Below are typical cases in which modifying source_roots is necessary.

Example: Python below project root

Suppose your repository contains a subfolder where all of your Python code lives. This could be a web server, a collection of serverless functions, or even utility scripts. In this example we will assume the Python code in our repo lives in the backend/ folder.

my_repo/
  tach.toml
  backend/
    module1.py
    module2/
      __init__.py
      service.py
    module3.py
  docs/
  tests/

In a Python module such as backend/module1.py, we can see imports from other modules.

# In backend/module1.py

import module3
from module2.service import MyService

Notice that these import paths (module3, module2.service.MyService) are rooted in the backend/ folder, NOT the project root.

To indicate this structure to Tach, set:

source_roots = ["backend"]

in your tach.toml, or use tach mod and mark the backend folder as the only source root.

Example: Monorepo

Suppose you work on a ‘monorepo’, in which Python packages which import from each other are located in distinct project directories. You may package your utility libraries in a utilityfolder, while your core packages live in core_one and core_two. You may also use a namespace package to share a common top-level namespace. In this example we’ll use myorg as the namespace package.

The file tree in a case like this might look like:

my_repo/
  tach.toml
  utility/
    pyproject.toml
    myorg/
      utils/
        __init__.py
  core_one/
    pyproject.toml
    myorg/
      core_one/
        __init__.py
        module1.py
        module2/
          __init__.py
          service.py
        module3.py
  core_two/
    pyproject.toml
    myorg/
      core_two/
        __init__.py
        module1.py
        module2/
          __init__.py
          service.py
        module3.py
  docs/
  tests/

In a Python module such as core_one/myorg/core_one/module1.py, there may be imports from other packages:

# In core_one/myorg/core_one/module1.py

from myorg.utils import utility_fn

Notice that this import path (myorg.utils.utility_fn) is rooted in the utility folder, NOT the project root.

To indicate the project structure to Tach, you would set:

source_roots = [
  "utility",
  "core_one",
  "core_two"
]

in your tach.toml, or use tach mod and mark the same folders as source root.

In tach.toml, each entry in source_roots is interpreted as a relative path from the project root.

Cache

Tach allows configuration of the computation cache it uses to speed up tasks like testing.

The file_dependencies key accepts a list of glob patterns to indicate additional file contents that should be considered when checking for cache hits. This should typically include files outside of your source roots which affect your project’s behavior under test, including the tests themselves. Additionally, if you have non-Python files which affect your project’s behavior (such as Rust or C extensions), these should be included as well.

The env_dependencies key accepts a list of environment variable names whose values affect your project’s behavior under test. This may include a DEBUG flag, or database connection parameters in the case of tests which use a configurable database.

External Checks

When running check-external, Tach allows excluding certain modules from validation.

Adding the top level module name to the exclude key (underneath the external key) will allow all usages of the corresponding module.