Skip to content
View as Markdown

Use configuration profiles

This how-to shows how to adopt the bundled profiles, compose several, point at a local .toml, and read the merged result.

A profile is a named bundle of LaNorme settings you adopt with the extends key, instead of copying the same toggles into every project. Use a profile to turn on a coherent set of checks in one line, then override individual keys locally where a project differs. For the full meaning of every key a profile can set, see the configuration reference.

Adopt a single profile

Add extends to the [tool.lanorme] table in pyproject.toml:

[tool.lanorme]
extends = ["strict"]

In a standalone lanorme.toml or .lanorme.toml, write the same keys at the top level with no [tool.lanorme] header; a header there is silently ignored.

extends takes one profile name or a list. A single name may also be given as a bare string (extends = "strict").

The bundled profiles

Four profiles ship inside the package. One tightens severity, three configure an architecture style.

Profile What it does
strict Turns on every check that ships with its enabled switch off and sets promote = ["ALL"], so all advisory warnings become build-failing errors.
hexagonal Sets layer_deps to a four-layer ports-and-adapters layout with explicit composition-root globs, and points port_coverage at infrastructure/ as the adapter root (the built-in default is infrastructure/services).
clean Configures layer_deps for Clean Architecture's four layers (entities, use_cases, interface_adapters, frameworks).
layered Configures layer_deps for classic N-tier layers (presentation, business, persistence).

The default-off checks strict enables are named_args, test_style, attribute_access, restating, similarity, prose, docstrings, naming_scope, suppressions, docs and naming_clean_code. Two of those carry assumptions worth knowing before you adopt it:

  • suppressions starts with a budget of zero, so any existing # noqa fails the build until you set max_total to today's count and ratchet it down. The SUPPRESS codes cannot be silenced inline, only in config.
  • docs expects the Markdown under docs/ to follow a Diataxis layout, and is inert when that directory is absent. Set docs_root, sections or known_top_level where your tree differs.

Any one of them can be switched back off with a local table such as [tool.lanorme.docs] enabled = false, which wins over the profile because tables merge key by key.

It does not add architecture rules. layer_deps and port_coverage have no enabled switch and always run with their built-in hexagonal layout (domain, application, infrastructure, api; ports under application/ports, adapters under infrastructure/services), which only bites on a tree that uses those directory names. Pick hexagonal, clean or layered to set the layout your tree uses, and compose it with strict if you want both.

The architecture profiles are mutually exclusive in practice: each configures layer_deps for a different layer layout, so extend exactly one of them.

Compose several profiles

List more than one name to merge them. The common pairing is strict severity plus an architecture style:

[tool.lanorme]
extends = ["strict", "hexagonal"]

This gives you every default-off check, promote = ["ALL"], the hexagonal layer rules and adapter roots, all from two profile names.

Extend a local .toml

An extends entry that ends in .toml, or contains a path separator, is read as a file relative to the project root instead of a bundled name. Use this to share one team standard across repositories:

[tool.lanorme]
extends = ["strict", "team-rules.toml"]

The file holds a bare [tool.lanorme]-style body (the keys directly, with no wrapping table):

# team-rules.toml
promote = ["TYPE-004"]

[similarity]
enabled = true

An extends key inside a profile is ignored, so profiles do not chain.

How merging works

Profiles merge left to right, then your own keys merge on top, so a local key always wins. Tables merge per key: a local [tool.lanorme.layer_deps] that sets only transport_layers overrides that one key and leaves the rest of the profile's layer_deps table intact.

flowchart LR
    A["First profile<br/>(leftmost)"] --> B["Next profile<br/>(further right wins ties)"]
    B --> C["Your [tool.lanorme]<br/>local keys (always win)"]
    C --> D[Effective config]

Order matters when two profiles set the same key. In extends = ["strict", "team-rules.toml"] above, team-rules.toml is to the right of strict, so its promote = ["TYPE-004"] replaces strict's promote = ["ALL"]. Put the profile whose value should win further right, or set the key in your own [tool.lanorme] table to beat every profile.

Confirm the merged result

Run check with --show-config to print the discovered config and the effective per-check settings, then exit without running any check. Given this project config:

[tool.lanorme]
extends = ["strict", "hexagonal"]
ignore = ["NAMING-003"]

[tool.lanorme.prose]
enabled = false

the command reports the merged outcome:

$ lanorme check src --show-config
config file:  /path/to/project/pyproject.toml [tool.lanorme]
project root: /path/to/project

[tool.lanorme]
  ignore = ['NAMING-003']
  promote = ['ALL']

checks (effective settings):
  ...
  layer_deps         source_root='' layers=<4 items> transport_layers=('api',) allowed_imports=<4 keys> composition_root=<4 items>
  ...
  port_coverage      source_root='' ports_dir='application/ports' adapter_roots=('infrastructure',) composition_root=<4 items> ...
  prose              enabled=False ...   (opt-in, not enabled)
  ...

Read it as the merge in action:

  • promote = ['ALL'] comes from strict.
  • layer_deps and port_coverage are configured by hexagonal.
  • ignore comes from the local table.
  • prose is enabled=False: strict turns prose on, but the local [tool.lanorme.prose] table wins.

The config file and project root paths vary per machine.

Note

An unknown profile name is a configuration error. lanorme check prints the available bundled names and exits with code 2 (usage or config error), the same exit code as a config file that is not valid TOML, an invalid config value, or a missing local profile file.

Command-line flags still override

Command-line flags override config, including anything a profile sets. To include or skip a category for one run without editing the profile, pass --select or --ignore:

$ lanorme check src --ignore PORT

The --promote flag does the same for severity, so you can escalate or hold back warnings for one run regardless of the profile's promote value.

  • Configuration reference for every [tool.lanorme] key, including extends, promote and baseline.
  • Rule reference for what each check flags and the per-check settings a profile configures.

A profile sets policy, not adoption pace. To roll a strict profile onto an existing codebase without drowning in pre-existing findings, pair it with a baseline: record current findings with lanorme baseline write, set the baseline key, and only new findings report. See the baseline entry in the configuration reference.