Metadata-Version: 2.4
Name: pylint-tyrant
Version: 0.9.0
Summary: Pylint plugin with documentation and architecture rules.
Author-email: otakutyrant <otakutyrant@gmail.com>
License-Expression: LicenseRef-Proprietary
Project-URL: Homepage, https://github.com/otakutyrant/tyrant-rules/tree/main/implementations/pylint-tyrant
Project-URL: Repository, https://github.com/otakutyrant/tyrant-rules
Project-URL: Issues, https://github.com/otakutyrant/tyrant-rules/issues
Requires-Python: >=3.13
Description-Content-Type: text/markdown
License-File: LICENSE.txt
Requires-Dist: pylint>=4.0.0
Requires-Dist: tomli>=2.0.1; python_version < "3.11"
Dynamic: license-file

# pylint-tyrant

Pylint plugin with documentation and architecture rules inspired by
`eslint-plugin-tyrant`.

In rule names, `doc` means Python docstrings.

The plugin currently ships these rules:

- `enforce-doc-tag-order`
- `enforce-package-layer-dependencies`
- `no-direct-error-instantiation`
- `no-test-only-exports`
- `require-doc-for-public-api`
- `require-file-doc`
- `require-empty-line-after-file-doc`
- `prefer-single-line-doc`
- `prefer-directory-modules-for-siblings`
- `restrict-relative-imports-to-base-module`

### `no-test-only-exports`

Reports public module-level identifiers that have no imports from production
files. Imports from test files and uses within the declaring module do not make
an identifier part of the production API.

Names beginning with `_` or `__` are private and are not checked. Prefer an `_`
prefix for implementation details that do not need to be imported by production
code. If a public identifier is complex enough to require direct tests, suppress
the message for that declaration with a Pylint disable comment.

### `enforce-package-layer-dependencies`

Organizes same-package modules into shared, feature, and entry layers declared
in the entry-file docstring, allowing only downward coupling.

This rule uses `--tyrant-shared-modules`. `__init__.py` is treated as the exact entry file automatically, configured same-directory module names such as `base` or `types.py` are treated as shared low-level modules, and every other source file or importable directory in the directory is treated as a feature module.

Prefix inheritance is disabled by default. Set
`--tyrant-inherit-layers-from-module-prefixes=y` to let dotted-qualified
modules inherit the layer of their first-component base module. For example,
`bbb.unit.test.py` and `bbb.integration.test.py` then inherit the layer
declared by `@module bbb.py`, so they do not need to be listed separately and
must not be listed separately. They cannot import higher layers. Qualified
configured shared modules such as
`types.unit.test.py` inherit the shared layer from
`--tyrant-shared-modules=types.py`. The entry layer is always the exact
`__init__.py` entry file.

Use `@module` for one ordered module layer and `@module-group` for one ordered flat peer layer:

```python
"""Package docs.

@module materials.py - Core data.
@module-group paragraphs.py, lexicons.py - Peer modules derived from the same inputs.
@module metrics.py - Aggregates.
"""
```

Every feature module must be listed exactly once. Missing modules, extra listed modules, repeated modules, and upward imports are violations.
Every `@module` and `@module-group` tag must also include a non-empty relationship description.
When declaring feature modules or configuring `--tyrant-shared-modules`, names with `.py` are source file modules; names without extensions are directory modules. A package may not contain both a source file and an importable directory with the same basename.

```sh
pylint \
  --load-plugins=pylint_tyrant \
  --enable=enforce-package-layer-dependencies \
  --tyrant-shared-modules=base,types.py \
  path/to/your_package
```

Add `--tyrant-inherit-layers-from-module-prefixes=y` to that command only when
prefix inheritance is wanted.

### `require-doc-for-public-api`

Makes public classes, functions, and methods self-describing with docstrings,
so consumers need not read the implementation.

This rule currently treats names that do not start with `_` as public. It checks:

- top-level public classes
- top-level public functions
- public methods on public classes

Valid:

```python
class UserService:
    """Load users."""

    def load_user(self) -> None:
        """Load one user."""
```

Also valid:

```python
def load_user() -> None:
    """Load one user."""
```

Invalid:

```python
def load_user() -> None:
    pass
```

### `enforce-doc-tag-order`

Makes recognized docstring tags follow one configured order, so the order
stays the single source of truth for tag sequence.

This rule reads tags written as `@tag - value` from module, class, and function docstrings. It only checks tags listed in `tyrant-doc-tag-order`.

Configure it in `pyproject.toml` like this:

```toml
[tool.pylint.main]
load-plugins = ["pylint_tyrant"]

[tool.pylint."messages control"]
enable = ["enforce-doc-tag-order"]

[tool.pylint.tyrant-doc-tag-order]
tyrant-doc-tag-order = ["@remarks", "@param", "@returns"]
```

Command-line equivalent:

```sh
pylint \
  --load-plugins=pylint_tyrant \
  --enable=enforce-doc-tag-order \
  --tyrant-doc-tag-order=@remarks,@param,@returns \
  path/to/your_package
```

Valid:

```python
def load_user() -> User:
    """Load one user.

    @remarks - Used by the public API.
    @param - User id.
    @returns - Loaded user.
    """
```

Invalid:

```python
def load_user() -> User:
    """Load one user.

    @returns - Loaded user.
    @param - User id.
    """
```
