lintro 0.6.2__py3-none-any.whl → 0.17.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.
Files changed (109) hide show
  1. lintro/__init__.py +1 -1
  2. lintro/cli.py +230 -14
  3. lintro/cli_utils/commands/__init__.py +8 -1
  4. lintro/cli_utils/commands/check.py +1 -0
  5. lintro/cli_utils/commands/config.py +325 -0
  6. lintro/cli_utils/commands/format.py +2 -2
  7. lintro/cli_utils/commands/init.py +361 -0
  8. lintro/cli_utils/commands/list_tools.py +180 -42
  9. lintro/cli_utils/commands/test.py +316 -0
  10. lintro/cli_utils/commands/versions.py +81 -0
  11. lintro/config/__init__.py +62 -0
  12. lintro/config/config_loader.py +420 -0
  13. lintro/config/lintro_config.py +189 -0
  14. lintro/config/tool_config_generator.py +403 -0
  15. lintro/enums/__init__.py +1 -0
  16. lintro/enums/darglint_strictness.py +10 -0
  17. lintro/enums/hadolint_enums.py +22 -0
  18. lintro/enums/tool_name.py +2 -0
  19. lintro/enums/tool_type.py +2 -0
  20. lintro/enums/yamllint_format.py +11 -0
  21. lintro/exceptions/__init__.py +1 -0
  22. lintro/formatters/__init__.py +1 -0
  23. lintro/formatters/core/__init__.py +1 -0
  24. lintro/formatters/core/output_style.py +11 -0
  25. lintro/formatters/core/table_descriptor.py +8 -0
  26. lintro/formatters/styles/csv.py +2 -0
  27. lintro/formatters/styles/grid.py +2 -0
  28. lintro/formatters/styles/html.py +2 -0
  29. lintro/formatters/styles/json.py +2 -0
  30. lintro/formatters/styles/markdown.py +2 -0
  31. lintro/formatters/styles/plain.py +2 -0
  32. lintro/formatters/tools/__init__.py +12 -0
  33. lintro/formatters/tools/black_formatter.py +27 -5
  34. lintro/formatters/tools/darglint_formatter.py +16 -1
  35. lintro/formatters/tools/eslint_formatter.py +108 -0
  36. lintro/formatters/tools/hadolint_formatter.py +13 -0
  37. lintro/formatters/tools/markdownlint_formatter.py +88 -0
  38. lintro/formatters/tools/prettier_formatter.py +15 -0
  39. lintro/formatters/tools/pytest_formatter.py +201 -0
  40. lintro/formatters/tools/ruff_formatter.py +26 -5
  41. lintro/formatters/tools/yamllint_formatter.py +14 -1
  42. lintro/models/__init__.py +1 -0
  43. lintro/models/core/__init__.py +1 -0
  44. lintro/models/core/tool_config.py +11 -7
  45. lintro/parsers/__init__.py +69 -9
  46. lintro/parsers/actionlint/actionlint_parser.py +1 -1
  47. lintro/parsers/bandit/__init__.py +6 -0
  48. lintro/parsers/bandit/bandit_issue.py +49 -0
  49. lintro/parsers/bandit/bandit_parser.py +99 -0
  50. lintro/parsers/black/black_issue.py +4 -0
  51. lintro/parsers/darglint/__init__.py +1 -0
  52. lintro/parsers/darglint/darglint_issue.py +11 -0
  53. lintro/parsers/eslint/__init__.py +6 -0
  54. lintro/parsers/eslint/eslint_issue.py +26 -0
  55. lintro/parsers/eslint/eslint_parser.py +63 -0
  56. lintro/parsers/markdownlint/__init__.py +6 -0
  57. lintro/parsers/markdownlint/markdownlint_issue.py +22 -0
  58. lintro/parsers/markdownlint/markdownlint_parser.py +113 -0
  59. lintro/parsers/prettier/__init__.py +1 -0
  60. lintro/parsers/prettier/prettier_issue.py +12 -0
  61. lintro/parsers/prettier/prettier_parser.py +1 -1
  62. lintro/parsers/pytest/__init__.py +21 -0
  63. lintro/parsers/pytest/pytest_issue.py +28 -0
  64. lintro/parsers/pytest/pytest_parser.py +483 -0
  65. lintro/parsers/ruff/ruff_parser.py +6 -2
  66. lintro/parsers/yamllint/__init__.py +1 -0
  67. lintro/tools/__init__.py +3 -1
  68. lintro/tools/core/__init__.py +1 -0
  69. lintro/tools/core/timeout_utils.py +112 -0
  70. lintro/tools/core/tool_base.py +286 -50
  71. lintro/tools/core/tool_manager.py +77 -24
  72. lintro/tools/core/version_requirements.py +482 -0
  73. lintro/tools/implementations/__init__.py +1 -0
  74. lintro/tools/implementations/pytest/pytest_command_builder.py +311 -0
  75. lintro/tools/implementations/pytest/pytest_config.py +200 -0
  76. lintro/tools/implementations/pytest/pytest_error_handler.py +128 -0
  77. lintro/tools/implementations/pytest/pytest_executor.py +122 -0
  78. lintro/tools/implementations/pytest/pytest_handlers.py +375 -0
  79. lintro/tools/implementations/pytest/pytest_option_validators.py +212 -0
  80. lintro/tools/implementations/pytest/pytest_output_processor.py +408 -0
  81. lintro/tools/implementations/pytest/pytest_result_processor.py +113 -0
  82. lintro/tools/implementations/pytest/pytest_utils.py +697 -0
  83. lintro/tools/implementations/tool_actionlint.py +106 -16
  84. lintro/tools/implementations/tool_bandit.py +34 -29
  85. lintro/tools/implementations/tool_black.py +236 -29
  86. lintro/tools/implementations/tool_darglint.py +183 -22
  87. lintro/tools/implementations/tool_eslint.py +374 -0
  88. lintro/tools/implementations/tool_hadolint.py +94 -25
  89. lintro/tools/implementations/tool_markdownlint.py +354 -0
  90. lintro/tools/implementations/tool_prettier.py +317 -24
  91. lintro/tools/implementations/tool_pytest.py +327 -0
  92. lintro/tools/implementations/tool_ruff.py +278 -84
  93. lintro/tools/implementations/tool_yamllint.py +448 -34
  94. lintro/tools/tool_enum.py +8 -0
  95. lintro/utils/__init__.py +1 -0
  96. lintro/utils/ascii_normalize_cli.py +5 -0
  97. lintro/utils/config.py +41 -18
  98. lintro/utils/console_logger.py +211 -25
  99. lintro/utils/path_utils.py +42 -0
  100. lintro/utils/tool_executor.py +339 -45
  101. lintro/utils/tool_utils.py +51 -24
  102. lintro/utils/unified_config.py +926 -0
  103. {lintro-0.6.2.dist-info → lintro-0.17.2.dist-info}/METADATA +172 -30
  104. lintro-0.17.2.dist-info/RECORD +134 -0
  105. lintro-0.6.2.dist-info/RECORD +0 -96
  106. {lintro-0.6.2.dist-info → lintro-0.17.2.dist-info}/WHEEL +0 -0
  107. {lintro-0.6.2.dist-info → lintro-0.17.2.dist-info}/entry_points.txt +0 -0
  108. {lintro-0.6.2.dist-info → lintro-0.17.2.dist-info}/licenses/LICENSE +0 -0
  109. {lintro-0.6.2.dist-info → lintro-0.17.2.dist-info}/top_level.txt +0 -0
@@ -27,14 +27,26 @@ from lintro.formatters.tools.darglint_formatter import (
27
27
  DarglintTableDescriptor,
28
28
  format_darglint_issues,
29
29
  )
30
+ from lintro.formatters.tools.eslint_formatter import (
31
+ EslintTableDescriptor,
32
+ format_eslint_issues,
33
+ )
30
34
  from lintro.formatters.tools.hadolint_formatter import (
31
35
  HadolintTableDescriptor,
32
36
  format_hadolint_issues,
33
37
  )
38
+ from lintro.formatters.tools.markdownlint_formatter import (
39
+ MarkdownlintTableDescriptor,
40
+ format_markdownlint_issues,
41
+ )
34
42
  from lintro.formatters.tools.prettier_formatter import (
35
43
  PrettierTableDescriptor,
36
44
  format_prettier_issues,
37
45
  )
46
+ from lintro.formatters.tools.pytest_formatter import (
47
+ PytestFailuresTableDescriptor,
48
+ format_pytest_issues,
49
+ )
38
50
  from lintro.formatters.tools.ruff_formatter import (
39
51
  RuffTableDescriptor,
40
52
  format_ruff_issues,
@@ -47,9 +59,13 @@ from lintro.parsers.bandit.bandit_parser import parse_bandit_output
47
59
  from lintro.parsers.black.black_issue import BlackIssue
48
60
  from lintro.parsers.black.black_parser import parse_black_output
49
61
  from lintro.parsers.darglint.darglint_parser import parse_darglint_output
62
+ from lintro.parsers.eslint.eslint_issue import EslintIssue
63
+ from lintro.parsers.eslint.eslint_parser import parse_eslint_output
50
64
  from lintro.parsers.hadolint.hadolint_parser import parse_hadolint_output
65
+ from lintro.parsers.markdownlint.markdownlint_parser import parse_markdownlint_output
51
66
  from lintro.parsers.prettier.prettier_issue import PrettierIssue
52
67
  from lintro.parsers.prettier.prettier_parser import parse_prettier_output
68
+ from lintro.parsers.pytest.pytest_parser import parse_pytest_text_output
53
69
  from lintro.parsers.ruff.ruff_issue import RuffFormatIssue, RuffIssue
54
70
  from lintro.parsers.ruff.ruff_parser import parse_ruff_output
55
71
  from lintro.parsers.yamllint.yamllint_parser import parse_yamllint_output
@@ -57,13 +73,16 @@ from lintro.parsers.yamllint.yamllint_parser import parse_yamllint_output
57
73
  # Constants
58
74
  TOOL_TABLE_FORMATTERS: dict[str, tuple] = {
59
75
  "darglint": (DarglintTableDescriptor(), format_darglint_issues),
76
+ "eslint": (EslintTableDescriptor(), format_eslint_issues),
60
77
  "hadolint": (HadolintTableDescriptor(), format_hadolint_issues),
61
78
  "black": (BlackTableDescriptor(), format_black_issues),
62
79
  "prettier": (PrettierTableDescriptor(), format_prettier_issues),
80
+ "pytest": (PytestFailuresTableDescriptor(), format_pytest_issues),
63
81
  "ruff": (RuffTableDescriptor(), format_ruff_issues),
64
82
  "yamllint": (YamllintTableDescriptor(), format_yamllint_issues),
65
83
  "actionlint": (ActionlintTableDescriptor(), format_actionlint_issues),
66
84
  "bandit": (BanditTableDescriptor(), format_bandit_issues),
85
+ "markdownlint": (MarkdownlintTableDescriptor(), format_markdownlint_issues),
67
86
  }
68
87
  VENV_PATTERNS: list[str] = [
69
88
  "venv",
@@ -104,7 +123,7 @@ def parse_tool_list(tools_str: str | None) -> list[str]:
104
123
  try:
105
124
  result.append(ToolEnum[t.upper()])
106
125
  except KeyError:
107
- raise ValueError(f"Unknown core: {t}")
126
+ raise ValueError(f"Unknown core: {t}") from None
108
127
  return result
109
128
 
110
129
 
@@ -200,10 +219,7 @@ def get_table_columns(
200
219
  columns = expected_columns
201
220
  else:
202
221
  # Fallback: use all unique keys from the first issue
203
- if issues:
204
- columns = list(issues[0].keys())
205
- else:
206
- columns = []
222
+ columns = list(issues[0].keys()) if issues else []
207
223
 
208
224
  # Convert issues to rows
209
225
  rows: list[list[str]] = []
@@ -307,9 +323,19 @@ def format_tool_output(
307
323
  isinstance(i, RuffIssue) and getattr(i, "fixable", False)
308
324
  )
309
325
  if tool == "prettier":
310
- return lambda i: isinstance(i, PrettierIssue) or True
326
+ return lambda i: isinstance(i, PrettierIssue)
311
327
  if tool == "black":
312
- return lambda i: isinstance(i, BlackIssue) or True
328
+ return lambda i: isinstance(i, BlackIssue) and getattr(
329
+ i,
330
+ "fixable",
331
+ True,
332
+ )
333
+ if tool == "eslint":
334
+ return lambda i: isinstance(i, EslintIssue) and getattr(
335
+ i,
336
+ "fixable",
337
+ False,
338
+ )
313
339
  return None
314
340
 
315
341
  is_fixable = _is_fixable_predicate(tool_name)
@@ -367,10 +393,14 @@ def format_tool_output(
367
393
  parsed_issues = parse_black_output(output=output)
368
394
  elif tool_name == "darglint":
369
395
  parsed_issues = parse_darglint_output(output=output)
396
+ elif tool_name == "eslint":
397
+ parsed_issues = parse_eslint_output(output=output)
370
398
  elif tool_name == "hadolint":
371
399
  parsed_issues = parse_hadolint_output(output=output)
372
400
  elif tool_name == "yamllint":
373
401
  parsed_issues = parse_yamllint_output(output=output)
402
+ elif tool_name == "markdownlint":
403
+ parsed_issues = parse_markdownlint_output(output=output)
374
404
  elif tool_name == "bandit":
375
405
  # Bandit emits JSON; try parsing when raw output is provided
376
406
  try:
@@ -379,6 +409,9 @@ def format_tool_output(
379
409
  )
380
410
  except Exception:
381
411
  parsed_issues = []
412
+ elif tool_name == "pytest":
413
+ # Pytest emits text output; parse it
414
+ parsed_issues = parse_pytest_text_output(output=output)
382
415
 
383
416
  if parsed_issues and tool_name in TOOL_TABLE_FORMATTERS:
384
417
  _, formatter_func = TOOL_TABLE_FORMATTERS[tool_name]
@@ -394,21 +427,17 @@ def walk_files_with_excludes(
394
427
  exclude_patterns: list[str],
395
428
  include_venv: bool = False,
396
429
  ) -> list[str]:
397
- """Walk through directories and find files matching patterns, excluding
398
- specified patterns.
430
+ """Return files under ``paths`` matching patterns and not excluded.
399
431
 
400
432
  Args:
401
- paths: list[str]: List of file or directory paths to search.
402
- file_patterns: list[str]: List of file patterns to include (e.g.,
403
- ["*.py", "*.js"]).
404
- exclude_patterns: list[str]: List of patterns to exclude (e.g.,
405
- ["__pycache__", "*.pyc"]).
406
- include_venv: bool: Whether to include virtual environment directories.
433
+ paths: list[str]: Files or directories to search.
434
+ file_patterns: list[str]: Glob patterns to include.
435
+ exclude_patterns: list[str]: Glob patterns to exclude.
436
+ include_venv: bool: Include virtual environment directories when True.
407
437
 
408
438
  Returns:
409
- list[str]: List of file paths that match the patterns and are not excluded.
439
+ list[str]: Sorted file paths matching include filters and not excluded.
410
440
  """
411
-
412
441
  all_files: list[str] = []
413
442
 
414
443
  for path in paths:
@@ -438,13 +467,11 @@ def walk_files_with_excludes(
438
467
  matches_pattern = True
439
468
  break
440
469
 
441
- if matches_pattern:
442
- # Check if file should be excluded
443
- if not should_exclude_path(
444
- path=rel_path,
445
- exclude_patterns=exclude_patterns,
446
- ):
447
- all_files.append(file_path)
470
+ if matches_pattern and not should_exclude_path(
471
+ path=rel_path,
472
+ exclude_patterns=exclude_patterns,
473
+ ):
474
+ all_files.append(file_path)
448
475
 
449
476
  return sorted(all_files)
450
477