# core/producers/tool.pylint.producer.ts

> 85 lines of code and 19 definitions.

Tree: GovLab Quality
Language: typescript
Layer: application
Canonical: https://banes-lab.com/anatomy/quality#file-quality-core-producers-tool-pylint-producer-ts
Source text: https://banes-lab.com/source/quality/core/producers/tool.pylint.producer.ts.txt

Listed in [core/producers](https://banes-lab.com/api/source/quality/core/producers.md), after [core/producers/tool.prettier.producer.ts](https://banes-lab.com/source/quality/core/producers/tool.prettier.producer.ts.md) and before [core/producers/tool.revive.producer.ts](https://banes-lab.com/source/quality/core/producers/tool.revive.producer.ts.md).

## Definitions

- `headerOf` (lexical_declaration, line 25)
- `messagesOf` (lexical_declaration, line 38)
- `produce` (lexical_declaration, line 75)
- `TOOL` (lexical_declaration, line 7)
- `PYTHON` (lexical_declaration, line 8)
- `CATEGORY_BY_LETTER` (lexical_declaration, line 9)
- `Message` (interface_declaration, line 18)
- `openParen` (lexical_declaration, line 26)
- `closeParen` (lexical_declaration, line 27)
- `firstStar` (lexical_declaration, line 28)
- `lastStar` (lexical_declaration, line 29)
- `messages` (lexical_declaration, line 39)
- `last` (lexical_declaration, line 41)
- `ruleOf` (lexical_declaration, line 53)
- `{ code }` (lexical_declaration, line 54)
- `letter` (lexical_declaration, line 58)
- `version` (lexical_declaration, line 76)
- `lines` (lexical_declaration, line 77)
- `rules` (lexical_declaration, line 80)

## Contained in

- [core/producers](https://banes-lab.com/anatomy/quality/folder-quality-core-producers.md)

## Uses

- [core/aggregators/catalog.aggregator.ts](https://banes-lab.com/source/quality/core/aggregators/catalog.aggregator.ts.md)

## Linked from

- [core/aggregators](https://banes-lab.com/anatomy/quality/folder-quality-core-aggregators.md)
- [core/producers](https://banes-lab.com/anatomy/quality/folder-quality-core-producers.md)

## Source

```typescript
import type { CatalogProduct, CatalogRule } from "#types/catalog.types";
import { commandOutput, commandVersion } from "#core/adapters/invocation.adapter";
import { byRuleId } from "#core/comparators/rule.comparator";
import { defineProducer } from "#core/registries/producer.registry";
import { tallyBy } from "#core/aggregators/catalog.aggregator";

const TOOL = "pylint";
const PYTHON = "python";
const CATEGORY_BY_LETTER: ReadonlyMap<string, string> = new Map([
    ["C", "convention"],
    ["E", "error"],
    ["F", "fatal"],
    ["I", "information"],
    ["R", "refactor"],
    ["W", "warning"],
]);

interface Message {
    code: string | null;
    desc: string;
    name: string;
    template: string | null;
}

const headerOf = function headerOf(line: string): Message {
    const openParen = line.indexOf(" (");
    const closeParen = line.indexOf(")", openParen);
    const firstStar = line.indexOf("*");
    const lastStar = line.lastIndexOf("*");
    return {
        code: openParen > 0 && closeParen > openParen ? line.slice(openParen + " (".length, closeParen) : null,
        desc: "",
        name: openParen > 0 ? line.slice(1, openParen) : line.slice(1),
        template: firstStar !== -1 && lastStar > firstStar ? line.slice(firstStar + 1, lastStar) : null,
    };
};

const messagesOf = function messagesOf(lines: readonly string[]): Message[] {
    const messages: Message[] = [];
    for (const line of lines) {
        const last = messages.at(-1);
        if (line.startsWith(":")) {
            messages.push(headerOf(line));
            continue;
        }
        if (last !== undefined && line.trim() !== "") {
            last.desc += (last.desc === "" ? "" : " ") + line.trim();
        }
    }
    return messages;
};

const ruleOf = function ruleOf(message: Message, version: string): CatalogRule[] {
    const { code } = message;
    if (code === null || code === "") {
        return [];
    }
    const letter = code[0] ?? "";
    return [
        {
            canonical: null,
            category: CATEGORY_BY_LETTER.get(letter) ?? letter,
            description: message.desc === "" ? message.template : message.desc,
            ecosystem: PYTHON,
            messageTemplate: message.template,
            name: message.name,
            ruleId: code,
            tool: TOOL,
            toolVersion: version,
            url: null,
        },
    ];
};

const produce = async function produce(): Promise<CatalogProduct[]> {
    const version = commandVersion(PYTHON, ["-m", TOOL, "--version"]);
    const lines = commandOutput(PYTHON, ["-m", TOOL, "--list-msgs"])
        .split("\n")
        .map((line) => (line.endsWith("\r") ? line.slice(0, -1) : line));
    const rules = messagesOf(lines)
        .flatMap((message) => ruleOf(message, version))
        .toSorted(byRuleId);
    return [
        {
            rules,
            source: TOOL,
            summary: { byCategory: tallyBy(rules, "category"), tool: TOOL, toolVersion: version, total: rules.length },
        },
    ];
};

defineProducer({ name: TOOL, produce, refresh: "manual" });
```
