lintro 0.3.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of lintro might be problematic. Click here for more details.
- lintro/__init__.py +3 -0
- lintro/__main__.py +6 -0
- lintro/ascii-art/fail.txt +404 -0
- lintro/ascii-art/success.txt +484 -0
- lintro/cli.py +70 -0
- lintro/cli_utils/__init__.py +7 -0
- lintro/cli_utils/commands/__init__.py +7 -0
- lintro/cli_utils/commands/check.py +210 -0
- lintro/cli_utils/commands/format.py +167 -0
- lintro/cli_utils/commands/list_tools.py +114 -0
- lintro/enums/__init__.py +0 -0
- lintro/enums/action.py +29 -0
- lintro/enums/darglint_strictness.py +22 -0
- lintro/enums/group_by.py +31 -0
- lintro/enums/hadolint_enums.py +46 -0
- lintro/enums/output_format.py +40 -0
- lintro/enums/tool_name.py +36 -0
- lintro/enums/tool_type.py +27 -0
- lintro/enums/yamllint_format.py +22 -0
- lintro/exceptions/__init__.py +0 -0
- lintro/exceptions/errors.py +15 -0
- lintro/formatters/__init__.py +0 -0
- lintro/formatters/core/__init__.py +0 -0
- lintro/formatters/core/output_style.py +21 -0
- lintro/formatters/core/table_descriptor.py +24 -0
- lintro/formatters/styles/__init__.py +17 -0
- lintro/formatters/styles/csv.py +41 -0
- lintro/formatters/styles/grid.py +91 -0
- lintro/formatters/styles/html.py +48 -0
- lintro/formatters/styles/json.py +61 -0
- lintro/formatters/styles/markdown.py +41 -0
- lintro/formatters/styles/plain.py +39 -0
- lintro/formatters/tools/__init__.py +35 -0
- lintro/formatters/tools/darglint_formatter.py +72 -0
- lintro/formatters/tools/hadolint_formatter.py +84 -0
- lintro/formatters/tools/prettier_formatter.py +76 -0
- lintro/formatters/tools/ruff_formatter.py +116 -0
- lintro/formatters/tools/yamllint_formatter.py +87 -0
- lintro/models/__init__.py +0 -0
- lintro/models/core/__init__.py +0 -0
- lintro/models/core/tool.py +104 -0
- lintro/models/core/tool_config.py +23 -0
- lintro/models/core/tool_result.py +39 -0
- lintro/parsers/__init__.py +0 -0
- lintro/parsers/darglint/__init__.py +0 -0
- lintro/parsers/darglint/darglint_issue.py +9 -0
- lintro/parsers/darglint/darglint_parser.py +62 -0
- lintro/parsers/hadolint/__init__.py +1 -0
- lintro/parsers/hadolint/hadolint_issue.py +24 -0
- lintro/parsers/hadolint/hadolint_parser.py +65 -0
- lintro/parsers/prettier/__init__.py +0 -0
- lintro/parsers/prettier/prettier_issue.py +10 -0
- lintro/parsers/prettier/prettier_parser.py +60 -0
- lintro/parsers/ruff/__init__.py +1 -0
- lintro/parsers/ruff/ruff_issue.py +43 -0
- lintro/parsers/ruff/ruff_parser.py +89 -0
- lintro/parsers/yamllint/__init__.py +0 -0
- lintro/parsers/yamllint/yamllint_issue.py +24 -0
- lintro/parsers/yamllint/yamllint_parser.py +68 -0
- lintro/tools/__init__.py +40 -0
- lintro/tools/core/__init__.py +0 -0
- lintro/tools/core/tool_base.py +320 -0
- lintro/tools/core/tool_manager.py +167 -0
- lintro/tools/implementations/__init__.py +0 -0
- lintro/tools/implementations/tool_darglint.py +245 -0
- lintro/tools/implementations/tool_hadolint.py +302 -0
- lintro/tools/implementations/tool_prettier.py +270 -0
- lintro/tools/implementations/tool_ruff.py +618 -0
- lintro/tools/implementations/tool_yamllint.py +240 -0
- lintro/tools/tool_enum.py +17 -0
- lintro/utils/__init__.py +0 -0
- lintro/utils/ascii_normalize_cli.py +84 -0
- lintro/utils/config.py +39 -0
- lintro/utils/console_logger.py +783 -0
- lintro/utils/formatting.py +173 -0
- lintro/utils/output_manager.py +301 -0
- lintro/utils/path_utils.py +41 -0
- lintro/utils/tool_executor.py +443 -0
- lintro/utils/tool_utils.py +431 -0
- lintro-0.3.2.dist-info/METADATA +338 -0
- lintro-0.3.2.dist-info/RECORD +85 -0
- lintro-0.3.2.dist-info/WHEEL +5 -0
- lintro-0.3.2.dist-info/entry_points.txt +2 -0
- lintro-0.3.2.dist-info/licenses/LICENSE +21 -0
- lintro-0.3.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Hadolint enums for formats and failure thresholds."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from enum import StrEnum, auto
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class HadolintFormat(StrEnum):
|
|
9
|
+
TTY = auto()
|
|
10
|
+
JSON = auto()
|
|
11
|
+
CHECKSTYLE = auto()
|
|
12
|
+
CODECLIMATE = auto()
|
|
13
|
+
GITLAB_CODECLIMATE = auto()
|
|
14
|
+
GNU = auto()
|
|
15
|
+
CODACY = auto()
|
|
16
|
+
SONARQUBE = auto()
|
|
17
|
+
SARIF = auto()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class HadolintFailureThreshold(StrEnum):
|
|
21
|
+
ERROR = auto()
|
|
22
|
+
WARNING = auto()
|
|
23
|
+
INFO = auto()
|
|
24
|
+
STYLE = auto()
|
|
25
|
+
IGNORE = auto()
|
|
26
|
+
NONE = auto()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def normalize_hadolint_format(value: str | HadolintFormat) -> HadolintFormat:
|
|
30
|
+
if isinstance(value, HadolintFormat):
|
|
31
|
+
return value
|
|
32
|
+
try:
|
|
33
|
+
return HadolintFormat[value.upper()]
|
|
34
|
+
except Exception:
|
|
35
|
+
return HadolintFormat.TTY
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def normalize_hadolint_threshold(
|
|
39
|
+
value: str | HadolintFailureThreshold,
|
|
40
|
+
) -> HadolintFailureThreshold:
|
|
41
|
+
if isinstance(value, HadolintFailureThreshold):
|
|
42
|
+
return value
|
|
43
|
+
try:
|
|
44
|
+
return HadolintFailureThreshold[value.upper()]
|
|
45
|
+
except Exception:
|
|
46
|
+
return HadolintFailureThreshold.INFO
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Output format enum definitions.
|
|
2
|
+
|
|
3
|
+
This module defines the supported output formats for displaying results.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from enum import StrEnum, auto
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class OutputFormat(StrEnum):
|
|
12
|
+
"""Supported output formats for rendering results.
|
|
13
|
+
|
|
14
|
+
Values are lower-case string identifiers to align with CLI choices.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
PLAIN = auto()
|
|
18
|
+
GRID = auto()
|
|
19
|
+
MARKDOWN = auto()
|
|
20
|
+
HTML = auto()
|
|
21
|
+
JSON = auto()
|
|
22
|
+
CSV = auto()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def normalize_output_format(value: str | OutputFormat) -> OutputFormat:
|
|
26
|
+
"""Normalize a raw value to an OutputFormat enum.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
value: str or OutputFormat to normalize.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
OutputFormat: Normalized enum value.
|
|
33
|
+
"""
|
|
34
|
+
if isinstance(value, OutputFormat):
|
|
35
|
+
return value
|
|
36
|
+
try:
|
|
37
|
+
return OutputFormat[value.upper()]
|
|
38
|
+
except Exception:
|
|
39
|
+
# Fallback to GRID if invalid; callers may override upstream.
|
|
40
|
+
return OutputFormat.GRID
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Canonical tool name definitions.
|
|
2
|
+
|
|
3
|
+
Provides a stable set of identifiers for tools used across the codebase.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from enum import StrEnum, auto
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ToolName(StrEnum):
|
|
12
|
+
"""Supported tool identifiers in lower-case values."""
|
|
13
|
+
|
|
14
|
+
DARGLINT = auto()
|
|
15
|
+
HADOLINT = auto()
|
|
16
|
+
PRETTIER = auto()
|
|
17
|
+
RUFF = auto()
|
|
18
|
+
YAMLLINT = auto()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def normalize_tool_name(value: str | ToolName) -> ToolName:
|
|
22
|
+
"""Normalize a raw name to ToolName.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
value: Tool name as str or ToolName.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
ToolName: Normalized enum member.
|
|
29
|
+
"""
|
|
30
|
+
if isinstance(value, ToolName):
|
|
31
|
+
return value
|
|
32
|
+
try:
|
|
33
|
+
return ToolName[value.upper()]
|
|
34
|
+
except Exception:
|
|
35
|
+
# Conservative default if unknown
|
|
36
|
+
return ToolName.RUFF
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Tool type definitions."""
|
|
2
|
+
|
|
3
|
+
from enum import Flag, auto
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ToolType(Flag):
|
|
7
|
+
"""Tool type definitions.
|
|
8
|
+
|
|
9
|
+
This enum defines the different types of tools that can be used in Lintro.
|
|
10
|
+
Tools can be of multiple types (e.g., a core can be both a linter and a formatter),
|
|
11
|
+
which is why this is a Flag enum rather than a regular Enum.
|
|
12
|
+
|
|
13
|
+
Attributes:
|
|
14
|
+
LINTER = Tool that checks code for issues
|
|
15
|
+
FORMATTER = Tool that formats code
|
|
16
|
+
TYPE_CHECKER = Tool that checks types
|
|
17
|
+
DOCUMENTATION = Tool that checks documentation
|
|
18
|
+
SECURITY = Tool that checks for security issues
|
|
19
|
+
INFRASTRUCTURE = Tool that checks infrastructure code
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
LINTER = auto()
|
|
23
|
+
FORMATTER = auto()
|
|
24
|
+
TYPE_CHECKER = auto()
|
|
25
|
+
DOCUMENTATION = auto()
|
|
26
|
+
SECURITY = auto()
|
|
27
|
+
INFRASTRUCTURE = auto()
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Yamllint format enum."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from enum import StrEnum, auto
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class YamllintFormat(StrEnum):
|
|
9
|
+
PARSABLE = auto()
|
|
10
|
+
STANDARD = auto()
|
|
11
|
+
COLORED = auto()
|
|
12
|
+
GITHUB = auto()
|
|
13
|
+
AUTO = auto()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def normalize_yamllint_format(value: str | YamllintFormat) -> YamllintFormat:
|
|
17
|
+
if isinstance(value, YamllintFormat):
|
|
18
|
+
return value
|
|
19
|
+
try:
|
|
20
|
+
return YamllintFormat[value.upper()]
|
|
21
|
+
except Exception:
|
|
22
|
+
return YamllintFormat.PARSABLE
|
|
File without changes
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Custom exception types for Lintro."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class LintroError(Exception):
|
|
7
|
+
"""Base exception for all Lintro-related errors."""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class InvalidToolConfigError(LintroError):
|
|
11
|
+
"""Raised when a tool's configuration is invalid."""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class InvalidToolOptionError(LintroError):
|
|
15
|
+
"""Raised when invalid options are provided to a tool."""
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class OutputStyle(ABC):
|
|
6
|
+
@abstractmethod
|
|
7
|
+
def format(
|
|
8
|
+
self,
|
|
9
|
+
columns: list[str],
|
|
10
|
+
rows: list[list[Any]],
|
|
11
|
+
) -> str:
|
|
12
|
+
"""Format a table given columns and rows.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
columns: List of column header names.
|
|
16
|
+
rows: List of rows, where each row is a list of values.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
str: Formatted table as a string.
|
|
20
|
+
"""
|
|
21
|
+
pass
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class TableDescriptor(ABC):
|
|
6
|
+
@abstractmethod
|
|
7
|
+
def get_columns(self) -> list[str]:
|
|
8
|
+
"""Return the list of column names in order."""
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
@abstractmethod
|
|
12
|
+
def get_rows(
|
|
13
|
+
self,
|
|
14
|
+
issues: list[Any],
|
|
15
|
+
) -> list[list[Any]]:
|
|
16
|
+
"""Return the values for each column for a list of issues.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
issues: List of issue objects to extract data from.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
list[list]: Nested list representing table rows and columns.
|
|
23
|
+
"""
|
|
24
|
+
pass
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Output format styles for lintro."""
|
|
2
|
+
|
|
3
|
+
from .csv import CsvStyle
|
|
4
|
+
from .grid import GridStyle
|
|
5
|
+
from .html import HtmlStyle
|
|
6
|
+
from .json import JsonStyle
|
|
7
|
+
from .markdown import MarkdownStyle
|
|
8
|
+
from .plain import PlainStyle
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"PlainStyle",
|
|
12
|
+
"GridStyle",
|
|
13
|
+
"MarkdownStyle",
|
|
14
|
+
"HtmlStyle",
|
|
15
|
+
"JsonStyle",
|
|
16
|
+
"CsvStyle",
|
|
17
|
+
]
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import csv
|
|
2
|
+
import io
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from lintro.formatters.core.output_style import OutputStyle
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CsvStyle(OutputStyle):
|
|
9
|
+
"""Output format that renders data as CSV."""
|
|
10
|
+
|
|
11
|
+
def format(
|
|
12
|
+
self,
|
|
13
|
+
columns: list[str],
|
|
14
|
+
rows: list[list[Any]],
|
|
15
|
+
) -> str:
|
|
16
|
+
"""Format a table given columns and rows as CSV.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
columns: List of column header names.
|
|
20
|
+
rows: List of row values (each row is a list of cell values).
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
Formatted data as CSV string.
|
|
24
|
+
"""
|
|
25
|
+
if not rows:
|
|
26
|
+
return ""
|
|
27
|
+
|
|
28
|
+
# Create a string buffer to write CSV data
|
|
29
|
+
output = io.StringIO()
|
|
30
|
+
writer = csv.writer(output)
|
|
31
|
+
|
|
32
|
+
# Write header
|
|
33
|
+
writer.writerow(columns)
|
|
34
|
+
|
|
35
|
+
# Write rows
|
|
36
|
+
for row in rows:
|
|
37
|
+
# Ensure row has same number of elements as columns
|
|
38
|
+
padded_row = row + [""] * (len(columns) - len(row))
|
|
39
|
+
writer.writerow(padded_row)
|
|
40
|
+
|
|
41
|
+
return output.getvalue()
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from lintro.formatters.core.output_style import OutputStyle
|
|
4
|
+
|
|
5
|
+
# Try to import tabulate
|
|
6
|
+
try:
|
|
7
|
+
from tabulate import tabulate
|
|
8
|
+
|
|
9
|
+
TABULATE_AVAILABLE = True
|
|
10
|
+
except ImportError:
|
|
11
|
+
TABULATE_AVAILABLE = False
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class GridStyle(OutputStyle):
|
|
15
|
+
"""Output format that renders data as a formatted grid table.
|
|
16
|
+
|
|
17
|
+
This style creates a nicely formatted table with proper column alignment
|
|
18
|
+
and borders, similar to what you might see in a terminal or console.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def format(
|
|
22
|
+
self,
|
|
23
|
+
columns: list[str],
|
|
24
|
+
rows: list[list[Any]],
|
|
25
|
+
) -> str:
|
|
26
|
+
"""Format a table given columns and rows as a grid.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
columns: List of column header names.
|
|
30
|
+
rows: List of row values (each row is a list of cell values).
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Formatted data as grid table string.
|
|
34
|
+
"""
|
|
35
|
+
if not rows:
|
|
36
|
+
return ""
|
|
37
|
+
|
|
38
|
+
# Use tabulate if available
|
|
39
|
+
if TABULATE_AVAILABLE:
|
|
40
|
+
# Provide sane defaults for alignment and column widths to avoid
|
|
41
|
+
# terminal wrapping that misaligns the grid. We keep most columns
|
|
42
|
+
# left-aligned, numeric columns right-aligned, and cap wide
|
|
43
|
+
# columns like File/Message.
|
|
44
|
+
colalign_map = {
|
|
45
|
+
"Line": "right",
|
|
46
|
+
"Column": "right",
|
|
47
|
+
"Fixable": "center",
|
|
48
|
+
}
|
|
49
|
+
colalign = [colalign_map.get(col, "left") for col in columns]
|
|
50
|
+
|
|
51
|
+
# Cap very wide columns so tabulate wraps within cells, preserving
|
|
52
|
+
# alignment instead of letting the terminal wrap mid-grid.
|
|
53
|
+
width_map: dict[str, int] = {
|
|
54
|
+
"File": 48,
|
|
55
|
+
"Message": 64,
|
|
56
|
+
"Code": 12,
|
|
57
|
+
"Line": 8,
|
|
58
|
+
"Column": 8,
|
|
59
|
+
"Fixable": 8,
|
|
60
|
+
}
|
|
61
|
+
maxcolwidths = [width_map.get(col) for col in columns]
|
|
62
|
+
|
|
63
|
+
return tabulate(
|
|
64
|
+
tabular_data=rows,
|
|
65
|
+
headers=columns,
|
|
66
|
+
tablefmt="grid",
|
|
67
|
+
stralign="left",
|
|
68
|
+
numalign="right",
|
|
69
|
+
colalign=colalign,
|
|
70
|
+
maxcolwidths=maxcolwidths,
|
|
71
|
+
disable_numparse=True,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Fallback to simple format when tabulate is not available
|
|
75
|
+
if not columns:
|
|
76
|
+
return ""
|
|
77
|
+
|
|
78
|
+
# Build the header
|
|
79
|
+
header = " | ".join(columns)
|
|
80
|
+
separator = "-" * len(header)
|
|
81
|
+
|
|
82
|
+
# Build the rows
|
|
83
|
+
formatted_rows = []
|
|
84
|
+
for row in rows:
|
|
85
|
+
# Ensure row has same number of elements as columns
|
|
86
|
+
padded_row = row + [""] * (len(columns) - len(row))
|
|
87
|
+
formatted_rows.append(" | ".join(str(cell) for cell in padded_row))
|
|
88
|
+
|
|
89
|
+
# Combine all parts
|
|
90
|
+
result = [header, separator] + formatted_rows
|
|
91
|
+
return "\n".join(result)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from lintro.formatters.core.output_style import OutputStyle
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class HtmlStyle(OutputStyle):
|
|
7
|
+
"""Output format that renders data as HTML table."""
|
|
8
|
+
|
|
9
|
+
def format(
|
|
10
|
+
self,
|
|
11
|
+
columns: list[str],
|
|
12
|
+
rows: list[list[Any]],
|
|
13
|
+
) -> str:
|
|
14
|
+
"""Format a table given columns and rows as HTML.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
columns: List of column header names.
|
|
18
|
+
rows: List of row values (each row is a list of cell values).
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
Formatted data as HTML table string.
|
|
22
|
+
"""
|
|
23
|
+
if not rows:
|
|
24
|
+
return "<p>No issues found.</p>"
|
|
25
|
+
|
|
26
|
+
# Build the header
|
|
27
|
+
header_cells = "".join(f"<th>{col}</th>" for col in columns)
|
|
28
|
+
header = f"<tr>{header_cells}</tr>"
|
|
29
|
+
|
|
30
|
+
# Build the rows
|
|
31
|
+
formatted_rows = []
|
|
32
|
+
for row in rows:
|
|
33
|
+
# Ensure row has same number of elements as columns
|
|
34
|
+
padded_row = row + [""] * (len(columns) - len(row))
|
|
35
|
+
# Escape HTML characters in cell values
|
|
36
|
+
escaped_cells = [
|
|
37
|
+
str(cell)
|
|
38
|
+
.replace("&", "&")
|
|
39
|
+
.replace("<", "<")
|
|
40
|
+
.replace(">", ">")
|
|
41
|
+
for cell in padded_row
|
|
42
|
+
]
|
|
43
|
+
row_cells = "".join(f"<td>{cell}</td>" for cell in escaped_cells)
|
|
44
|
+
formatted_rows.append(f"<tr>{row_cells}</tr>")
|
|
45
|
+
|
|
46
|
+
# Combine all parts
|
|
47
|
+
table_content = header + "".join(formatted_rows)
|
|
48
|
+
return f"<table>{table_content}</table>"
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from lintro.formatters.core.output_style import OutputStyle
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class JsonStyle(OutputStyle):
|
|
9
|
+
"""Output format that renders data as structured JSON."""
|
|
10
|
+
|
|
11
|
+
def format(
|
|
12
|
+
self,
|
|
13
|
+
columns: list[str],
|
|
14
|
+
rows: list[list[Any]],
|
|
15
|
+
tool_name: str | None = None,
|
|
16
|
+
metadata: dict[str, Any] | None = None,
|
|
17
|
+
**kwargs,
|
|
18
|
+
) -> str:
|
|
19
|
+
"""Format a table given columns and rows as structured JSON.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
columns: List of column names.
|
|
23
|
+
rows: List of row values (each row is a list of cell values).
|
|
24
|
+
tool_name: Name of the tool that generated the data.
|
|
25
|
+
metadata: Tool-specific metadata.
|
|
26
|
+
**kwargs: Additional metadata.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
Formatted data as structured JSON string.
|
|
30
|
+
"""
|
|
31
|
+
# Convert column names to standardized format
|
|
32
|
+
normalized_columns = [col.lower().replace(" ", "_") for col in columns]
|
|
33
|
+
|
|
34
|
+
# Convert rows to list of dictionaries with proper data types
|
|
35
|
+
issues = []
|
|
36
|
+
for row in rows:
|
|
37
|
+
issue_dict = {}
|
|
38
|
+
for i, value in enumerate(row):
|
|
39
|
+
if i < len(normalized_columns):
|
|
40
|
+
issue_dict[normalized_columns[i]] = value
|
|
41
|
+
issues.append(issue_dict)
|
|
42
|
+
|
|
43
|
+
# Create the final JSON structure
|
|
44
|
+
result = {
|
|
45
|
+
"tool": tool_name,
|
|
46
|
+
"timestamp": datetime.now().isoformat(),
|
|
47
|
+
"total_issues": len(issues),
|
|
48
|
+
"issues": issues,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# Add metadata if provided
|
|
52
|
+
if metadata:
|
|
53
|
+
result["metadata"] = metadata
|
|
54
|
+
|
|
55
|
+
# Add any additional kwargs as metadata
|
|
56
|
+
if kwargs:
|
|
57
|
+
if "metadata" not in result:
|
|
58
|
+
result["metadata"] = {}
|
|
59
|
+
result["metadata"].update(kwargs)
|
|
60
|
+
|
|
61
|
+
return json.dumps(result, indent=2, ensure_ascii=False)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from lintro.formatters.core.output_style import OutputStyle
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MarkdownStyle(OutputStyle):
|
|
7
|
+
"""Output format that renders data as markdown table."""
|
|
8
|
+
|
|
9
|
+
def format(
|
|
10
|
+
self,
|
|
11
|
+
columns: list[str],
|
|
12
|
+
rows: list[list[Any]],
|
|
13
|
+
) -> str:
|
|
14
|
+
"""Format a table given columns and rows as markdown.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
columns: List of column header names.
|
|
18
|
+
rows: List of row values (each row is a list of cell values).
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
Formatted data as markdown table string.
|
|
22
|
+
"""
|
|
23
|
+
if not rows:
|
|
24
|
+
return "No issues found."
|
|
25
|
+
|
|
26
|
+
# Build the header
|
|
27
|
+
header = "| " + " | ".join(columns) + " |"
|
|
28
|
+
separator = "| " + " | ".join("---" for _ in columns) + " |"
|
|
29
|
+
|
|
30
|
+
# Build the rows
|
|
31
|
+
formatted_rows = []
|
|
32
|
+
for row in rows:
|
|
33
|
+
# Ensure row has same number of elements as columns
|
|
34
|
+
padded_row = row + [""] * (len(columns) - len(row))
|
|
35
|
+
# Escape pipe characters in cell values
|
|
36
|
+
escaped_cells = [str(cell).replace("|", "\\|") for cell in padded_row]
|
|
37
|
+
formatted_rows.append("| " + " | ".join(escaped_cells) + " |")
|
|
38
|
+
|
|
39
|
+
# Combine all parts
|
|
40
|
+
result = [header, separator] + formatted_rows
|
|
41
|
+
return "\n".join(result)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from lintro.formatters.core.output_style import OutputStyle
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class PlainStyle(OutputStyle):
|
|
7
|
+
"""Output format that renders data as plain text."""
|
|
8
|
+
|
|
9
|
+
def format(
|
|
10
|
+
self,
|
|
11
|
+
columns: list[str],
|
|
12
|
+
rows: list[list[Any]],
|
|
13
|
+
) -> str:
|
|
14
|
+
"""Format a table given columns and rows as plain text.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
columns: List of column header names.
|
|
18
|
+
rows: List of row values (each row is a list of cell values).
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
Formatted data as plain text string.
|
|
22
|
+
"""
|
|
23
|
+
if not rows:
|
|
24
|
+
return "No issues found."
|
|
25
|
+
|
|
26
|
+
# Build the header
|
|
27
|
+
header = " | ".join(columns)
|
|
28
|
+
separator = "-" * len(header)
|
|
29
|
+
|
|
30
|
+
# Build the rows
|
|
31
|
+
formatted_rows = []
|
|
32
|
+
for row in rows:
|
|
33
|
+
# Ensure row has same number of elements as columns
|
|
34
|
+
padded_row = row + [""] * (len(columns) - len(row))
|
|
35
|
+
formatted_rows.append(" | ".join(str(cell) for cell in padded_row))
|
|
36
|
+
|
|
37
|
+
# Combine all parts
|
|
38
|
+
result = [header, separator] + formatted_rows
|
|
39
|
+
return "\n".join(result)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Tool-specific table formatters package exports."""
|
|
2
|
+
|
|
3
|
+
from lintro.formatters.tools.darglint_formatter import (
|
|
4
|
+
DarglintTableDescriptor,
|
|
5
|
+
format_darglint_issues,
|
|
6
|
+
)
|
|
7
|
+
from lintro.formatters.tools.hadolint_formatter import (
|
|
8
|
+
HadolintTableDescriptor,
|
|
9
|
+
format_hadolint_issues,
|
|
10
|
+
)
|
|
11
|
+
from lintro.formatters.tools.prettier_formatter import (
|
|
12
|
+
PrettierTableDescriptor,
|
|
13
|
+
format_prettier_issues,
|
|
14
|
+
)
|
|
15
|
+
from lintro.formatters.tools.ruff_formatter import (
|
|
16
|
+
RuffTableDescriptor,
|
|
17
|
+
format_ruff_issues,
|
|
18
|
+
)
|
|
19
|
+
from lintro.formatters.tools.yamllint_formatter import (
|
|
20
|
+
YamllintTableDescriptor,
|
|
21
|
+
format_yamllint_issues,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
"DarglintTableDescriptor",
|
|
26
|
+
"format_darglint_issues",
|
|
27
|
+
"HadolintTableDescriptor",
|
|
28
|
+
"format_hadolint_issues",
|
|
29
|
+
"PrettierTableDescriptor",
|
|
30
|
+
"format_prettier_issues",
|
|
31
|
+
"RuffTableDescriptor",
|
|
32
|
+
"format_ruff_issues",
|
|
33
|
+
"YamllintTableDescriptor",
|
|
34
|
+
"format_yamllint_issues",
|
|
35
|
+
]
|