lintro 0.11.0__tar.gz → 0.12.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.

Potentially problematic release.


This version of lintro might be problematic. Click here for more details.

Files changed (207) hide show
  1. {lintro-0.11.0/lintro.egg-info → lintro-0.12.0}/PKG-INFO +1 -1
  2. {lintro-0.11.0 → lintro-0.12.0}/docs/tool-analysis/ruff-analysis.md +2 -2
  3. {lintro-0.11.0 → lintro-0.12.0}/lintro/__init__.py +1 -1
  4. {lintro-0.11.0 → lintro-0.12.0}/lintro/cli_utils/commands/format.py +2 -2
  5. {lintro-0.11.0 → lintro-0.12.0}/lintro/parsers/prettier/prettier_parser.py +1 -1
  6. {lintro-0.11.0 → lintro-0.12.0}/lintro/tools/__init__.py +1 -1
  7. {lintro-0.11.0 → lintro-0.12.0}/lintro/utils/tool_executor.py +1 -1
  8. {lintro-0.11.0 → lintro-0.12.0}/lintro/utils/tool_utils.py +1 -1
  9. {lintro-0.11.0 → lintro-0.12.0/lintro.egg-info}/PKG-INFO +1 -1
  10. {lintro-0.11.0 → lintro-0.12.0}/lintro.egg-info/SOURCES.txt +2 -0
  11. {lintro-0.11.0 → lintro-0.12.0}/pyproject.toml +5 -5
  12. lintro-0.12.0/test_samples/ruff_bugbear_violations.py +243 -0
  13. lintro-0.12.0/tests/integration/test_ruff_bugbear.py +165 -0
  14. {lintro-0.11.0 → lintro-0.12.0}/tests/scripts/test_script_environment.py +1 -1
  15. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_cli_programmatic.py +1 -1
  16. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_tool_executor_more.py +8 -2
  17. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_tool_utils_fallbacks.py +6 -2
  18. {lintro-0.11.0 → lintro-0.12.0}/LICENSE +0 -0
  19. {lintro-0.11.0 → lintro-0.12.0}/MANIFEST.in +0 -0
  20. {lintro-0.11.0 → lintro-0.12.0}/README.md +0 -0
  21. {lintro-0.11.0 → lintro-0.12.0}/assets/images/coverage-badge.svg +0 -0
  22. {lintro-0.11.0 → lintro-0.12.0}/assets/images/lintro.png +0 -0
  23. {lintro-0.11.0 → lintro-0.12.0}/docs/README.md +0 -0
  24. {lintro-0.11.0 → lintro-0.12.0}/docs/configuration.md +0 -0
  25. {lintro-0.11.0 → lintro-0.12.0}/docs/contributing.md +0 -0
  26. {lintro-0.11.0 → lintro-0.12.0}/docs/coverage-setup.md +0 -0
  27. {lintro-0.11.0 → lintro-0.12.0}/docs/docker.md +0 -0
  28. {lintro-0.11.0 → lintro-0.12.0}/docs/getting-started.md +0 -0
  29. {lintro-0.11.0 → lintro-0.12.0}/docs/github-integration.md +0 -0
  30. {lintro-0.11.0 → lintro-0.12.0}/docs/lintro-self-use.md +0 -0
  31. {lintro-0.11.0 → lintro-0.12.0}/docs/security/assurance.md +0 -0
  32. {lintro-0.11.0 → lintro-0.12.0}/docs/security/requirements.md +0 -0
  33. {lintro-0.11.0 → lintro-0.12.0}/docs/style-guide.md +0 -0
  34. {lintro-0.11.0 → lintro-0.12.0}/docs/tool-analysis/README.md +0 -0
  35. {lintro-0.11.0 → lintro-0.12.0}/docs/tool-analysis/actionlint-analysis.md +0 -0
  36. {lintro-0.11.0 → lintro-0.12.0}/docs/tool-analysis/bandit-analysis.md +0 -0
  37. {lintro-0.11.0 → lintro-0.12.0}/docs/tool-analysis/black-analysis.md +0 -0
  38. {lintro-0.11.0 → lintro-0.12.0}/docs/tool-analysis/darglint-analysis.md +0 -0
  39. {lintro-0.11.0 → lintro-0.12.0}/docs/tool-analysis/hadolint-analysis.md +0 -0
  40. {lintro-0.11.0 → lintro-0.12.0}/docs/tool-analysis/prettier-analysis.md +0 -0
  41. {lintro-0.11.0 → lintro-0.12.0}/docs/tool-analysis/yamllint-analysis.md +0 -0
  42. {lintro-0.11.0 → lintro-0.12.0}/lintro/__main__.py +0 -0
  43. {lintro-0.11.0 → lintro-0.12.0}/lintro/ascii-art/fail.txt +0 -0
  44. {lintro-0.11.0 → lintro-0.12.0}/lintro/ascii-art/success.txt +0 -0
  45. {lintro-0.11.0 → lintro-0.12.0}/lintro/cli.py +0 -0
  46. {lintro-0.11.0 → lintro-0.12.0}/lintro/cli_utils/__init__.py +0 -0
  47. {lintro-0.11.0 → lintro-0.12.0}/lintro/cli_utils/commands/__init__.py +0 -0
  48. {lintro-0.11.0 → lintro-0.12.0}/lintro/cli_utils/commands/check.py +0 -0
  49. {lintro-0.11.0 → lintro-0.12.0}/lintro/cli_utils/commands/list_tools.py +0 -0
  50. {lintro-0.11.0 → lintro-0.12.0}/lintro/enums/__init__.py +0 -0
  51. {lintro-0.11.0 → lintro-0.12.0}/lintro/enums/action.py +0 -0
  52. {lintro-0.11.0 → lintro-0.12.0}/lintro/enums/darglint_strictness.py +0 -0
  53. {lintro-0.11.0 → lintro-0.12.0}/lintro/enums/group_by.py +0 -0
  54. {lintro-0.11.0 → lintro-0.12.0}/lintro/enums/hadolint_enums.py +0 -0
  55. {lintro-0.11.0 → lintro-0.12.0}/lintro/enums/output_format.py +0 -0
  56. {lintro-0.11.0 → lintro-0.12.0}/lintro/enums/tool_name.py +0 -0
  57. {lintro-0.11.0 → lintro-0.12.0}/lintro/enums/tool_type.py +0 -0
  58. {lintro-0.11.0 → lintro-0.12.0}/lintro/enums/yamllint_format.py +0 -0
  59. {lintro-0.11.0 → lintro-0.12.0}/lintro/exceptions/__init__.py +0 -0
  60. {lintro-0.11.0 → lintro-0.12.0}/lintro/exceptions/errors.py +0 -0
  61. {lintro-0.11.0 → lintro-0.12.0}/lintro/formatters/__init__.py +0 -0
  62. {lintro-0.11.0 → lintro-0.12.0}/lintro/formatters/core/__init__.py +0 -0
  63. {lintro-0.11.0 → lintro-0.12.0}/lintro/formatters/core/output_style.py +0 -0
  64. {lintro-0.11.0 → lintro-0.12.0}/lintro/formatters/core/table_descriptor.py +0 -0
  65. {lintro-0.11.0 → lintro-0.12.0}/lintro/formatters/styles/__init__.py +0 -0
  66. {lintro-0.11.0 → lintro-0.12.0}/lintro/formatters/styles/csv.py +0 -0
  67. {lintro-0.11.0 → lintro-0.12.0}/lintro/formatters/styles/grid.py +0 -0
  68. {lintro-0.11.0 → lintro-0.12.0}/lintro/formatters/styles/html.py +0 -0
  69. {lintro-0.11.0 → lintro-0.12.0}/lintro/formatters/styles/json.py +0 -0
  70. {lintro-0.11.0 → lintro-0.12.0}/lintro/formatters/styles/markdown.py +0 -0
  71. {lintro-0.11.0 → lintro-0.12.0}/lintro/formatters/styles/plain.py +0 -0
  72. {lintro-0.11.0 → lintro-0.12.0}/lintro/formatters/tools/__init__.py +0 -0
  73. {lintro-0.11.0 → lintro-0.12.0}/lintro/formatters/tools/actionlint_formatter.py +0 -0
  74. {lintro-0.11.0 → lintro-0.12.0}/lintro/formatters/tools/bandit_formatter.py +0 -0
  75. {lintro-0.11.0 → lintro-0.12.0}/lintro/formatters/tools/black_formatter.py +0 -0
  76. {lintro-0.11.0 → lintro-0.12.0}/lintro/formatters/tools/darglint_formatter.py +0 -0
  77. {lintro-0.11.0 → lintro-0.12.0}/lintro/formatters/tools/hadolint_formatter.py +0 -0
  78. {lintro-0.11.0 → lintro-0.12.0}/lintro/formatters/tools/prettier_formatter.py +0 -0
  79. {lintro-0.11.0 → lintro-0.12.0}/lintro/formatters/tools/ruff_formatter.py +0 -0
  80. {lintro-0.11.0 → lintro-0.12.0}/lintro/formatters/tools/yamllint_formatter.py +0 -0
  81. {lintro-0.11.0 → lintro-0.12.0}/lintro/models/__init__.py +0 -0
  82. {lintro-0.11.0 → lintro-0.12.0}/lintro/models/core/__init__.py +0 -0
  83. {lintro-0.11.0 → lintro-0.12.0}/lintro/models/core/tool.py +0 -0
  84. {lintro-0.11.0 → lintro-0.12.0}/lintro/models/core/tool_config.py +0 -0
  85. {lintro-0.11.0 → lintro-0.12.0}/lintro/models/core/tool_result.py +0 -0
  86. {lintro-0.11.0 → lintro-0.12.0}/lintro/parsers/__init__.py +0 -0
  87. {lintro-0.11.0 → lintro-0.12.0}/lintro/parsers/actionlint/__init__.py +0 -0
  88. {lintro-0.11.0 → lintro-0.12.0}/lintro/parsers/actionlint/actionlint_issue.py +0 -0
  89. {lintro-0.11.0 → lintro-0.12.0}/lintro/parsers/actionlint/actionlint_parser.py +0 -0
  90. {lintro-0.11.0 → lintro-0.12.0}/lintro/parsers/black/black_issue.py +0 -0
  91. {lintro-0.11.0 → lintro-0.12.0}/lintro/parsers/black/black_parser.py +0 -0
  92. {lintro-0.11.0 → lintro-0.12.0}/lintro/parsers/darglint/__init__.py +0 -0
  93. {lintro-0.11.0 → lintro-0.12.0}/lintro/parsers/darglint/darglint_issue.py +0 -0
  94. {lintro-0.11.0 → lintro-0.12.0}/lintro/parsers/darglint/darglint_parser.py +0 -0
  95. {lintro-0.11.0 → lintro-0.12.0}/lintro/parsers/hadolint/__init__.py +0 -0
  96. {lintro-0.11.0 → lintro-0.12.0}/lintro/parsers/hadolint/hadolint_issue.py +0 -0
  97. {lintro-0.11.0 → lintro-0.12.0}/lintro/parsers/hadolint/hadolint_parser.py +0 -0
  98. {lintro-0.11.0 → lintro-0.12.0}/lintro/parsers/prettier/__init__.py +0 -0
  99. {lintro-0.11.0 → lintro-0.12.0}/lintro/parsers/prettier/prettier_issue.py +0 -0
  100. {lintro-0.11.0 → lintro-0.12.0}/lintro/parsers/ruff/__init__.py +0 -0
  101. {lintro-0.11.0 → lintro-0.12.0}/lintro/parsers/ruff/ruff_issue.py +0 -0
  102. {lintro-0.11.0 → lintro-0.12.0}/lintro/parsers/ruff/ruff_parser.py +0 -0
  103. {lintro-0.11.0 → lintro-0.12.0}/lintro/parsers/yamllint/__init__.py +0 -0
  104. {lintro-0.11.0 → lintro-0.12.0}/lintro/parsers/yamllint/yamllint_issue.py +0 -0
  105. {lintro-0.11.0 → lintro-0.12.0}/lintro/parsers/yamllint/yamllint_parser.py +0 -0
  106. {lintro-0.11.0 → lintro-0.12.0}/lintro/tools/core/__init__.py +0 -0
  107. {lintro-0.11.0 → lintro-0.12.0}/lintro/tools/core/tool_base.py +0 -0
  108. {lintro-0.11.0 → lintro-0.12.0}/lintro/tools/core/tool_manager.py +0 -0
  109. {lintro-0.11.0 → lintro-0.12.0}/lintro/tools/implementations/__init__.py +0 -0
  110. {lintro-0.11.0 → lintro-0.12.0}/lintro/tools/implementations/tool_actionlint.py +0 -0
  111. {lintro-0.11.0 → lintro-0.12.0}/lintro/tools/implementations/tool_bandit.py +0 -0
  112. {lintro-0.11.0 → lintro-0.12.0}/lintro/tools/implementations/tool_black.py +0 -0
  113. {lintro-0.11.0 → lintro-0.12.0}/lintro/tools/implementations/tool_darglint.py +0 -0
  114. {lintro-0.11.0 → lintro-0.12.0}/lintro/tools/implementations/tool_hadolint.py +0 -0
  115. {lintro-0.11.0 → lintro-0.12.0}/lintro/tools/implementations/tool_prettier.py +0 -0
  116. {lintro-0.11.0 → lintro-0.12.0}/lintro/tools/implementations/tool_ruff.py +0 -0
  117. {lintro-0.11.0 → lintro-0.12.0}/lintro/tools/implementations/tool_yamllint.py +0 -0
  118. {lintro-0.11.0 → lintro-0.12.0}/lintro/tools/tool_enum.py +0 -0
  119. {lintro-0.11.0 → lintro-0.12.0}/lintro/utils/__init__.py +0 -0
  120. {lintro-0.11.0 → lintro-0.12.0}/lintro/utils/ascii_normalize_cli.py +0 -0
  121. {lintro-0.11.0 → lintro-0.12.0}/lintro/utils/config.py +0 -0
  122. {lintro-0.11.0 → lintro-0.12.0}/lintro/utils/console_logger.py +0 -0
  123. {lintro-0.11.0 → lintro-0.12.0}/lintro/utils/formatting.py +0 -0
  124. {lintro-0.11.0 → lintro-0.12.0}/lintro/utils/output_manager.py +0 -0
  125. {lintro-0.11.0 → lintro-0.12.0}/lintro/utils/path_utils.py +0 -0
  126. {lintro-0.11.0 → lintro-0.12.0}/lintro.egg-info/dependency_links.txt +0 -0
  127. {lintro-0.11.0 → lintro-0.12.0}/lintro.egg-info/entry_points.txt +0 -0
  128. {lintro-0.11.0 → lintro-0.12.0}/lintro.egg-info/requires.txt +0 -0
  129. {lintro-0.11.0 → lintro-0.12.0}/lintro.egg-info/top_level.txt +0 -0
  130. {lintro-0.11.0 → lintro-0.12.0}/setup.cfg +0 -0
  131. {lintro-0.11.0 → lintro-0.12.0}/test_samples/Dockerfile.violations +0 -0
  132. {lintro-0.11.0 → lintro-0.12.0}/test_samples/actionlint_violations.yml +0 -0
  133. {lintro-0.11.0 → lintro-0.12.0}/test_samples/bandit_violations.py +0 -0
  134. {lintro-0.11.0 → lintro-0.12.0}/test_samples/darglint_violations.py +0 -0
  135. {lintro-0.11.0 → lintro-0.12.0}/test_samples/prettier_violations.js +0 -0
  136. {lintro-0.11.0 → lintro-0.12.0}/test_samples/ruff_annotations_violations.py +0 -0
  137. {lintro-0.11.0 → lintro-0.12.0}/test_samples/ruff_black_e501_wrappable.py +0 -0
  138. {lintro-0.11.0 → lintro-0.12.0}/test_samples/ruff_clean.py +0 -0
  139. {lintro-0.11.0 → lintro-0.12.0}/test_samples/ruff_naming_violations.py +0 -0
  140. {lintro-0.11.0 → lintro-0.12.0}/test_samples/ruff_violations.py +0 -0
  141. {lintro-0.11.0 → lintro-0.12.0}/test_samples/yaml_violations.yml +0 -0
  142. {lintro-0.11.0 → lintro-0.12.0}/tests/__init__.py +0 -0
  143. {lintro-0.11.0 → lintro-0.12.0}/tests/cli/__init__.py +0 -0
  144. {lintro-0.11.0 → lintro-0.12.0}/tests/cli/conftest.py +0 -0
  145. {lintro-0.11.0 → lintro-0.12.0}/tests/cli/test_cli.py +0 -0
  146. {lintro-0.11.0 → lintro-0.12.0}/tests/conftest.py +0 -0
  147. {lintro-0.11.0 → lintro-0.12.0}/tests/formatters/__init__.py +0 -0
  148. {lintro-0.11.0 → lintro-0.12.0}/tests/formatters/conftest.py +0 -0
  149. {lintro-0.11.0 → lintro-0.12.0}/tests/formatters/test_formatters.py +0 -0
  150. {lintro-0.11.0 → lintro-0.12.0}/tests/integration/__init__.py +0 -0
  151. {lintro-0.11.0 → lintro-0.12.0}/tests/integration/conftest.py +0 -0
  152. {lintro-0.11.0 → lintro-0.12.0}/tests/integration/test_actionlint_integration.py +0 -0
  153. {lintro-0.11.0 → lintro-0.12.0}/tests/integration/test_bandit_integration.py +0 -0
  154. {lintro-0.11.0 → lintro-0.12.0}/tests/integration/test_darglint_integration.py +0 -0
  155. {lintro-0.11.0 → lintro-0.12.0}/tests/integration/test_hadolint_integration.py +0 -0
  156. {lintro-0.11.0 → lintro-0.12.0}/tests/integration/test_prettier_integration.py +0 -0
  157. {lintro-0.11.0 → lintro-0.12.0}/tests/integration/test_ruff_annotations.py +0 -0
  158. {lintro-0.11.0 → lintro-0.12.0}/tests/integration/test_ruff_black_policy.py +0 -0
  159. {lintro-0.11.0 → lintro-0.12.0}/tests/integration/test_ruff_integration.py +0 -0
  160. {lintro-0.11.0 → lintro-0.12.0}/tests/integration/test_ruff_naming.py +0 -0
  161. {lintro-0.11.0 → lintro-0.12.0}/tests/integration/test_yamllint_integration.py +0 -0
  162. {lintro-0.11.0 → lintro-0.12.0}/tests/scripts/__init__.py +0 -0
  163. {lintro-0.11.0 → lintro-0.12.0}/tests/scripts/test_ci_post_pr_comment.py +0 -0
  164. {lintro-0.11.0 → lintro-0.12.0}/tests/scripts/test_delete_previous_lintro_comments.py +0 -0
  165. {lintro-0.11.0 → lintro-0.12.0}/tests/scripts/test_extract_version.py +0 -0
  166. {lintro-0.11.0 → lintro-0.12.0}/tests/scripts/test_ghcr_prune_untagged.py +0 -0
  167. {lintro-0.11.0 → lintro-0.12.0}/tests/scripts/test_github_comment_utilities.py +0 -0
  168. {lintro-0.11.0 → lintro-0.12.0}/tests/scripts/test_semantic_release_compute_next.py +0 -0
  169. {lintro-0.11.0 → lintro-0.12.0}/tests/scripts/test_shell_scripts.py +0 -0
  170. {lintro-0.11.0 → lintro-0.12.0}/tests/test_documentation.py +0 -0
  171. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/__init__.py +0 -0
  172. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_ascii_normalize.py +0 -0
  173. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_bandit_command_building.py +0 -0
  174. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_bandit_config_hydration.py +0 -0
  175. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_bandit_formatter_mapping.py +0 -0
  176. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_bandit_parsing.py +0 -0
  177. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_black_formatter.py +0 -0
  178. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_black_parser.py +0 -0
  179. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_black_tool.py +0 -0
  180. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_black_tool_more.py +0 -0
  181. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_cli_commands.py +0 -0
  182. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_cli_commands_more.py +0 -0
  183. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_compatibility_ruff_black.py +0 -0
  184. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_config_loader.py +0 -0
  185. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_config_loader_more.py +0 -0
  186. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_console_logger.py +0 -0
  187. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_console_logger_more.py +0 -0
  188. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_enums_and_normalizers.py +0 -0
  189. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_exceptions.py +0 -0
  190. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_formatters_tables.py +0 -0
  191. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_output_manager_reports.py +0 -0
  192. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_parsers_actionlint.py +0 -0
  193. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_ruff_parser_additional.py +0 -0
  194. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_ruff_parser_more.py +0 -0
  195. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_subprocess_validator.py +0 -0
  196. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_tool_base_subprocess.py +0 -0
  197. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_tool_executor.py +0 -0
  198. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_tool_executor_fmt_exclusion.py +0 -0
  199. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_tool_executor_post_checks.py +0 -0
  200. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_tool_manager.py +0 -0
  201. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_tool_utils.py +0 -0
  202. {lintro-0.11.0 → lintro-0.12.0}/tests/unit/test_tool_utils_more.py +0 -0
  203. {lintro-0.11.0 → lintro-0.12.0}/tests/utils/__init__.py +0 -0
  204. {lintro-0.11.0 → lintro-0.12.0}/tests/utils/conftest.py +0 -0
  205. {lintro-0.11.0 → lintro-0.12.0}/tests/utils/test_formatting.py +0 -0
  206. {lintro-0.11.0 → lintro-0.12.0}/tests/utils/test_output_manager.py +0 -0
  207. {lintro-0.11.0 → lintro-0.12.0}/tests/utils/test_path_utils.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.12.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
@@ -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.12.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
@@ -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
@@ -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
 
@@ -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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lintro
3
- Version: 0.11.0
3
+ Version: 0.12.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,6 +127,7 @@ 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
130
131
  test_samples/ruff_clean.py
131
132
  test_samples/ruff_naming_violations.py
132
133
  test_samples/ruff_violations.py
@@ -149,6 +150,7 @@ tests/integration/test_hadolint_integration.py
149
150
  tests/integration/test_prettier_integration.py
150
151
  tests/integration/test_ruff_annotations.py
151
152
  tests/integration/test_ruff_black_policy.py
153
+ tests/integration/test_ruff_bugbear.py
152
154
  tests/integration/test_ruff_integration.py
153
155
  tests/integration/test_ruff_naming.py
154
156
  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.12.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,12 +65,9 @@ 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",]
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",]
@@ -81,3 +78,6 @@ testpaths = [ "tests",]
81
78
  python_files = "test_*.py"
82
79
  python_classes = "Test*"
83
80
  python_functions = "test_*"
81
+
82
+ [tool.ruff.lint.pydocstyle]
83
+ 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()
@@ -0,0 +1,165 @@
1
+ """Integration tests for Ruff flake8-bugbear (B) rule support."""
2
+
3
+ import pytest
4
+
5
+ from lintro.tools.implementations.tool_ruff import RuffTool
6
+
7
+
8
+ class TestRuffBugbearIntegration:
9
+ """Test Ruff integration with flake8-bugbear rules."""
10
+
11
+ def test_ruff_bugbear_violations_detection(self):
12
+ """Test that Ruff detects flake8-bugbear violations."""
13
+ ruff_tool = RuffTool()
14
+ ruff_tool.set_options(select=["B"]) # Only enable flake8-bugbear rules
15
+
16
+ result = ruff_tool.check(["test_samples/ruff_bugbear_violations.py"])
17
+
18
+ # Should detect violations
19
+ assert not result.success
20
+ assert result.issues_count > 0
21
+
22
+ # Check that we have B-prefixed issues
23
+ b_issues = [
24
+ issue
25
+ for issue in result.issues
26
+ if hasattr(issue, "code") and issue.code.startswith("B")
27
+ ]
28
+ assert len(b_issues) > 0
29
+
30
+ # Verify specific bugbear rules are detected
31
+ issue_codes = {issue.code for issue in b_issues}
32
+ expected_codes = {
33
+ "B006",
34
+ "B007",
35
+ "B011",
36
+ "B001",
37
+ "B023",
38
+ "B008",
39
+ "B009",
40
+ "B010",
41
+ }
42
+
43
+ # At least some expected codes should be present
44
+ assert len(issue_codes.intersection(expected_codes)) > 0
45
+
46
+ def test_ruff_bugbear_with_other_rules(self):
47
+ """Test that flake8-bugbear works alongside other Ruff rules."""
48
+ ruff_tool = RuffTool()
49
+ ruff_tool.set_options(
50
+ select=["E", "F", "B"],
51
+ ) # Enable pycodestyle, pyflakes, and bugbear
52
+
53
+ result = ruff_tool.check(["test_samples/ruff_bugbear_violations.py"])
54
+
55
+ # Should detect violations from multiple rule sets
56
+ assert not result.success
57
+ assert result.issues_count > 0
58
+
59
+ # Check that we have issues from different rule categories
60
+ issue_codes = {issue.code for issue in result.issues if hasattr(issue, "code")}
61
+ has_bugbear = any(code.startswith("B") for code in issue_codes)
62
+ any(code.startswith("E") for code in issue_codes)
63
+ any(code.startswith("F") for code in issue_codes)
64
+
65
+ assert has_bugbear
66
+ # May or may not have pycodestyle/pyflakes issues depending on the test file
67
+
68
+ def test_ruff_bugbear_fix_capability(self):
69
+ """Test that Ruff can fix some flake8-bugbear violations."""
70
+ ruff_tool = RuffTool()
71
+ ruff_tool.set_options(select=["B"], unsafe_fixes=True)
72
+
73
+ # Check initial state
74
+ initial_result = ruff_tool.check(["test_samples/ruff_bugbear_violations.py"])
75
+ initial_count = initial_result.issues_count
76
+
77
+ # Apply fixes
78
+ fix_result = ruff_tool.fix(["test_samples/ruff_bugbear_violations.py"])
79
+
80
+ # Some issues should be fixable
81
+ assert fix_result.fixed_issues_count >= 0
82
+ assert fix_result.remaining_issues_count >= 0
83
+
84
+ # Total should match initial count
85
+ assert (
86
+ fix_result.fixed_issues_count + fix_result.remaining_issues_count
87
+ == initial_count
88
+ )
89
+
90
+ def test_ruff_bugbear_rule_selection(self):
91
+ """Test specific flake8-bugbear rule selection."""
92
+ ruff_tool = RuffTool()
93
+ ruff_tool.set_options(select=["B006", "B007"]) # Only specific bugbear rules
94
+
95
+ result = ruff_tool.check(["test_samples/ruff_bugbear_violations.py"])
96
+
97
+ # Should only detect the selected rules
98
+ issue_codes = {issue.code for issue in result.issues if hasattr(issue, "code")}
99
+ allowed_codes = {"B006", "B007"}
100
+
101
+ # All detected codes should be in the allowed set
102
+ assert issue_codes.issubset(allowed_codes)
103
+
104
+ def test_ruff_bugbear_rule_ignoring(self):
105
+ """Test ignoring specific flake8-bugbear rules."""
106
+ ruff_tool = RuffTool()
107
+ ruff_tool.set_options(select=["B"], ignore=["B006", "B007"])
108
+
109
+ result = ruff_tool.check(["test_samples/ruff_bugbear_violations.py"])
110
+
111
+ # Should not detect ignored rules
112
+ issue_codes = {issue.code for issue in result.issues if hasattr(issue, "code")}
113
+ ignored_codes = {"B006", "B007"}
114
+
115
+ # No ignored codes should be present
116
+ assert issue_codes.isdisjoint(ignored_codes)
117
+
118
+ def test_ruff_bugbear_extend_select(self):
119
+ """Test extending selection with flake8-bugbear rules."""
120
+ ruff_tool = RuffTool()
121
+ ruff_tool.set_options(select=["E"], extend_select=["B006", "B007"])
122
+
123
+ result = ruff_tool.check(["test_samples/ruff_bugbear_violations.py"])
124
+
125
+ # Should detect both E and B rules
126
+ issue_codes = {issue.code for issue in result.issues if hasattr(issue, "code")}
127
+ has_e_rules = any(code.startswith("E") for code in issue_codes)
128
+ has_b_rules = any(code.startswith("B") for code in issue_codes)
129
+
130
+ # Should have both rule types
131
+ assert has_e_rules or has_b_rules
132
+
133
+ def test_ruff_bugbear_extend_ignore(self):
134
+ """Test extending ignore list with flake8-bugbear rules."""
135
+ ruff_tool = RuffTool()
136
+ ruff_tool.set_options(select=["B"], extend_ignore=["B006", "B007"])
137
+
138
+ result = ruff_tool.check(["test_samples/ruff_bugbear_violations.py"])
139
+
140
+ # Should not detect extended ignore rules
141
+ issue_codes = {issue.code for issue in result.issues if hasattr(issue, "code")}
142
+ extended_ignore = {"B006", "B007"}
143
+
144
+ # No extended ignore codes should be present
145
+ assert issue_codes.isdisjoint(extended_ignore)
146
+
147
+ def test_ruff_bugbear_empty_file(self):
148
+ """Test that Ruff handles empty files with flake8-bugbear rules."""
149
+ ruff_tool = RuffTool()
150
+ ruff_tool.set_options(select=["B"])
151
+
152
+ result = ruff_tool.check(["test_samples/ruff_clean.py"])
153
+
154
+ # Should succeed with no issues
155
+ assert result.success
156
+ assert result.issues_count == 0
157
+
158
+ def test_ruff_bugbear_nonexistent_file(self):
159
+ """Test that Ruff handles nonexistent files with flake8-bugbear rules."""
160
+ ruff_tool = RuffTool()
161
+ ruff_tool.set_options(select=["B"])
162
+
163
+ # Should raise FileNotFoundError for nonexistent files
164
+ with pytest.raises(FileNotFoundError):
165
+ ruff_tool.check(["nonexistent_file.py"])
@@ -547,7 +547,7 @@ class TestScriptSecurity:
547
547
  with open(script) as f:
548
548
  content = f.read()
549
549
  lines = content.split("\n")
550
- for i, line in enumerate(lines, 1):
550
+ for _i, line in enumerate(lines, 1):
551
551
  if line.strip().startswith("#"):
552
552
  continue
553
553
  if " $1" in line and '"$1"' not in line and ("'$1'" not in line):
@@ -77,7 +77,7 @@ def test_format_programmatic_failure_raises(monkeypatch) -> None:
77
77
  lambda **k: 1,
78
78
  raising=True,
79
79
  )
80
- with pytest.raises(Exception):
80
+ with pytest.raises(RuntimeError):
81
81
  format_code_legacy(
82
82
  paths=["."],
83
83
  tools="prettier",
@@ -42,6 +42,9 @@ def test_get_tools_to_run_unknown_tool_raises(monkeypatch) -> None:
42
42
 
43
43
  Args:
44
44
  monkeypatch: Pytest fixture to modify objects during the test.
45
+
46
+ Raises:
47
+ AssertionError: If the expected ValueError is not raised.
45
48
  """
46
49
  import lintro.utils.tool_executor as te
47
50
 
@@ -57,7 +60,7 @@ def test_get_tools_to_run_unknown_tool_raises(monkeypatch) -> None:
57
60
 
58
61
  try:
59
62
  _ = te._get_tools_to_run(tools="notatool", action="check")
60
- assert False, "Expected ValueError for unknown tool"
63
+ raise AssertionError("Expected ValueError for unknown tool")
61
64
  except ValueError as e: # noqa: PT017
62
65
  assert_that(str(e)).contains("Unknown tool")
63
66
 
@@ -67,6 +70,9 @@ def test_get_tools_to_run_fmt_with_cannot_fix_raises(monkeypatch) -> None:
67
70
 
68
71
  Args:
69
72
  monkeypatch: Pytest fixture to modify objects during the test.
73
+
74
+ Raises:
75
+ AssertionError: If the expected ValueError is not raised.
70
76
  """
71
77
  import lintro.utils.tool_executor as te
72
78
 
@@ -89,7 +95,7 @@ def test_get_tools_to_run_fmt_with_cannot_fix_raises(monkeypatch) -> None:
89
95
  # Directly call the helper
90
96
  try:
91
97
  _ = te._get_tools_to_run(tools="bandit", action="fmt")
92
- assert False, "Expected ValueError for non-fix tool in fmt"
98
+ raise AssertionError("Expected ValueError for non-fix tool in fmt")
93
99
  except ValueError as e: # noqa: PT017
94
100
  assert_that(str(e)).contains("does not support formatting")
95
101
 
@@ -24,9 +24,13 @@ def test_format_as_table_fallback_when_no_tabulate() -> None:
24
24
 
25
25
 
26
26
  def test_parse_tool_list_error_on_bad_name() -> None:
27
- """Raise ValueError when parsing an unknown tool name."""
27
+ """Raise ValueError when parsing an unknown tool name.
28
+
29
+ Raises:
30
+ AssertionError: If the expected ValueError is not raised.
31
+ """
28
32
  try:
29
33
  _ = tu.parse_tool_list("notatool")
30
- assert False, "Expected ValueError for bad tool name"
34
+ raise AssertionError("Expected ValueError for bad tool name")
31
35
  except ValueError as e: # noqa: PT017
32
36
  assert_that("Unknown core" in str(e) or "Unknown tool" in str(e)).is_true()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes