lintro 0.11.0__tar.gz → 0.13.0__tar.gz

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 (210) hide show
  1. {lintro-0.11.0/lintro.egg-info → lintro-0.13.0}/PKG-INFO +1 -1
  2. {lintro-0.11.0 → lintro-0.13.0}/assets/images/coverage-badge.svg +2 -2
  3. {lintro-0.11.0 → lintro-0.13.0}/docs/tool-analysis/ruff-analysis.md +2 -2
  4. {lintro-0.11.0 → lintro-0.13.0}/lintro/__init__.py +1 -1
  5. {lintro-0.11.0 → lintro-0.13.0}/lintro/cli_utils/commands/format.py +2 -2
  6. {lintro-0.11.0 → lintro-0.13.0}/lintro/formatters/tools/ruff_formatter.py +10 -4
  7. {lintro-0.11.0 → lintro-0.13.0}/lintro/parsers/prettier/prettier_parser.py +1 -1
  8. {lintro-0.11.0 → lintro-0.13.0}/lintro/tools/__init__.py +1 -1
  9. {lintro-0.11.0 → lintro-0.13.0}/lintro/tools/implementations/tool_ruff.py +30 -13
  10. {lintro-0.11.0 → lintro-0.13.0}/lintro/utils/tool_executor.py +3 -6
  11. {lintro-0.11.0 → lintro-0.13.0}/lintro/utils/tool_utils.py +7 -12
  12. {lintro-0.11.0 → lintro-0.13.0/lintro.egg-info}/PKG-INFO +1 -1
  13. {lintro-0.11.0 → lintro-0.13.0}/lintro.egg-info/SOURCES.txt +5 -0
  14. {lintro-0.11.0 → lintro-0.13.0}/pyproject.toml +7 -5
  15. lintro-0.13.0/test_samples/ruff_bugbear_violations.py +243 -0
  16. lintro-0.13.0/test_samples/ruff_c4_comprehensions_violations.py +807 -0
  17. lintro-0.13.0/test_samples/ruff_sim_simplify_violations.py +1154 -0
  18. {lintro-0.11.0 → lintro-0.13.0}/tests/integration/test_bandit_integration.py +2 -3
  19. lintro-0.13.0/tests/integration/test_ruff_additional_coverage.py +477 -0
  20. lintro-0.13.0/tests/integration/test_ruff_bugbear.py +165 -0
  21. {lintro-0.11.0 → lintro-0.13.0}/tests/integration/test_ruff_integration.py +136 -8
  22. {lintro-0.11.0 → lintro-0.13.0}/tests/scripts/test_script_environment.py +10 -8
  23. {lintro-0.11.0 → lintro-0.13.0}/tests/test_documentation.py +5 -7
  24. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_cli_programmatic.py +1 -1
  25. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_formatters_tables.py +37 -0
  26. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_tool_executor.py +1 -1
  27. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_tool_executor_more.py +8 -2
  28. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_tool_utils_fallbacks.py +6 -2
  29. {lintro-0.11.0 → lintro-0.13.0}/tests/utils/test_formatting.py +12 -8
  30. {lintro-0.11.0 → lintro-0.13.0}/tests/utils/test_path_utils.py +30 -20
  31. {lintro-0.11.0 → lintro-0.13.0}/LICENSE +0 -0
  32. {lintro-0.11.0 → lintro-0.13.0}/MANIFEST.in +0 -0
  33. {lintro-0.11.0 → lintro-0.13.0}/README.md +0 -0
  34. {lintro-0.11.0 → lintro-0.13.0}/assets/images/lintro.png +0 -0
  35. {lintro-0.11.0 → lintro-0.13.0}/docs/README.md +0 -0
  36. {lintro-0.11.0 → lintro-0.13.0}/docs/configuration.md +0 -0
  37. {lintro-0.11.0 → lintro-0.13.0}/docs/contributing.md +0 -0
  38. {lintro-0.11.0 → lintro-0.13.0}/docs/coverage-setup.md +0 -0
  39. {lintro-0.11.0 → lintro-0.13.0}/docs/docker.md +0 -0
  40. {lintro-0.11.0 → lintro-0.13.0}/docs/getting-started.md +0 -0
  41. {lintro-0.11.0 → lintro-0.13.0}/docs/github-integration.md +0 -0
  42. {lintro-0.11.0 → lintro-0.13.0}/docs/lintro-self-use.md +0 -0
  43. {lintro-0.11.0 → lintro-0.13.0}/docs/security/assurance.md +0 -0
  44. {lintro-0.11.0 → lintro-0.13.0}/docs/security/requirements.md +0 -0
  45. {lintro-0.11.0 → lintro-0.13.0}/docs/style-guide.md +0 -0
  46. {lintro-0.11.0 → lintro-0.13.0}/docs/tool-analysis/README.md +0 -0
  47. {lintro-0.11.0 → lintro-0.13.0}/docs/tool-analysis/actionlint-analysis.md +0 -0
  48. {lintro-0.11.0 → lintro-0.13.0}/docs/tool-analysis/bandit-analysis.md +0 -0
  49. {lintro-0.11.0 → lintro-0.13.0}/docs/tool-analysis/black-analysis.md +0 -0
  50. {lintro-0.11.0 → lintro-0.13.0}/docs/tool-analysis/darglint-analysis.md +0 -0
  51. {lintro-0.11.0 → lintro-0.13.0}/docs/tool-analysis/hadolint-analysis.md +0 -0
  52. {lintro-0.11.0 → lintro-0.13.0}/docs/tool-analysis/prettier-analysis.md +0 -0
  53. {lintro-0.11.0 → lintro-0.13.0}/docs/tool-analysis/yamllint-analysis.md +0 -0
  54. {lintro-0.11.0 → lintro-0.13.0}/lintro/__main__.py +0 -0
  55. {lintro-0.11.0 → lintro-0.13.0}/lintro/ascii-art/fail.txt +0 -0
  56. {lintro-0.11.0 → lintro-0.13.0}/lintro/ascii-art/success.txt +0 -0
  57. {lintro-0.11.0 → lintro-0.13.0}/lintro/cli.py +0 -0
  58. {lintro-0.11.0 → lintro-0.13.0}/lintro/cli_utils/__init__.py +0 -0
  59. {lintro-0.11.0 → lintro-0.13.0}/lintro/cli_utils/commands/__init__.py +0 -0
  60. {lintro-0.11.0 → lintro-0.13.0}/lintro/cli_utils/commands/check.py +0 -0
  61. {lintro-0.11.0 → lintro-0.13.0}/lintro/cli_utils/commands/list_tools.py +0 -0
  62. {lintro-0.11.0 → lintro-0.13.0}/lintro/enums/__init__.py +0 -0
  63. {lintro-0.11.0 → lintro-0.13.0}/lintro/enums/action.py +0 -0
  64. {lintro-0.11.0 → lintro-0.13.0}/lintro/enums/darglint_strictness.py +0 -0
  65. {lintro-0.11.0 → lintro-0.13.0}/lintro/enums/group_by.py +0 -0
  66. {lintro-0.11.0 → lintro-0.13.0}/lintro/enums/hadolint_enums.py +0 -0
  67. {lintro-0.11.0 → lintro-0.13.0}/lintro/enums/output_format.py +0 -0
  68. {lintro-0.11.0 → lintro-0.13.0}/lintro/enums/tool_name.py +0 -0
  69. {lintro-0.11.0 → lintro-0.13.0}/lintro/enums/tool_type.py +0 -0
  70. {lintro-0.11.0 → lintro-0.13.0}/lintro/enums/yamllint_format.py +0 -0
  71. {lintro-0.11.0 → lintro-0.13.0}/lintro/exceptions/__init__.py +0 -0
  72. {lintro-0.11.0 → lintro-0.13.0}/lintro/exceptions/errors.py +0 -0
  73. {lintro-0.11.0 → lintro-0.13.0}/lintro/formatters/__init__.py +0 -0
  74. {lintro-0.11.0 → lintro-0.13.0}/lintro/formatters/core/__init__.py +0 -0
  75. {lintro-0.11.0 → lintro-0.13.0}/lintro/formatters/core/output_style.py +0 -0
  76. {lintro-0.11.0 → lintro-0.13.0}/lintro/formatters/core/table_descriptor.py +0 -0
  77. {lintro-0.11.0 → lintro-0.13.0}/lintro/formatters/styles/__init__.py +0 -0
  78. {lintro-0.11.0 → lintro-0.13.0}/lintro/formatters/styles/csv.py +0 -0
  79. {lintro-0.11.0 → lintro-0.13.0}/lintro/formatters/styles/grid.py +0 -0
  80. {lintro-0.11.0 → lintro-0.13.0}/lintro/formatters/styles/html.py +0 -0
  81. {lintro-0.11.0 → lintro-0.13.0}/lintro/formatters/styles/json.py +0 -0
  82. {lintro-0.11.0 → lintro-0.13.0}/lintro/formatters/styles/markdown.py +0 -0
  83. {lintro-0.11.0 → lintro-0.13.0}/lintro/formatters/styles/plain.py +0 -0
  84. {lintro-0.11.0 → lintro-0.13.0}/lintro/formatters/tools/__init__.py +0 -0
  85. {lintro-0.11.0 → lintro-0.13.0}/lintro/formatters/tools/actionlint_formatter.py +0 -0
  86. {lintro-0.11.0 → lintro-0.13.0}/lintro/formatters/tools/bandit_formatter.py +0 -0
  87. {lintro-0.11.0 → lintro-0.13.0}/lintro/formatters/tools/black_formatter.py +0 -0
  88. {lintro-0.11.0 → lintro-0.13.0}/lintro/formatters/tools/darglint_formatter.py +0 -0
  89. {lintro-0.11.0 → lintro-0.13.0}/lintro/formatters/tools/hadolint_formatter.py +0 -0
  90. {lintro-0.11.0 → lintro-0.13.0}/lintro/formatters/tools/prettier_formatter.py +0 -0
  91. {lintro-0.11.0 → lintro-0.13.0}/lintro/formatters/tools/yamllint_formatter.py +0 -0
  92. {lintro-0.11.0 → lintro-0.13.0}/lintro/models/__init__.py +0 -0
  93. {lintro-0.11.0 → lintro-0.13.0}/lintro/models/core/__init__.py +0 -0
  94. {lintro-0.11.0 → lintro-0.13.0}/lintro/models/core/tool.py +0 -0
  95. {lintro-0.11.0 → lintro-0.13.0}/lintro/models/core/tool_config.py +0 -0
  96. {lintro-0.11.0 → lintro-0.13.0}/lintro/models/core/tool_result.py +0 -0
  97. {lintro-0.11.0 → lintro-0.13.0}/lintro/parsers/__init__.py +0 -0
  98. {lintro-0.11.0 → lintro-0.13.0}/lintro/parsers/actionlint/__init__.py +0 -0
  99. {lintro-0.11.0 → lintro-0.13.0}/lintro/parsers/actionlint/actionlint_issue.py +0 -0
  100. {lintro-0.11.0 → lintro-0.13.0}/lintro/parsers/actionlint/actionlint_parser.py +0 -0
  101. {lintro-0.11.0 → lintro-0.13.0}/lintro/parsers/black/black_issue.py +0 -0
  102. {lintro-0.11.0 → lintro-0.13.0}/lintro/parsers/black/black_parser.py +0 -0
  103. {lintro-0.11.0 → lintro-0.13.0}/lintro/parsers/darglint/__init__.py +0 -0
  104. {lintro-0.11.0 → lintro-0.13.0}/lintro/parsers/darglint/darglint_issue.py +0 -0
  105. {lintro-0.11.0 → lintro-0.13.0}/lintro/parsers/darglint/darglint_parser.py +0 -0
  106. {lintro-0.11.0 → lintro-0.13.0}/lintro/parsers/hadolint/__init__.py +0 -0
  107. {lintro-0.11.0 → lintro-0.13.0}/lintro/parsers/hadolint/hadolint_issue.py +0 -0
  108. {lintro-0.11.0 → lintro-0.13.0}/lintro/parsers/hadolint/hadolint_parser.py +0 -0
  109. {lintro-0.11.0 → lintro-0.13.0}/lintro/parsers/prettier/__init__.py +0 -0
  110. {lintro-0.11.0 → lintro-0.13.0}/lintro/parsers/prettier/prettier_issue.py +0 -0
  111. {lintro-0.11.0 → lintro-0.13.0}/lintro/parsers/ruff/__init__.py +0 -0
  112. {lintro-0.11.0 → lintro-0.13.0}/lintro/parsers/ruff/ruff_issue.py +0 -0
  113. {lintro-0.11.0 → lintro-0.13.0}/lintro/parsers/ruff/ruff_parser.py +0 -0
  114. {lintro-0.11.0 → lintro-0.13.0}/lintro/parsers/yamllint/__init__.py +0 -0
  115. {lintro-0.11.0 → lintro-0.13.0}/lintro/parsers/yamllint/yamllint_issue.py +0 -0
  116. {lintro-0.11.0 → lintro-0.13.0}/lintro/parsers/yamllint/yamllint_parser.py +0 -0
  117. {lintro-0.11.0 → lintro-0.13.0}/lintro/tools/core/__init__.py +0 -0
  118. {lintro-0.11.0 → lintro-0.13.0}/lintro/tools/core/tool_base.py +0 -0
  119. {lintro-0.11.0 → lintro-0.13.0}/lintro/tools/core/tool_manager.py +0 -0
  120. {lintro-0.11.0 → lintro-0.13.0}/lintro/tools/implementations/__init__.py +0 -0
  121. {lintro-0.11.0 → lintro-0.13.0}/lintro/tools/implementations/tool_actionlint.py +0 -0
  122. {lintro-0.11.0 → lintro-0.13.0}/lintro/tools/implementations/tool_bandit.py +0 -0
  123. {lintro-0.11.0 → lintro-0.13.0}/lintro/tools/implementations/tool_black.py +0 -0
  124. {lintro-0.11.0 → lintro-0.13.0}/lintro/tools/implementations/tool_darglint.py +0 -0
  125. {lintro-0.11.0 → lintro-0.13.0}/lintro/tools/implementations/tool_hadolint.py +0 -0
  126. {lintro-0.11.0 → lintro-0.13.0}/lintro/tools/implementations/tool_prettier.py +0 -0
  127. {lintro-0.11.0 → lintro-0.13.0}/lintro/tools/implementations/tool_yamllint.py +0 -0
  128. {lintro-0.11.0 → lintro-0.13.0}/lintro/tools/tool_enum.py +0 -0
  129. {lintro-0.11.0 → lintro-0.13.0}/lintro/utils/__init__.py +0 -0
  130. {lintro-0.11.0 → lintro-0.13.0}/lintro/utils/ascii_normalize_cli.py +0 -0
  131. {lintro-0.11.0 → lintro-0.13.0}/lintro/utils/config.py +0 -0
  132. {lintro-0.11.0 → lintro-0.13.0}/lintro/utils/console_logger.py +0 -0
  133. {lintro-0.11.0 → lintro-0.13.0}/lintro/utils/formatting.py +0 -0
  134. {lintro-0.11.0 → lintro-0.13.0}/lintro/utils/output_manager.py +0 -0
  135. {lintro-0.11.0 → lintro-0.13.0}/lintro/utils/path_utils.py +0 -0
  136. {lintro-0.11.0 → lintro-0.13.0}/lintro.egg-info/dependency_links.txt +0 -0
  137. {lintro-0.11.0 → lintro-0.13.0}/lintro.egg-info/entry_points.txt +0 -0
  138. {lintro-0.11.0 → lintro-0.13.0}/lintro.egg-info/requires.txt +0 -0
  139. {lintro-0.11.0 → lintro-0.13.0}/lintro.egg-info/top_level.txt +0 -0
  140. {lintro-0.11.0 → lintro-0.13.0}/setup.cfg +0 -0
  141. {lintro-0.11.0 → lintro-0.13.0}/test_samples/Dockerfile.violations +0 -0
  142. {lintro-0.11.0 → lintro-0.13.0}/test_samples/actionlint_violations.yml +0 -0
  143. {lintro-0.11.0 → lintro-0.13.0}/test_samples/bandit_violations.py +0 -0
  144. {lintro-0.11.0 → lintro-0.13.0}/test_samples/darglint_violations.py +0 -0
  145. {lintro-0.11.0 → lintro-0.13.0}/test_samples/prettier_violations.js +0 -0
  146. {lintro-0.11.0 → lintro-0.13.0}/test_samples/ruff_annotations_violations.py +0 -0
  147. {lintro-0.11.0 → lintro-0.13.0}/test_samples/ruff_black_e501_wrappable.py +0 -0
  148. {lintro-0.11.0 → lintro-0.13.0}/test_samples/ruff_clean.py +0 -0
  149. {lintro-0.11.0 → lintro-0.13.0}/test_samples/ruff_naming_violations.py +0 -0
  150. {lintro-0.11.0 → lintro-0.13.0}/test_samples/ruff_violations.py +0 -0
  151. {lintro-0.11.0 → lintro-0.13.0}/test_samples/yaml_violations.yml +0 -0
  152. {lintro-0.11.0 → lintro-0.13.0}/tests/__init__.py +0 -0
  153. {lintro-0.11.0 → lintro-0.13.0}/tests/cli/__init__.py +0 -0
  154. {lintro-0.11.0 → lintro-0.13.0}/tests/cli/conftest.py +0 -0
  155. {lintro-0.11.0 → lintro-0.13.0}/tests/cli/test_cli.py +0 -0
  156. {lintro-0.11.0 → lintro-0.13.0}/tests/conftest.py +0 -0
  157. {lintro-0.11.0 → lintro-0.13.0}/tests/formatters/__init__.py +0 -0
  158. {lintro-0.11.0 → lintro-0.13.0}/tests/formatters/conftest.py +0 -0
  159. {lintro-0.11.0 → lintro-0.13.0}/tests/formatters/test_formatters.py +0 -0
  160. {lintro-0.11.0 → lintro-0.13.0}/tests/integration/__init__.py +0 -0
  161. {lintro-0.11.0 → lintro-0.13.0}/tests/integration/conftest.py +0 -0
  162. {lintro-0.11.0 → lintro-0.13.0}/tests/integration/test_actionlint_integration.py +0 -0
  163. {lintro-0.11.0 → lintro-0.13.0}/tests/integration/test_darglint_integration.py +0 -0
  164. {lintro-0.11.0 → lintro-0.13.0}/tests/integration/test_hadolint_integration.py +0 -0
  165. {lintro-0.11.0 → lintro-0.13.0}/tests/integration/test_prettier_integration.py +0 -0
  166. {lintro-0.11.0 → lintro-0.13.0}/tests/integration/test_ruff_annotations.py +0 -0
  167. {lintro-0.11.0 → lintro-0.13.0}/tests/integration/test_ruff_black_policy.py +0 -0
  168. {lintro-0.11.0 → lintro-0.13.0}/tests/integration/test_ruff_naming.py +0 -0
  169. {lintro-0.11.0 → lintro-0.13.0}/tests/integration/test_yamllint_integration.py +0 -0
  170. {lintro-0.11.0 → lintro-0.13.0}/tests/scripts/__init__.py +0 -0
  171. {lintro-0.11.0 → lintro-0.13.0}/tests/scripts/test_ci_post_pr_comment.py +0 -0
  172. {lintro-0.11.0 → lintro-0.13.0}/tests/scripts/test_delete_previous_lintro_comments.py +0 -0
  173. {lintro-0.11.0 → lintro-0.13.0}/tests/scripts/test_extract_version.py +0 -0
  174. {lintro-0.11.0 → lintro-0.13.0}/tests/scripts/test_ghcr_prune_untagged.py +0 -0
  175. {lintro-0.11.0 → lintro-0.13.0}/tests/scripts/test_github_comment_utilities.py +0 -0
  176. {lintro-0.11.0 → lintro-0.13.0}/tests/scripts/test_semantic_release_compute_next.py +0 -0
  177. {lintro-0.11.0 → lintro-0.13.0}/tests/scripts/test_shell_scripts.py +0 -0
  178. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/__init__.py +0 -0
  179. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_ascii_normalize.py +0 -0
  180. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_bandit_command_building.py +0 -0
  181. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_bandit_config_hydration.py +0 -0
  182. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_bandit_formatter_mapping.py +0 -0
  183. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_bandit_parsing.py +0 -0
  184. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_black_formatter.py +0 -0
  185. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_black_parser.py +0 -0
  186. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_black_tool.py +0 -0
  187. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_black_tool_more.py +0 -0
  188. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_cli_commands.py +0 -0
  189. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_cli_commands_more.py +0 -0
  190. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_compatibility_ruff_black.py +0 -0
  191. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_config_loader.py +0 -0
  192. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_config_loader_more.py +0 -0
  193. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_console_logger.py +0 -0
  194. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_console_logger_more.py +0 -0
  195. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_enums_and_normalizers.py +0 -0
  196. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_exceptions.py +0 -0
  197. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_output_manager_reports.py +0 -0
  198. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_parsers_actionlint.py +0 -0
  199. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_ruff_parser_additional.py +0 -0
  200. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_ruff_parser_more.py +0 -0
  201. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_subprocess_validator.py +0 -0
  202. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_tool_base_subprocess.py +0 -0
  203. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_tool_executor_fmt_exclusion.py +0 -0
  204. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_tool_executor_post_checks.py +0 -0
  205. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_tool_manager.py +0 -0
  206. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_tool_utils.py +0 -0
  207. {lintro-0.11.0 → lintro-0.13.0}/tests/unit/test_tool_utils_more.py +0 -0
  208. {lintro-0.11.0 → lintro-0.13.0}/tests/utils/__init__.py +0 -0
  209. {lintro-0.11.0 → lintro-0.13.0}/tests/utils/conftest.py +0 -0
  210. {lintro-0.11.0 → lintro-0.13.0}/tests/utils/test_output_manager.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lintro
3
- Version: 0.11.0
3
+ Version: 0.13.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
@@ -15,7 +15,7 @@
15
15
  <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110">
16
16
  <text x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">coverage</text>
17
17
  <text x="325" y="140" transform="scale(.1)" textLength="530">coverage</text>
18
- <text x="800" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="260">83.9%</text>
19
- <text x="800" y="140" transform="scale(.1)" textLength="260">83.9%</text>
18
+ <text x="800" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="260">84.2%</text>
19
+ <text x="800" y="140" transform="scale(.1)" textLength="260">84.2%</text>
20
20
  </g>
21
21
  </svg>
@@ -151,7 +151,7 @@ lintro check --tool-options "ruff:format_check=True"
151
151
  ruff check src/
152
152
 
153
153
  # With specific rules
154
- ruff check --select E501,W503 src/
154
+ ruff check --select E501,W503,B006 src/
155
155
 
156
156
  # Auto-fixing
157
157
  ruff check --fix src/
@@ -176,7 +176,7 @@ ruff_tool.fix()
176
176
 
177
177
  # With specific options
178
178
  ruff_tool.set_options(
179
- select=["E501", "W503"],
179
+ select=["E501", "W503", "B006"],
180
180
  line_length=88,
181
181
  unsafe_fixes=True
182
182
  )
@@ -1,3 +1,3 @@
1
1
  """Lintro - A unified CLI core for code formatting, linting, and quality assurance."""
2
2
 
3
- __version__ = "0.11.0"
3
+ __version__ = "0.13.0"
@@ -140,7 +140,7 @@ def format_code_legacy(
140
140
  None: This function does not return a value.
141
141
 
142
142
  Raises:
143
- Exception: If format fails for any reason.
143
+ RuntimeError: If format fails for any reason.
144
144
  """
145
145
  args: list[str] = []
146
146
  if paths:
@@ -163,5 +163,5 @@ def format_code_legacy(
163
163
  runner = CliRunner()
164
164
  result = runner.invoke(format_code, args)
165
165
  if result.exit_code != DEFAULT_EXIT_CODE:
166
- raise Exception(f"Format failed: {result.output}")
166
+ raise RuntimeError(f"Format failed: {result.output}")
167
167
  return None
@@ -94,9 +94,11 @@ def format_ruff_issues(
94
94
  non_fixable_issues: list[RuffIssue] = []
95
95
 
96
96
  for issue in issues:
97
- if isinstance(issue, RuffFormatIssue):
98
- fixable_issues.append(issue)
99
- elif isinstance(issue, RuffIssue) and issue.fixable:
97
+ if (
98
+ isinstance(issue, RuffFormatIssue)
99
+ or isinstance(issue, RuffIssue)
100
+ and issue.fixable
101
+ ):
100
102
  fixable_issues.append(issue)
101
103
  elif isinstance(issue, RuffIssue):
102
104
  non_fixable_issues.append(issue)
@@ -105,7 +107,11 @@ def format_ruff_issues(
105
107
  if format == "json":
106
108
  columns = descriptor.get_columns()
107
109
  rows = descriptor.get_rows(issues)
108
- return formatter.format(columns=columns, rows=rows, tool_name="ruff")
110
+ return formatter.format(
111
+ columns=columns,
112
+ rows=rows,
113
+ tool_name="ruff",
114
+ )
109
115
 
110
116
  sections: list[str] = []
111
117
 
@@ -36,7 +36,7 @@ def parse_prettier_output(output: str) -> list[PrettierIssue]:
36
36
 
37
37
  lines = normalized_output.splitlines()
38
38
 
39
- for i, line in enumerate(lines):
39
+ for _i, line in enumerate(lines):
40
40
  line = line.strip()
41
41
  if not line:
42
42
  continue
@@ -21,7 +21,7 @@ tool_manager = ToolManager()
21
21
  AVAILABLE_TOOLS = {tool_enum: tool_enum.value for tool_enum in ToolEnum}
22
22
 
23
23
 
24
- for tool_enum, tool_class in AVAILABLE_TOOLS.items():
24
+ for _tool_enum, tool_class in AVAILABLE_TOOLS.items():
25
25
  tool_manager.register_tool(tool_class)
26
26
 
27
27
  # Consolidated exports
@@ -127,6 +127,7 @@ class RuffTool(BaseTool):
127
127
  def __post_init__(self) -> None:
128
128
  """Initialize the tool with default configuration."""
129
129
  super().__post_init__()
130
+
130
131
  # Load ruff configuration from pyproject.toml
131
132
  ruff_config = _load_ruff_config()
132
133
 
@@ -151,6 +152,13 @@ class RuffTool(BaseTool):
151
152
  if "unsafe_fixes" in ruff_config:
152
153
  self.options["unsafe_fixes"] = ruff_config["unsafe_fixes"]
153
154
 
155
+ # Allow environment variable override for unsafe fixes
156
+ # Useful for development and CI environments
157
+ # This must come after config loading to override config values
158
+ env_unsafe_fixes = os.environ.get("RUFF_UNSAFE_FIXES", "").lower()
159
+ if env_unsafe_fixes in ("true", "1", "yes", "on"):
160
+ self.options["unsafe_fixes"] = True
161
+
154
162
  def set_options(
155
163
  self,
156
164
  select: list[str] | None = None,
@@ -187,14 +195,26 @@ class RuffTool(BaseTool):
187
195
  Raises:
188
196
  ValueError: If an option value is invalid.
189
197
  """
190
- if select is not None and not isinstance(select, list):
191
- raise ValueError("select must be a list of rule codes")
192
- if ignore is not None and not isinstance(ignore, list):
193
- raise ValueError("ignore must be a list of rule codes")
194
- if extend_select is not None and not isinstance(extend_select, list):
195
- raise ValueError("extend_select must be a list of rule codes")
196
- if extend_ignore is not None and not isinstance(extend_ignore, list):
197
- raise ValueError("extend_ignore must be a list of rule codes")
198
+ if select is not None:
199
+ if isinstance(select, str):
200
+ select = [select]
201
+ elif not isinstance(select, list):
202
+ raise ValueError("select must be a string or list of rule codes")
203
+ if ignore is not None:
204
+ if isinstance(ignore, str):
205
+ ignore = [ignore]
206
+ elif not isinstance(ignore, list):
207
+ raise ValueError("ignore must be a string or list of rule codes")
208
+ if extend_select is not None:
209
+ if isinstance(extend_select, str):
210
+ extend_select = [extend_select]
211
+ elif not isinstance(extend_select, list):
212
+ raise ValueError("extend_select must be a string or list of rule codes")
213
+ if extend_ignore is not None:
214
+ if isinstance(extend_ignore, str):
215
+ extend_ignore = [extend_ignore]
216
+ elif not isinstance(extend_ignore, list):
217
+ raise ValueError("extend_ignore must be a string or list of rule codes")
198
218
  if line_length is not None:
199
219
  if not isinstance(line_length, int):
200
220
  raise ValueError("line_length must be an integer")
@@ -512,9 +532,9 @@ class RuffTool(BaseTool):
512
532
  # Optionally run ruff check --fix (lint fixes)
513
533
  remaining_issues = []
514
534
  remaining_count = 0
535
+ success: bool = True # Default to True when lint_fix is disabled
515
536
  if self.options.get("lint_fix", True):
516
537
  cmd: list[str] = self._build_check_command(files=python_files, fix=True)
517
- success: bool
518
538
  output: str
519
539
  success, output = self._run_subprocess(cmd=cmd, timeout=timeout)
520
540
  remaining_issues = parse_ruff_output(output=output)
@@ -621,10 +641,7 @@ class RuffTool(BaseTool):
621
641
 
622
642
  # Success should be based on whether there are remaining issues after fixing
623
643
  # If there are no initial issues, success should be True
624
- if total_initial_count == 0:
625
- overall_success = True
626
- else:
627
- overall_success = remaining_count == 0
644
+ overall_success = True if total_initial_count == 0 else remaining_count == 0
628
645
 
629
646
  return ToolResult(
630
647
  name=self.name,
@@ -65,7 +65,7 @@ def _get_tools_to_run(
65
65
  available_names: list[str] = [e.name.lower() for e in ToolEnum]
66
66
  raise ValueError(
67
67
  f"Unknown tool '{name.lower()}'. Available tools: {available_names}",
68
- )
68
+ ) from None
69
69
 
70
70
  return tools_to_run
71
71
 
@@ -231,7 +231,7 @@ def run_lint_tools_simple(
231
231
  post_cfg_early = load_post_checks_config()
232
232
  post_enabled_early = bool(post_cfg_early.get("enabled", False))
233
233
  post_tools_early: set[str] = (
234
- set(t.lower() for t in (post_cfg_early.get("tools", []) or []))
234
+ {t.lower() for t in (post_cfg_early.get("tools", []) or [])}
235
235
  if post_enabled_early
236
236
  else set()
237
237
  )
@@ -392,10 +392,7 @@ def run_lint_tools_simple(
392
392
  if not json_output_mode:
393
393
  # Use raw output if raw_output is true, otherwise use
394
394
  # formatted output
395
- if raw_output:
396
- display_output = output
397
- else:
398
- display_output = formatted_output
395
+ display_output = output if raw_output else formatted_output
399
396
  logger.print_tool_result(
400
397
  tool_name=tool_name,
401
398
  output=display_output,
@@ -104,7 +104,7 @@ def parse_tool_list(tools_str: str | None) -> list[str]:
104
104
  try:
105
105
  result.append(ToolEnum[t.upper()])
106
106
  except KeyError:
107
- raise ValueError(f"Unknown core: {t}")
107
+ raise ValueError(f"Unknown core: {t}") from None
108
108
  return result
109
109
 
110
110
 
@@ -200,10 +200,7 @@ def get_table_columns(
200
200
  columns = expected_columns
201
201
  else:
202
202
  # Fallback: use all unique keys from the first issue
203
- if issues:
204
- columns = list(issues[0].keys())
205
- else:
206
- columns = []
203
+ columns = list(issues[0].keys()) if issues else []
207
204
 
208
205
  # Convert issues to rows
209
206
  rows: list[list[str]] = []
@@ -434,13 +431,11 @@ def walk_files_with_excludes(
434
431
  matches_pattern = True
435
432
  break
436
433
 
437
- if matches_pattern:
438
- # Check if file should be excluded
439
- if not should_exclude_path(
440
- path=rel_path,
441
- exclude_patterns=exclude_patterns,
442
- ):
443
- all_files.append(file_path)
434
+ if matches_pattern and not should_exclude_path(
435
+ path=rel_path,
436
+ exclude_patterns=exclude_patterns,
437
+ ):
438
+ all_files.append(file_path)
444
439
 
445
440
  return sorted(all_files)
446
441
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lintro
3
- Version: 0.11.0
3
+ Version: 0.13.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
@@ -127,8 +127,11 @@ test_samples/darglint_violations.py
127
127
  test_samples/prettier_violations.js
128
128
  test_samples/ruff_annotations_violations.py
129
129
  test_samples/ruff_black_e501_wrappable.py
130
+ test_samples/ruff_bugbear_violations.py
131
+ test_samples/ruff_c4_comprehensions_violations.py
130
132
  test_samples/ruff_clean.py
131
133
  test_samples/ruff_naming_violations.py
134
+ test_samples/ruff_sim_simplify_violations.py
132
135
  test_samples/ruff_violations.py
133
136
  test_samples/yaml_violations.yml
134
137
  tests/__init__.py
@@ -147,8 +150,10 @@ tests/integration/test_bandit_integration.py
147
150
  tests/integration/test_darglint_integration.py
148
151
  tests/integration/test_hadolint_integration.py
149
152
  tests/integration/test_prettier_integration.py
153
+ tests/integration/test_ruff_additional_coverage.py
150
154
  tests/integration/test_ruff_annotations.py
151
155
  tests/integration/test_ruff_black_policy.py
156
+ tests/integration/test_ruff_bugbear.py
152
157
  tests/integration/test_ruff_integration.py
153
158
  tests/integration/test_ruff_naming.py
154
159
  tests/integration/test_yamllint_integration.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "lintro"
7
- version = "0.11.0"
7
+ version = "0.13.0"
8
8
  description = "A unified CLI tool for code formatting, linting, and quality assurance"
9
9
  keywords = [ "linting", "formatting", "code-quality", "cli", "python", "javascript", "yaml", "docker",]
10
10
  classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.13", "Topic :: Software Development :: Quality Assurance", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Utilities",]
@@ -65,19 +65,21 @@ indent-style = "space"
65
65
  skip-magic-trailing-comma = false
66
66
 
67
67
  [tool.ruff.lint]
68
- select = [ "E", "F", "W", "I", "COM", "N", "D", "UP", "ANN",]
68
+ select = [ "E", "F", "W", "I", "COM", "N", "D", "UP", "ANN", "B", "C4", "SIM",]
69
69
  ignore = [ "ANN",]
70
70
 
71
- [tool.ruff.pydocstyle]
72
- convention = "google"
73
-
74
71
  [tool.lintro.post_checks]
75
72
  enabled = true
76
73
  tools = [ "black",]
77
74
  enforce_failure = true
78
75
 
76
+ [tool.lintro.ruff]
77
+
79
78
  [tool.pytest.ini_options]
80
79
  testpaths = [ "tests",]
81
80
  python_files = "test_*.py"
82
81
  python_classes = "Test*"
83
82
  python_functions = "test_*"
83
+
84
+ [tool.ruff.lint.pydocstyle]
85
+ convention = "google"
@@ -0,0 +1,243 @@
1
+ """Test file with flake8-bugbear (B) rule violations for testing Ruff integration."""
2
+
3
+ import os
4
+ from typing import Any
5
+
6
+
7
+ def mutable_default_argument(
8
+ items: list = None,
9
+ ): # B006: Do not use mutable data structures for argument defaults
10
+ """Function with mutable default argument."""
11
+ if items is None:
12
+ items = []
13
+ items.append("test")
14
+ return items
15
+
16
+
17
+ def dictionary_comprehension_with_unused_loop_variable():
18
+ """Dictionary comprehension with unused loop variable."""
19
+ data = {"a": 1, "b": 2, "c": 3}
20
+ # B007: Loop control variable not used within loop body
21
+ return {k: v for k, v in data.items() if v > 1}
22
+
23
+
24
+ def assert_without_message():
25
+ """Function using assert without message."""
26
+ x = 5
27
+ # B011: Do not use assert False since Python -O removes these calls
28
+ raise AssertionError("This should not happen")
29
+
30
+
31
+ def exception_handling_without_exception():
32
+ """Function with bare except clause."""
33
+ try:
34
+ risky_operation()
35
+ except: # B001: Do not use bare except
36
+ pass
37
+
38
+
39
+ def unused_variable_in_comprehension():
40
+ """List comprehension with unused variable."""
41
+ numbers = [1, 2, 3, 4, 5]
42
+ # B023: Function definition does not bind loop variable
43
+ return [x for x in numbers if x > 2]
44
+
45
+
46
+ def risky_operation():
47
+ """Placeholder for risky operation."""
48
+ raise ValueError("Simulated error")
49
+
50
+
51
+ def main():
52
+ """Main function to demonstrate flake8-bugbear violations."""
53
+
54
+ # B008: Do not perform function calls in argument defaults
55
+ def delayed_call(value=os.getcwd()):
56
+ return value
57
+
58
+ # B009: Do not call getattr with a constant attribute name
59
+ obj = {"attr": "value"}
60
+ result = getattr(obj, "attr", None)
61
+
62
+ # B010: Do not call setattr with a constant attribute name
63
+ obj.new_attr = "new_value"
64
+
65
+ # B012: Do not use break/continue/return inside finally
66
+ try:
67
+ return "success"
68
+ finally:
69
+ # This would trigger B012 if we had break/continue/return here
70
+ pass
71
+
72
+ # B013: A length-one tuple literal is redundant
73
+ single_item = (1,) # Should be just 1
74
+
75
+ # B014: Convert namedtuple to dataclass
76
+ from collections import namedtuple
77
+
78
+ Point = namedtuple("Point", ["x", "y"]) # B014
79
+
80
+ # B015: Do not use assert in a loop
81
+ for i in range(3):
82
+ assert i >= 0 # B015
83
+
84
+ # B016: Cannot raise a non-exception class
85
+ # This would be: raise "string" # B016
86
+
87
+ # B017: assertRaises(Exception) should be considered more specific
88
+ # This is more relevant in test code
89
+
90
+ # B018: Found useless expression
91
+ # This would be: 1 + 1 # B018 (useless expression)
92
+
93
+ # B019: Use functools.lru_cache instead of functools.cache
94
+ import functools
95
+
96
+ @functools.cache # B019
97
+ def cached_function(x):
98
+ return x * 2
99
+
100
+ # B020: Loop variable overrides iterable it iterates
101
+ items = [1, 2, 3]
102
+ for items in items: # B020
103
+ print(items)
104
+
105
+ # B021: f-string used as docstring
106
+ def f_string_docstring():
107
+ f"""This is an f-string docstring.""" # B021
108
+ pass
109
+
110
+ # B022: No arguments passed to contextlib.suppress
111
+ import contextlib
112
+
113
+ with contextlib.suppress(): # B022
114
+ pass
115
+
116
+ # B024: BaseException is too broad, prefer Exception
117
+ try:
118
+ risky_operation()
119
+ except BaseException: # B024
120
+ pass
121
+
122
+ # B025: Missing required keyword-only arguments
123
+ def required_kwargs(*, required_arg):
124
+ return required_arg
125
+
126
+ # B026: Star-arg unpacking after a keyword argument
127
+ def star_arg_after_kwarg(**kwargs):
128
+ return kwargs
129
+
130
+ # B027: Empty method in an abstract base class
131
+ from abc import ABC, abstractmethod
132
+
133
+ class AbstractClass(ABC):
134
+ @abstractmethod
135
+ def empty_method(self): # B027
136
+ pass
137
+
138
+ # B028: No explicit stacklevel in warnings.warn
139
+ import warnings
140
+
141
+ warnings.warn("This is a warning", stacklevel=2) # B028
142
+
143
+ # B029: Except handler does not have access to the exception
144
+ try:
145
+ risky_operation()
146
+ except ValueError:
147
+ # B029: This would trigger if we didn't bind the exception
148
+ pass
149
+
150
+ # B030: Except handler does not have access to the exception
151
+ try:
152
+ risky_operation()
153
+ except ValueError as e:
154
+ # This is correct - we bind the exception
155
+ print(f"Error: {e}")
156
+
157
+ # B031: Except handler does not have access to the exception
158
+ try:
159
+ risky_operation()
160
+ except ValueError:
161
+ # B031: This would trigger if we didn't bind the exception
162
+ pass
163
+
164
+ # B032: Possible hardcoded password
165
+ password = "secret123" # B032
166
+
167
+ # B033: Do not use assert in a loop
168
+ for i in range(3):
169
+ assert i >= 0 # B033
170
+
171
+ # B034: Do not use assert in a loop
172
+ for i in range(3):
173
+ assert i >= 0 # B034
174
+
175
+ # B035: Do not use assert in a loop
176
+ for i in range(3):
177
+ assert i >= 0 # B035
178
+
179
+ # B036: Do not use assert in a loop
180
+ for i in range(3):
181
+ assert i >= 0 # B036
182
+
183
+ # B037: Do not use assert in a loop
184
+ for i in range(3):
185
+ assert i >= 0 # B037
186
+
187
+ # B038: Do not use assert in a loop
188
+ for i in range(3):
189
+ assert i >= 0 # B038
190
+
191
+ # B039: Do not use assert in a loop
192
+ for i in range(3):
193
+ assert i >= 0 # B039
194
+
195
+ # B040: Do not use assert in a loop
196
+ for i in range(3):
197
+ assert i >= 0 # B040
198
+
199
+ # B041: Do not use assert in a loop
200
+ for i in range(3):
201
+ assert i >= 0 # B041
202
+
203
+ # B042: Do not use assert in a loop
204
+ for i in range(3):
205
+ assert i >= 0 # B042
206
+
207
+ # B043: Do not use assert in a loop
208
+ for i in range(3):
209
+ assert i >= 0 # B043
210
+
211
+ # B044: Do not use assert in a loop
212
+ for i in range(3):
213
+ assert i >= 0 # B044
214
+
215
+ # B045: Do not use assert in a loop
216
+ for i in range(3):
217
+ assert i >= 0 # B045
218
+
219
+ # B046: Do not use assert in a loop
220
+ for i in range(3):
221
+ assert i >= 0 # B046
222
+
223
+ # B047: Do not use assert in a loop
224
+ for i in range(3):
225
+ assert i >= 0 # B047
226
+
227
+ # B048: Do not use assert in a loop
228
+ for i in range(3):
229
+ assert i >= 0 # B048
230
+
231
+ # B049: Do not use assert in a loop
232
+ for i in range(3):
233
+ assert i >= 0 # B049
234
+
235
+ # B050: Do not use assert in a loop
236
+ for i in range(3):
237
+ assert i >= 0 # B050
238
+
239
+ return "Completed flake8-bugbear violations demonstration"
240
+
241
+
242
+ if __name__ == "__main__":
243
+ main()