lintro 0.13.2__py3-none-any.whl → 0.14.0__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 CHANGED
@@ -1,3 +1,3 @@
1
1
  """Lintro - A unified CLI core for code formatting, linting, and quality assurance."""
2
2
 
3
- __version__ = "0.13.2"
3
+ __version__ = "0.14.0"
@@ -1,14 +1,21 @@
1
1
  """Parser modules for Lintro tools."""
2
2
 
3
- from . import (
4
- actionlint,
5
- bandit,
6
- darglint,
7
- hadolint,
8
- prettier,
9
- ruff,
10
- yamllint,
11
- )
3
+ from __future__ import annotations
4
+
5
+ from importlib import import_module
6
+ from typing import TYPE_CHECKING
7
+
8
+ if TYPE_CHECKING:
9
+ # Type checking imports
10
+ from lintro.parsers import (
11
+ actionlint,
12
+ bandit,
13
+ darglint,
14
+ hadolint,
15
+ prettier,
16
+ ruff,
17
+ yamllint,
18
+ )
12
19
 
13
20
  __all__ = [
14
21
  "actionlint",
@@ -19,3 +26,47 @@ __all__ = [
19
26
  "ruff",
20
27
  "yamllint",
21
28
  ]
29
+
30
+ # Lazy-load parser submodules to avoid circular imports
31
+ _SUBMODULES = {
32
+ "actionlint",
33
+ "bandit",
34
+ "darglint",
35
+ "hadolint",
36
+ "prettier",
37
+ "ruff",
38
+ "yamllint",
39
+ }
40
+
41
+
42
+ def __getattr__(name: str) -> object:
43
+ """Lazy-load parser submodules to avoid circular import issues.
44
+
45
+ This function is called when an attribute is accessed that doesn't exist
46
+ in the module. It allows accessing parser submodules without eagerly
47
+ importing them all at package initialization time.
48
+
49
+ Args:
50
+ name: The name of the attribute being accessed.
51
+
52
+ Returns:
53
+ The imported submodule.
54
+
55
+ Raises:
56
+ AttributeError: If the requested name is not a known submodule.
57
+ """
58
+ if name in _SUBMODULES:
59
+ module = import_module(f".{name}", __package__)
60
+ # Cache the module in this module's namespace for future access
61
+ globals()[name] = module
62
+ return module
63
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
64
+
65
+
66
+ def __dir__() -> list[str]:
67
+ """Return list of available attributes for this module.
68
+
69
+ Returns:
70
+ List of submodule names and other module attributes.
71
+ """
72
+ return list(__all__)
@@ -0,0 +1,6 @@
1
+ """Bandit parser module."""
2
+
3
+ from lintro.parsers.bandit.bandit_issue import BanditIssue
4
+ from lintro.parsers.bandit.bandit_parser import parse_bandit_output
5
+
6
+ __all__ = ["BanditIssue", "parse_bandit_output"]
@@ -0,0 +1,49 @@
1
+ """Bandit issue model for security vulnerabilities."""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any
5
+
6
+
7
+ @dataclass
8
+ class BanditIssue:
9
+ """Represents a security issue found by Bandit.
10
+
11
+ Attributes:
12
+ file: str: Path to the file containing the issue.
13
+ line: int: Line number where the issue was found.
14
+ col_offset: int: Column offset of the issue.
15
+ issue_severity: str: Severity level (LOW, MEDIUM, HIGH).
16
+ issue_confidence: str: Confidence level (LOW, MEDIUM, HIGH).
17
+ test_id: str: Bandit test ID (e.g., B602, B301).
18
+ test_name: str: Name of the test that found the issue.
19
+ issue_text: str: Description of the security issue.
20
+ more_info: str: URL with more information about the issue.
21
+ cwe: dict[str, Any] | None: CWE (Common Weakness Enumeration) information.
22
+ code: str: Code snippet containing the issue.
23
+ line_range: list[int]: Range of lines containing the issue.
24
+ """
25
+
26
+ file: str
27
+ line: int
28
+ col_offset: int
29
+ issue_severity: str
30
+ issue_confidence: str
31
+ test_id: str
32
+ test_name: str
33
+ issue_text: str
34
+ more_info: str
35
+ cwe: dict[str, Any] | None = None
36
+ code: str | None = None
37
+ line_range: list[int] | None = None
38
+
39
+ @property
40
+ def message(self) -> str:
41
+ """Get a human-readable message for the issue.
42
+
43
+ Returns:
44
+ str: Formatted issue message.
45
+ """
46
+ return (
47
+ f"[{self.test_id}:{self.test_name}] {self.issue_severity} severity, "
48
+ f"{self.issue_confidence} confidence: {self.issue_text}"
49
+ )
@@ -0,0 +1,99 @@
1
+ """Bandit output parser for security issues."""
2
+
3
+ from typing import Any
4
+
5
+ from loguru import logger
6
+
7
+ from lintro.parsers.bandit.bandit_issue import BanditIssue
8
+
9
+
10
+ def parse_bandit_output(bandit_data: dict[str, Any]) -> list[BanditIssue]:
11
+ """Parse Bandit JSON output into BanditIssue objects.
12
+
13
+ Args:
14
+ bandit_data: dict[str, Any]: JSON data from Bandit output.
15
+
16
+ Returns:
17
+ list[BanditIssue]: List of parsed security issues.
18
+
19
+ Raises:
20
+ ValueError: If the bandit data structure is invalid.
21
+ """
22
+ if not isinstance(bandit_data, dict):
23
+ raise ValueError("Bandit data must be a dictionary")
24
+
25
+ results = bandit_data.get("results", [])
26
+ if not isinstance(results, list):
27
+ raise ValueError("Bandit results must be a list")
28
+
29
+ issues: list[BanditIssue] = []
30
+
31
+ for result in results:
32
+ if not isinstance(result, dict):
33
+ continue
34
+
35
+ try:
36
+ filename = result.get("filename", "")
37
+ line_number = result.get("line_number", 0)
38
+ col_offset = result.get("col_offset", 0)
39
+ issue_severity = result.get("issue_severity", "UNKNOWN")
40
+ issue_confidence = result.get("issue_confidence", "UNKNOWN")
41
+ test_id = result.get("test_id", "")
42
+ test_name = result.get("test_name", "")
43
+ issue_text = result.get("issue_text", "")
44
+ more_info = result.get("more_info", "")
45
+ cwe = result.get("issue_cwe")
46
+ code = result.get("code")
47
+ line_range = result.get("line_range")
48
+
49
+ # Validate critical fields; skip malformed entries
50
+ if not isinstance(filename, str):
51
+ logger.warning("Skipping issue with non-string filename")
52
+ continue
53
+ if not isinstance(line_number, int):
54
+ logger.warning("Skipping issue with non-integer line_number")
55
+ continue
56
+ if not isinstance(col_offset, int):
57
+ col_offset = 0
58
+
59
+ sev = (
60
+ str(issue_severity).upper() if issue_severity is not None else "UNKNOWN"
61
+ )
62
+ conf = (
63
+ str(issue_confidence).upper()
64
+ if issue_confidence is not None
65
+ else "UNKNOWN"
66
+ )
67
+
68
+ test_id = test_id if isinstance(test_id, str) else ""
69
+ test_name = test_name if isinstance(test_name, str) else ""
70
+ issue_text = issue_text if isinstance(issue_text, str) else ""
71
+ more_info = more_info if isinstance(more_info, str) else ""
72
+
73
+ # Normalize line_range to list[int] when provided
74
+ if isinstance(line_range, list):
75
+ line_range = [x for x in line_range if isinstance(x, int)] or None
76
+ else:
77
+ line_range = None
78
+
79
+ issue = BanditIssue(
80
+ file=filename,
81
+ line=line_number,
82
+ col_offset=col_offset,
83
+ issue_severity=sev,
84
+ issue_confidence=conf,
85
+ test_id=test_id,
86
+ test_name=test_name,
87
+ issue_text=issue_text,
88
+ more_info=more_info,
89
+ cwe=cwe if isinstance(cwe, dict) else None,
90
+ code=code if isinstance(code, str) else None,
91
+ line_range=line_range,
92
+ )
93
+ issues.append(issue)
94
+ except (KeyError, TypeError, ValueError) as e:
95
+ # Log warning but continue processing other issues
96
+ logger.warning(f"Failed to parse bandit issue: {e}")
97
+ continue
98
+
99
+ return issues
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lintro
3
- Version: 0.13.2
3
+ Version: 0.14.0
4
4
  Summary: A unified CLI tool for code formatting, linting, and quality assurance
5
5
  Author-email: TurboCoder13 <turbocoder13@gmail.com>
6
6
  License: MIT License
@@ -1,4 +1,4 @@
1
- lintro/__init__.py,sha256=Ail-CkEgZsqrxbRM5lCx5xowe42QAZppsNICHedQsec,111
1
+ lintro/__init__.py,sha256=ainStkaMVTYQewEHw_5xgDlfUngdJMJkys5qlst44z8,111
2
2
  lintro/__main__.py,sha256=McxM6wEcEeCLBHZs0F8xzT1PxVqJYkjtaPq1_-Nug-0,106
3
3
  lintro/cli.py,sha256=0apxBcqVxBZdCeXGMyaSdJYSntMIiCFwPwan1jUmPzM,2520
4
4
  lintro/ascii-art/fail.txt,sha256=gshKngSrz5FvJdP6g7lPygpLsLsZZh4etBU_wR7PXOw,56396
@@ -44,10 +44,13 @@ lintro/models/core/__init__.py,sha256=mevoEG547VTotUsgyocEB72OknnGhExNWcfel7ZPLn
44
44
  lintro/models/core/tool.py,sha256=TWkVV_KOYziqCdq_f3Goft3PKWKByitsQ2VFiX6MVHM,2594
45
45
  lintro/models/core/tool_config.py,sha256=FCfKQ_bz2yBg_FJ5xEud7TdF0PZ3fPp1Yjty02rbHR8,1004
46
46
  lintro/models/core/tool_result.py,sha256=z5fexuCBt1ir0hvzojxVX-Abt6MNIT7_JvH5oyW_FlI,1319
47
- lintro/parsers/__init__.py,sha256=e2cdHTwELWVeewTLn33GTQHPV8A7QCT0rPmf0blm8A8,275
47
+ lintro/parsers/__init__.py,sha256=Xb2NegNLAm10mlY8BJ6lOIAtVtoCmQfY5uA6mljEs-M,1663
48
48
  lintro/parsers/actionlint/__init__.py,sha256=N953d0kbJEOZE9cG4eq9BjiSErdk10GMrngV_tKjLvo,33
49
49
  lintro/parsers/actionlint/actionlint_issue.py,sha256=H_BQr04KAGHpVp1qKqETcWLi3i-u6atl7ccDae-jqX8,636
50
50
  lintro/parsers/actionlint/actionlint_parser.py,sha256=tvpPOi1dQrnpf7V2-3LMANH_UfDdecI4_AwDQpZOyU0,1898
51
+ lintro/parsers/bandit/__init__.py,sha256=LMXIVOoW1wnS0mXbiVY-htdkUvb1clRSfSn7X3L2SSw,206
52
+ lintro/parsers/bandit/bandit_issue.py,sha256=zdC6dLFmks_w9JjDWQ00BPSpCUUkilElby-Ut0PVLtE,1607
53
+ lintro/parsers/bandit/bandit_parser.py,sha256=d4KRH_U_4Exv-Fxq63yyx7F484yorKr2p8caDPCyKHs,3591
51
54
  lintro/parsers/black/black_issue.py,sha256=RwrT7n8aw4Nybakv83eXoeUxZlDtHwicWKNfrHYIYOg,507
52
55
  lintro/parsers/black/black_parser.py,sha256=mCbEy4aTRtxC_SKKPNK6hThGEu8C0m9EnliFW__Sa3Y,2958
53
56
  lintro/parsers/darglint/__init__.py,sha256=r2ilRjf8WdHOvXIGsg4SmtYkRUR76DowWLA_n9sIOV0,55
@@ -88,9 +91,9 @@ lintro/utils/output_manager.py,sha256=oIE0g8LNVQcWSQOb1MbDVqGYPrOjBGuUZ4R80M1Rev
88
91
  lintro/utils/path_utils.py,sha256=NJP3vjVDcRBgUHtYNrpL0tRa0Sc3oQhGX3_2WWzdZE4,1395
89
92
  lintro/utils/tool_executor.py,sha256=PNUuWkQj4TWI9UHVvZUpWoDkU00MAd1o5_VhG-iviCo,26124
90
93
  lintro/utils/tool_utils.py,sha256=T3A5xf4zokUMPVzWzEn3f0M14EcDxoeRo-jf5yXEoyI,15613
91
- lintro-0.13.2.dist-info/licenses/LICENSE,sha256=CwaAnyD2psonDBBJjbqFUz00W8nQw-FGDlEGZReUV6A,1069
92
- lintro-0.13.2.dist-info/METADATA,sha256=fCsztQxGFnuQDksRXJBLrH0Zaps9T2h7oz33EqaeBbM,16194
93
- lintro-0.13.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
94
- lintro-0.13.2.dist-info/entry_points.txt,sha256=SYSk35jFyNLEHyrofSJsRv4qFN9NsT4VWSbvnTS9ov0,43
95
- lintro-0.13.2.dist-info/top_level.txt,sha256=_D-7eyV6gNBOoIwHuf_h60wN_RWiw8GxB430Il9VKhU,7
96
- lintro-0.13.2.dist-info/RECORD,,
94
+ lintro-0.14.0.dist-info/licenses/LICENSE,sha256=CwaAnyD2psonDBBJjbqFUz00W8nQw-FGDlEGZReUV6A,1069
95
+ lintro-0.14.0.dist-info/METADATA,sha256=k6mkkwpsJDo3pkWalx8Btw03SGwqo09x5JBeSmx7Ndo,16194
96
+ lintro-0.14.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
97
+ lintro-0.14.0.dist-info/entry_points.txt,sha256=SYSk35jFyNLEHyrofSJsRv4qFN9NsT4VWSbvnTS9ov0,43
98
+ lintro-0.14.0.dist-info/top_level.txt,sha256=_D-7eyV6gNBOoIwHuf_h60wN_RWiw8GxB430Il9VKhU,7
99
+ lintro-0.14.0.dist-info/RECORD,,