Skip to main content

Customs

A Rust lint tool that enforces Python import boundaries declared in pyproject.toml.

[tool.customs]
src-roots = ["src", "."]
ignore = ["examples", "tests"]

[tool.customs.module.my-service]
module = "my_project.apps.service"

[tool.customs.module.libraries-utils]
module = "my_project.libraries.utils"
allow = ["$submodules", "my_project.apps.service"]

Install

uv tool install customs-lint
# or: pip install customs-lint
customs check .

The VS Code extension starts customs lsp from the selected Python interpreter (the same customs-lint install you use for CI) and lints the active Python file on edit.

Commands

  • customs check [paths...] — lint files; exit 0 if clean, 1 on violations, 2 on tool errors
  • customs lsp — Language Server Protocol server on stdio

Configuration

Put a [tool.customs] table in pyproject.toml. Customs walks up from each file to the nearest pyproject.toml.

src-roots (default ["."]) — directories relative to that file, used to map a path to a module. The longest matching root wins. With ["src", "."], src/foo/bar.py is foo.bar, not src.foo.bar.

ignore — module prefixes of importing files to skip. tests skips tests and tests.test_foo, but not testsuite.

[tool.customs.module.<rule-name>] — a named rule. <rule-name> is your label; it appears in diagnostics (for example [my-service]).

  • module (required) — the controlled package. Imports of this module and its submodules are restricted.
  • allow — who may import that tree. Each entry is a module prefix (the named module and its submodules). $submodules means the controlled module and its own children. If allow is omitted, it defaults to ["$submodules"]. If you set allow yourself, $submodules is not implied; list it to keep internal imports allowed.

Development

System dependencies

You must have the following dependencies installed:

  • Rust and Cargo — stable toolchain, rustc 1.77 or newer. Install via rustup.
  • uv — Python package and environment manager (installs Python 3.12 from .python-version when needed).
  • Node.js 20+ and npm to build the VS Code extension.

Run tests

From the repo root, with Rust and Cargo on PATH:

cargo test --workspace

This runs the customs-core unit tests and the customs check integration tests against tests/fixtures/. To run one crate or one test:

cargo test -p customs-core
cargo test -p customs
cargo test -p customs-core longest_prefix_wins

Set up a virtual environment and build the wheel

From the repo root (Rust/Cargo must be on PATH):

uv sync --group dev
uv build --out-dir target/wheels

uv sync creates .venv, installs the locked dev group (maturin), and builds/installs customs into that environment. Wheels from uv build are written to target/wheels/.

Install the wheel into another environment

The distribution name is customs-lint; the installed executable is customs.

uv pip install target/wheels/customs_lint-*.whl

Invoke the CLI

Once the wheel is installed and the environment is active:

customs check .
customs check path/to/file.py
customs lsp   # stdio language server

customs check exits 0 if clean, 1 if it found forbidden imports, and 2 on tool or config errors.

Build the VS Code extension

The extension does not bundle customs. Install customs-lint in the workspace interpreter (or set customs.path). From the repo root:

cd editors/vscode
npm ci
npm run package

npm run package runs vsce package and produces editors/vscode/customs-0.1.0.vsix (version from package.json).

For local iteration without a VSIX, run npm run compile in editors/vscode.

Install the VS Code extension

code --install-extension editors/vscode/customs-0.1.0.vsix

Or use the Command Palette: Extensions: Install from VSIX… and select the .vsix file.

After install, open a Python file and select the project interpreter (the Python extension is required). The extension runs customs lsp from that environment, or from customs.path if you set it. If customs is missing, it reports an error and asks you to install customs-lint in that environment (uv pip install customs-lint or pip install customs-lint).

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

customs_lint-0.1.0.tar.gz (22.3 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

customs_lint-0.1.0-py3-none-win_amd64.whl (1.6 MB view details)

Uploaded Python 3Windows x86-64

customs_lint-0.1.0-py3-none-manylinux_2_28_x86_64.whl (1.9 MB view details)

Uploaded Python 3manylinux: glibc 2.28+ x86-64

customs_lint-0.1.0-py3-none-manylinux_2_28_aarch64.whl (1.9 MB view details)

Uploaded Python 3manylinux: glibc 2.28+ ARM64

customs_lint-0.1.0-py3-none-macosx_11_0_arm64.whl (1.7 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

customs_lint-0.1.0-py3-none-macosx_10_12_x86_64.whl (1.8 MB view details)

Uploaded Python 3macOS 10.12+ x86-64

File details

Details for the file customs_lint-0.1.0.tar.gz.

File metadata

  • Download URL: customs_lint-0.1.0.tar.gz
  • Upload date:
  • Size: 22.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.22

File hashes

Hashes for customs_lint-0.1.0.tar.gz
Algorithm Hash digest
SHA256 2ec71191a38452ab7a9ee93375e877f20d924f08b0921ea7d82e9d97a22168f6
MD5 298e35375fcd4254b53aba7c4a19b10a
BLAKE2b-256 582f7a6dd51beb8a60e7e00c27a113698caf14bcd6c9570c5d7459cf18d97c24

See more details on using hashes here.

File details

Details for the file customs_lint-0.1.0-py3-none-win_amd64.whl.

File metadata

File hashes

Hashes for customs_lint-0.1.0-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 7cd01f2a602c58a4c3f6528aeff21df4955806916c36c5086a766a6e34361c68
MD5 f0bff9d48fdc7a8674b00d30b344e65b
BLAKE2b-256 711eaab7123d6dee50df80d763f765142684f5ba8333a32d8ee81ae79fd9caeb

See more details on using hashes here.

File details

Details for the file customs_lint-0.1.0-py3-none-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for customs_lint-0.1.0-py3-none-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 99485fb72f092a8395deb4192582b53f8e86f8dbf1341a5dad82949de3c39ee6
MD5 2e9f3e64e53f94258ca242066e6a5a8b
BLAKE2b-256 8178f17e24636dddd022ef82858ac88695ecd4b256443021a52b99165652f317

See more details on using hashes here.

File details

Details for the file customs_lint-0.1.0-py3-none-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for customs_lint-0.1.0-py3-none-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 b9f2cc809915a8ad4de0dc569d3ee7555c6949649e7e87d3a60c5c78fcbff3c5
MD5 e087765a9a6312591d4c75c8c282c400
BLAKE2b-256 7bdcbc060d97b5765a87c86e83168d33f470f44bfcf9ea65a3e8b4faabd36b5d

See more details on using hashes here.

File details

Details for the file customs_lint-0.1.0-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for customs_lint-0.1.0-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 ff74e914fddb239bddffa247c68e15b3ba0d7a21d5e631a79d9dcf06a9110d9d
MD5 00fa40b37473e423c563db84e9a6ba6e
BLAKE2b-256 776ce44f0e4e12ba77565cd955e8cf50548b4e93f17c755404aaeb9b72b53e58

See more details on using hashes here.

File details

Details for the file customs_lint-0.1.0-py3-none-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for customs_lint-0.1.0-py3-none-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 04870b8588d55ad9d97aae6458f867428a02588a01a0d2c95f07e9c6b20aebff
MD5 460a03551dea0be9750adf8016c11e67
BLAKE2b-256 63b2902c103695e99a7ccc775e566f90e83603461d295deeae3cf902257b2c72

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page