deadcode 2.2.2__py3-none-any.whl → 2.3.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.
Files changed (307) hide show
  1. black/action/main.py +176 -0
  2. black/docs/conf.py +218 -0
  3. black/gallery/gallery.py +294 -0
  4. black/profiling/dict_big.py +8001 -0
  5. black/profiling/dict_huge.py +41440 -0
  6. black/profiling/list_big.py +4000 -0
  7. black/profiling/list_huge.py +22431 -0
  8. black/profiling/mix_big.py +1002 -0
  9. black/profiling/mix_huge.py +7692 -0
  10. black/profiling/mix_small.py +102 -0
  11. black/scripts/__init__.py +0 -0
  12. black/scripts/check_pre_commit_rev_in_example.py +54 -0
  13. black/scripts/check_version_in_basics_example.py +48 -0
  14. black/scripts/diff_shades_gha_helper.py +236 -0
  15. black/scripts/fuzz.py +92 -0
  16. black/scripts/generate_schema.py +74 -0
  17. black/scripts/make_width_table.py +66 -0
  18. black/scripts/migrate-black.py +96 -0
  19. black/scripts/release.py +245 -0
  20. black/scripts/release_tests.py +69 -0
  21. black/src/black/__init__.py +1605 -0
  22. black/src/black/__main__.py +3 -0
  23. black/src/black/_width_table.py +478 -0
  24. black/src/black/brackets.py +382 -0
  25. black/src/black/cache.py +150 -0
  26. black/src/black/comments.py +415 -0
  27. black/src/black/concurrency.py +190 -0
  28. black/src/black/const.py +4 -0
  29. black/src/black/debug.py +54 -0
  30. black/src/black/files.py +433 -0
  31. black/src/black/handle_ipynb_magics.py +452 -0
  32. black/src/black/linegen.py +1745 -0
  33. black/src/black/lines.py +1073 -0
  34. black/src/black/mode.py +265 -0
  35. black/src/black/nodes.py +1007 -0
  36. black/src/black/numerics.py +61 -0
  37. black/src/black/output.py +122 -0
  38. black/src/black/parsing.py +244 -0
  39. black/src/black/ranges.py +521 -0
  40. black/src/black/report.py +107 -0
  41. black/src/black/resources/__init__.py +0 -0
  42. black/src/black/rusty.py +28 -0
  43. black/src/black/schema.py +20 -0
  44. black/src/black/strings.py +325 -0
  45. black/src/black/trans.py +2534 -0
  46. black/src/blackd/__init__.py +259 -0
  47. black/src/blackd/__main__.py +3 -0
  48. black/src/blackd/middlewares.py +45 -0
  49. black/src/blib2to3/__init__.py +1 -0
  50. black/src/blib2to3/pgen2/__init__.py +4 -0
  51. black/src/blib2to3/pgen2/conv.py +256 -0
  52. black/src/blib2to3/pgen2/driver.py +316 -0
  53. black/src/blib2to3/pgen2/grammar.py +227 -0
  54. black/src/blib2to3/pgen2/literals.py +66 -0
  55. black/src/blib2to3/pgen2/parse.py +411 -0
  56. black/src/blib2to3/pgen2/pgen.py +428 -0
  57. black/src/blib2to3/pgen2/token.py +88 -0
  58. black/src/blib2to3/pgen2/tokenize.py +698 -0
  59. black/src/blib2to3/pygram.py +200 -0
  60. black/src/blib2to3/pytree.py +986 -0
  61. black/tests/__init__.py +0 -0
  62. black/tests/conftest.py +28 -0
  63. black/tests/data/cases/allow_empty_first_line.py +166 -0
  64. black/tests/data/cases/async_stmts.py +27 -0
  65. black/tests/data/cases/attribute_access_on_number_literals.py +47 -0
  66. black/tests/data/cases/beginning_backslash.py +12 -0
  67. black/tests/data/cases/bracketmatch.py +15 -0
  68. black/tests/data/cases/bytes_docstring.py +34 -0
  69. black/tests/data/cases/class_blank_parentheses.py +59 -0
  70. black/tests/data/cases/class_methods_new_line.py +270 -0
  71. black/tests/data/cases/collections.py +174 -0
  72. black/tests/data/cases/comment_after_escaped_newline.py +18 -0
  73. black/tests/data/cases/comment_type_hint.py +3 -0
  74. black/tests/data/cases/comments.py +96 -0
  75. black/tests/data/cases/comments2.py +344 -0
  76. black/tests/data/cases/comments3.py +48 -0
  77. black/tests/data/cases/comments4.py +94 -0
  78. black/tests/data/cases/comments5.py +70 -0
  79. black/tests/data/cases/comments6.py +118 -0
  80. black/tests/data/cases/comments8.py +15 -0
  81. black/tests/data/cases/comments9.py +305 -0
  82. black/tests/data/cases/comments_in_blocks.py +111 -0
  83. black/tests/data/cases/comments_in_double_parens.py +113 -0
  84. black/tests/data/cases/comments_non_breaking_space.py +44 -0
  85. black/tests/data/cases/composition.py +181 -0
  86. black/tests/data/cases/composition_no_trailing_comma.py +367 -0
  87. black/tests/data/cases/conditional_expression.py +204 -0
  88. black/tests/data/cases/context_managers_38.py +55 -0
  89. black/tests/data/cases/context_managers_39.py +175 -0
  90. black/tests/data/cases/context_managers_autodetect_310.py +36 -0
  91. black/tests/data/cases/context_managers_autodetect_311.py +38 -0
  92. black/tests/data/cases/context_managers_autodetect_38.py +46 -0
  93. black/tests/data/cases/context_managers_autodetect_39.py +35 -0
  94. black/tests/data/cases/docstring.py +457 -0
  95. black/tests/data/cases/docstring_newline_preview.py +4 -0
  96. black/tests/data/cases/docstring_no_extra_empty_line_before_eof.py +4 -0
  97. black/tests/data/cases/docstring_no_string_normalization.py +250 -0
  98. black/tests/data/cases/docstring_preview.py +104 -0
  99. black/tests/data/cases/dummy_implementations.py +144 -0
  100. black/tests/data/cases/empty_lines.py +187 -0
  101. black/tests/data/cases/expression.py +630 -0
  102. black/tests/data/cases/f_docstring.py +20 -0
  103. black/tests/data/cases/fmtonoff.py +409 -0
  104. black/tests/data/cases/fmtonoff2.py +40 -0
  105. black/tests/data/cases/fmtonoff3.py +35 -0
  106. black/tests/data/cases/fmtonoff4.py +36 -0
  107. black/tests/data/cases/fmtonoff5.py +175 -0
  108. black/tests/data/cases/fmtpass_imports.py +19 -0
  109. black/tests/data/cases/fmtskip.py +3 -0
  110. black/tests/data/cases/fmtskip2.py +20 -0
  111. black/tests/data/cases/fmtskip3.py +20 -0
  112. black/tests/data/cases/fmtskip4.py +13 -0
  113. black/tests/data/cases/fmtskip5.py +22 -0
  114. black/tests/data/cases/fmtskip6.py +13 -0
  115. black/tests/data/cases/fmtskip7.py +11 -0
  116. black/tests/data/cases/fmtskip8.py +62 -0
  117. black/tests/data/cases/fmtskip9.py +9 -0
  118. black/tests/data/cases/form_feeds.py +226 -0
  119. black/tests/data/cases/fstring.py +25 -0
  120. black/tests/data/cases/funcdef_return_type_trailing_comma.py +301 -0
  121. black/tests/data/cases/function.py +244 -0
  122. black/tests/data/cases/function2.py +121 -0
  123. black/tests/data/cases/function_trailing_comma.py +178 -0
  124. black/tests/data/cases/ignore_pyi.py +42 -0
  125. black/tests/data/cases/import_spacing.py +118 -0
  126. black/tests/data/cases/is_simple_lookup_for_doublestar_expression.py +14 -0
  127. black/tests/data/cases/keep_newline_after_match.py +41 -0
  128. black/tests/data/cases/line_ranges_basic.py +107 -0
  129. black/tests/data/cases/line_ranges_diff_edge_case.py +28 -0
  130. black/tests/data/cases/line_ranges_exceeding_end.py +36 -0
  131. black/tests/data/cases/line_ranges_fmt_off.py +49 -0
  132. black/tests/data/cases/line_ranges_fmt_off_decorator.py +47 -0
  133. black/tests/data/cases/line_ranges_fmt_off_overlap.py +37 -0
  134. black/tests/data/cases/line_ranges_imports.py +9 -0
  135. black/tests/data/cases/line_ranges_indentation.py +27 -0
  136. black/tests/data/cases/line_ranges_outside_source.py +7 -0
  137. black/tests/data/cases/line_ranges_two_passes.py +27 -0
  138. black/tests/data/cases/line_ranges_unwrapping.py +25 -0
  139. black/tests/data/cases/linelength6.py +6 -0
  140. black/tests/data/cases/long_strings_flag_disabled.py +296 -0
  141. black/tests/data/cases/module_docstring_1.py +25 -0
  142. black/tests/data/cases/module_docstring_2.py +70 -0
  143. black/tests/data/cases/module_docstring_3.py +7 -0
  144. black/tests/data/cases/module_docstring_4.py +8 -0
  145. black/tests/data/cases/module_docstring_followed_by_class.py +10 -0
  146. black/tests/data/cases/module_docstring_followed_by_function.py +10 -0
  147. black/tests/data/cases/multiline_consecutive_open_parentheses_ignore.py +41 -0
  148. black/tests/data/cases/nested_stub.py +69 -0
  149. black/tests/data/cases/no_blank_line_before_docstring.py +66 -0
  150. black/tests/data/cases/numeric_literals.py +39 -0
  151. black/tests/data/cases/numeric_literals_skip_underscores.py +19 -0
  152. black/tests/data/cases/one_element_subscript.py +36 -0
  153. black/tests/data/cases/parenthesized_context_managers.py +46 -0
  154. black/tests/data/cases/pattern_matching_complex.py +155 -0
  155. black/tests/data/cases/pattern_matching_extras.py +102 -0
  156. black/tests/data/cases/pattern_matching_generic.py +108 -0
  157. black/tests/data/cases/pattern_matching_long.py +34 -0
  158. black/tests/data/cases/pattern_matching_simple.py +93 -0
  159. black/tests/data/cases/pattern_matching_style.py +92 -0
  160. black/tests/data/cases/pattern_matching_trailing_comma.py +39 -0
  161. black/tests/data/cases/pattern_matching_with_if_stmt.py +72 -0
  162. black/tests/data/cases/pep604_union_types_line_breaks.py +188 -0
  163. black/tests/data/cases/pep_570.py +45 -0
  164. black/tests/data/cases/pep_572.py +48 -0
  165. black/tests/data/cases/pep_572_do_not_remove_parens.py +26 -0
  166. black/tests/data/cases/pep_572_py310.py +16 -0
  167. black/tests/data/cases/pep_572_py39.py +8 -0
  168. black/tests/data/cases/pep_572_remove_parens.py +142 -0
  169. black/tests/data/cases/pep_572_slices.py +6 -0
  170. black/tests/data/cases/pep_604.py +25 -0
  171. black/tests/data/cases/pep_646.py +195 -0
  172. black/tests/data/cases/pep_654.py +54 -0
  173. black/tests/data/cases/pep_654_style.py +112 -0
  174. black/tests/data/cases/percent_precedence.py +41 -0
  175. black/tests/data/cases/power_op_newline.py +11 -0
  176. black/tests/data/cases/power_op_spacing.py +149 -0
  177. black/tests/data/cases/power_op_spacing_long.py +96 -0
  178. black/tests/data/cases/prefer_rhs_split.py +106 -0
  179. black/tests/data/cases/prefer_rhs_split_reformatted.py +38 -0
  180. black/tests/data/cases/preview_cantfit.py +94 -0
  181. black/tests/data/cases/preview_cantfit_string.py +18 -0
  182. black/tests/data/cases/preview_comments7.py +309 -0
  183. black/tests/data/cases/preview_format_unicode_escape_seq.py +34 -0
  184. black/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets.py +347 -0
  185. black/tests/data/cases/preview_hug_parens_with_braces_and_square_brackets_no_ll1.py +106 -0
  186. black/tests/data/cases/preview_long_dict_values.py +129 -0
  187. black/tests/data/cases/preview_long_strings.py +928 -0
  188. black/tests/data/cases/preview_long_strings__east_asian_width.py +26 -0
  189. black/tests/data/cases/preview_long_strings__edge_case.py +141 -0
  190. black/tests/data/cases/preview_long_strings__regression.py +1247 -0
  191. black/tests/data/cases/preview_long_strings__type_annotations.py +60 -0
  192. black/tests/data/cases/preview_multiline_strings.py +401 -0
  193. black/tests/data/cases/preview_return_annotation_brackets_string.py +24 -0
  194. black/tests/data/cases/py310_pep572.py +13 -0
  195. black/tests/data/cases/python37.py +62 -0
  196. black/tests/data/cases/python38.py +42 -0
  197. black/tests/data/cases/python39.py +30 -0
  198. black/tests/data/cases/raw_docstring.py +32 -0
  199. black/tests/data/cases/raw_docstring_no_string_normalization.py +11 -0
  200. black/tests/data/cases/remove_await_parens.py +195 -0
  201. black/tests/data/cases/remove_except_parens.py +79 -0
  202. black/tests/data/cases/remove_for_brackets.py +48 -0
  203. black/tests/data/cases/remove_newline_after_code_block_open.py +212 -0
  204. black/tests/data/cases/remove_parens.py +142 -0
  205. black/tests/data/cases/remove_redundant_parens_in_case_guard.py +114 -0
  206. black/tests/data/cases/remove_with_brackets.py +120 -0
  207. black/tests/data/cases/return_annotation_brackets.py +219 -0
  208. black/tests/data/cases/single_line_format_skip_with_multiple_comments.py +19 -0
  209. black/tests/data/cases/skip_magic_trailing_comma.py +75 -0
  210. black/tests/data/cases/slices.py +31 -0
  211. black/tests/data/cases/split_delimiter_comments.py +51 -0
  212. black/tests/data/cases/starred_for_target.py +28 -0
  213. black/tests/data/cases/string_prefixes.py +45 -0
  214. black/tests/data/cases/stub.py +152 -0
  215. black/tests/data/cases/torture.py +91 -0
  216. black/tests/data/cases/trailing_comma.py +55 -0
  217. black/tests/data/cases/trailing_comma_optional_parens1.py +63 -0
  218. black/tests/data/cases/trailing_comma_optional_parens2.py +12 -0
  219. black/tests/data/cases/trailing_comma_optional_parens3.py +21 -0
  220. black/tests/data/cases/trailing_commas_in_leading_parts.py +88 -0
  221. black/tests/data/cases/tricky_unicode_symbols.py +9 -0
  222. black/tests/data/cases/tupleassign.py +23 -0
  223. black/tests/data/cases/type_aliases.py +30 -0
  224. black/tests/data/cases/type_comment_syntax_error.py +11 -0
  225. black/tests/data/cases/type_params.py +58 -0
  226. black/tests/data/cases/typed_params_trailing_comma.py +24 -0
  227. black/tests/data/cases/walrus_in_dict.py +9 -0
  228. black/tests/data/cases/whitespace.py +6 -0
  229. black/tests/data/gitignore_used_on_multiple_sources/dir1/a.py +0 -0
  230. black/tests/data/gitignore_used_on_multiple_sources/dir1/b.py +0 -0
  231. black/tests/data/gitignore_used_on_multiple_sources/dir2/a.py +0 -0
  232. black/tests/data/gitignore_used_on_multiple_sources/dir2/b.py +0 -0
  233. black/tests/data/ignore_subfolders_gitignore_tests/a.py +0 -0
  234. black/tests/data/ignore_subfolders_gitignore_tests/subdir/b.py +0 -0
  235. black/tests/data/ignore_subfolders_gitignore_tests/subdir/subdir/c.py +0 -0
  236. black/tests/data/include_exclude_tests/b/.definitely_exclude/a.py +0 -0
  237. black/tests/data/include_exclude_tests/b/dont_exclude/a.py +0 -0
  238. black/tests/data/include_exclude_tests/b/exclude/a.py +0 -0
  239. black/tests/data/invalid_gitignore_tests/a.py +0 -0
  240. black/tests/data/invalid_nested_gitignore_tests/a/a.py +0 -0
  241. black/tests/data/invalid_nested_gitignore_tests/a.py +0 -0
  242. black/tests/data/line_ranges_formatted/basic.py +50 -0
  243. black/tests/data/line_ranges_formatted/pattern_matching.py +25 -0
  244. black/tests/data/miscellaneous/async_as_identifier.py +49 -0
  245. black/tests/data/miscellaneous/blackd_diff.py +6 -0
  246. black/tests/data/miscellaneous/debug_visitor.py +32 -0
  247. black/tests/data/miscellaneous/decorators.py +182 -0
  248. black/tests/data/miscellaneous/force_py36.py +16 -0
  249. black/tests/data/miscellaneous/force_pyi.py +66 -0
  250. black/tests/data/miscellaneous/invalid_header.py +2 -0
  251. black/tests/data/miscellaneous/missing_final_newline.py +3 -0
  252. black/tests/data/miscellaneous/pattern_matching_invalid.py +18 -0
  253. black/tests/data/miscellaneous/python2_detection.py +90 -0
  254. black/tests/data/miscellaneous/string_quotes.py +114 -0
  255. black/tests/data/nested_gitignore_tests/root/a.py +1 -0
  256. black/tests/data/nested_gitignore_tests/root/b.py +1 -0
  257. black/tests/data/nested_gitignore_tests/root/c.py +1 -0
  258. black/tests/data/nested_gitignore_tests/root/child/a.py +1 -0
  259. black/tests/data/nested_gitignore_tests/root/child/b.py +1 -0
  260. black/tests/data/nested_gitignore_tests/root/child/c.py +1 -0
  261. black/tests/data/nested_gitignore_tests/x.py +0 -0
  262. black/tests/optional.py +132 -0
  263. black/tests/test_black.py +2980 -0
  264. black/tests/test_blackd.py +248 -0
  265. black/tests/test_docs.py +74 -0
  266. black/tests/test_format.py +90 -0
  267. black/tests/test_ipynb.py +517 -0
  268. black/tests/test_no_ipynb.py +36 -0
  269. black/tests/test_ranges.py +249 -0
  270. black/tests/test_schema.py +17 -0
  271. black/tests/test_trans.py +51 -0
  272. black/tests/util.py +354 -0
  273. call_arg_map_to_parameters_poc.py +3 -3
  274. deadcode/actions/{fix_unused_code.py → fix_or_show_unused_code.py} +25 -4
  275. deadcode/actions/parse_arguments.py +24 -6
  276. deadcode/cli.py +5 -4
  277. deadcode/constants.py +24 -24
  278. deadcode/data_types.py +1 -0
  279. deadcode/tests/base.py +2 -2
  280. deadcode/tests/cli_args/test_dry.py +318 -0
  281. deadcode/tests/cli_args/test_fix.py +5 -5
  282. deadcode/tests/cli_args/test_ignore_bodies_if_inherits_from.py +3 -3
  283. deadcode/tests/cli_args/test_ignore_definitions.py +1 -1
  284. deadcode/tests/cli_args/test_ignore_definitions_if_inherits_from.py +4 -4
  285. deadcode/tests/cli_args/test_ignore_names.py +3 -3
  286. deadcode/tests/cli_args/test_parsing.py +59 -12
  287. deadcode/tests/fix/test_assign.py +2 -2
  288. deadcode/tests/fix/test_class_def.py +4 -4
  289. deadcode/tests/fix/test_empty_files.py +2 -2
  290. deadcode/tests/nested_scope/test_nested_scope.py +96 -1
  291. deadcode/tests/noqa/test_noqa_comments.py +28 -28
  292. deadcode/tests/test_deadcode.py +11 -11
  293. deadcode/tests/test_imports.py +3 -3
  294. deadcode/tests/test_output.py +6 -6
  295. deadcode/tests/utils/test_add_colors_to_diff.py +38 -0
  296. deadcode/utils/add_colors_to_diff.py +10 -0
  297. deadcode/utils/nested_scopes.py +51 -15
  298. deadcode/visitor/code_item.py +3 -0
  299. deadcode/visitor/dead_code_visitor.py +37 -135
  300. deadcode/visitor/ignore.py +12 -12
  301. deadcode/visitor/noqa.py +27 -14
  302. {deadcode-2.2.2.dist-info → deadcode-2.3.0.dist-info}/METADATA +67 -25
  303. deadcode-2.3.0.dist-info/RECORD +342 -0
  304. deadcode-2.2.2.dist-info/RECORD +0 -67
  305. {deadcode-2.2.2.dist-info → deadcode-2.3.0.dist-info}/WHEEL +0 -0
  306. {deadcode-2.2.2.dist-info → deadcode-2.3.0.dist-info}/entry_points.txt +0 -0
  307. {deadcode-2.2.2.dist-info → deadcode-2.3.0.dist-info}/licenses/LICENSE +0 -0
black/action/main.py ADDED
@@ -0,0 +1,176 @@
1
+ import os
2
+ import re
3
+ import shlex
4
+ import shutil
5
+ import sys
6
+ from pathlib import Path
7
+ from subprocess import PIPE, STDOUT, run
8
+ from typing import Union
9
+
10
+ ACTION_PATH = Path(os.environ["GITHUB_ACTION_PATH"])
11
+ ENV_PATH = ACTION_PATH / ".black-env"
12
+ ENV_BIN = ENV_PATH / ("Scripts" if sys.platform == "win32" else "bin")
13
+ OPTIONS = os.getenv("INPUT_OPTIONS", default="")
14
+ SRC = os.getenv("INPUT_SRC", default="")
15
+ JUPYTER = os.getenv("INPUT_JUPYTER") == "true"
16
+ BLACK_ARGS = os.getenv("INPUT_BLACK_ARGS", default="")
17
+ VERSION = os.getenv("INPUT_VERSION", default="")
18
+ USE_PYPROJECT = os.getenv("INPUT_USE_PYPROJECT") == "true"
19
+
20
+ BLACK_VERSION_RE = re.compile(r"^black([^A-Z0-9._-]+.*)$", re.IGNORECASE)
21
+ EXTRAS_RE = re.compile(r"\[.*\]")
22
+
23
+
24
+ def determine_version_specifier() -> str:
25
+ """Determine the version of Black to install.
26
+
27
+ The version can be specified either via the `with.version` input or via the
28
+ pyproject.toml file if `with.use_pyproject` is set to `true`.
29
+ """
30
+ if USE_PYPROJECT and VERSION:
31
+ print(
32
+ "::error::'with.version' and 'with.use_pyproject' inputs are "
33
+ "mutually exclusive.",
34
+ file=sys.stderr,
35
+ flush=True,
36
+ )
37
+ sys.exit(1)
38
+ if USE_PYPROJECT:
39
+ return read_version_specifier_from_pyproject()
40
+ elif VERSION and VERSION[0] in "0123456789":
41
+ return f"=={VERSION}"
42
+ else:
43
+ return VERSION
44
+
45
+
46
+ def read_version_specifier_from_pyproject() -> str:
47
+ if sys.version_info < (3, 11):
48
+ print(
49
+ "::error::'with.use_pyproject' input requires Python 3.11 or later.",
50
+ file=sys.stderr,
51
+ flush=True,
52
+ )
53
+ sys.exit(1)
54
+
55
+ import tomllib # type: ignore[import-not-found,unreachable]
56
+
57
+ try:
58
+ with Path("pyproject.toml").open("rb") as fp:
59
+ pyproject = tomllib.load(fp)
60
+ except FileNotFoundError:
61
+ print(
62
+ "::error::'with.use_pyproject' input requires a pyproject.toml file.",
63
+ file=sys.stderr,
64
+ flush=True,
65
+ )
66
+ sys.exit(1)
67
+
68
+ version = pyproject.get("tool", {}).get("black", {}).get("required-version")
69
+ if version is not None:
70
+ return f"=={version}"
71
+
72
+ arrays = [
73
+ pyproject.get("project", {}).get("dependencies"),
74
+ *pyproject.get("project", {}).get("optional-dependencies", {}).values(),
75
+ ]
76
+ for array in arrays:
77
+ version = find_black_version_in_array(array)
78
+ if version is not None:
79
+ break
80
+
81
+ if version is None:
82
+ print(
83
+ "::error::'black' dependency missing from pyproject.toml.",
84
+ file=sys.stderr,
85
+ flush=True,
86
+ )
87
+ sys.exit(1)
88
+
89
+ return version
90
+
91
+
92
+ def find_black_version_in_array(array: object) -> Union[str, None]:
93
+ if not isinstance(array, list):
94
+ return None
95
+ try:
96
+ for item in array:
97
+ # Rudimentary PEP 508 parsing.
98
+ item = item.split(";")[0]
99
+ item = EXTRAS_RE.sub("", item).strip()
100
+ if item == "black":
101
+ print(
102
+ "::error::Version specifier missing for 'black' dependency in "
103
+ "pyproject.toml.",
104
+ file=sys.stderr,
105
+ flush=True,
106
+ )
107
+ sys.exit(1)
108
+ elif m := BLACK_VERSION_RE.match(item):
109
+ return m.group(1).strip()
110
+ except TypeError:
111
+ pass
112
+
113
+ return None
114
+
115
+
116
+ run([sys.executable, "-m", "venv", str(ENV_PATH)], check=True)
117
+
118
+ version_specifier = determine_version_specifier()
119
+ if JUPYTER:
120
+ extra_deps = "[colorama,jupyter]"
121
+ else:
122
+ extra_deps = "[colorama]"
123
+ if version_specifier:
124
+ req = f"black{extra_deps}{version_specifier}"
125
+ else:
126
+ describe_name = ""
127
+ with open(ACTION_PATH / ".git_archival.txt", encoding="utf-8") as fp:
128
+ for line in fp:
129
+ if line.startswith("describe-name: "):
130
+ describe_name = line[len("describe-name: ") :].rstrip()
131
+ break
132
+ if not describe_name:
133
+ print("::error::Failed to detect action version.", file=sys.stderr, flush=True)
134
+ sys.exit(1)
135
+ # expected format is one of:
136
+ # - 23.1.0
137
+ # - 23.1.0-51-g448bba7
138
+ if describe_name.count("-") < 2:
139
+ # the action's commit matches a tag exactly, install exact version from PyPI
140
+ req = f"black{extra_deps}=={describe_name}"
141
+ else:
142
+ # the action's commit does not match any tag, install from the local git repo
143
+ req = f".{extra_deps}"
144
+ print(f"Installing {req}...", flush=True)
145
+ pip_proc = run(
146
+ [str(ENV_BIN / "python"), "-m", "pip", "install", req],
147
+ stdout=PIPE,
148
+ stderr=STDOUT,
149
+ encoding="utf-8",
150
+ cwd=ACTION_PATH,
151
+ )
152
+ if pip_proc.returncode:
153
+ print(pip_proc.stdout)
154
+ print("::error::Failed to install Black.", file=sys.stderr, flush=True)
155
+ sys.exit(pip_proc.returncode)
156
+
157
+
158
+ base_cmd = [str(ENV_BIN / "black")]
159
+ if BLACK_ARGS:
160
+ # TODO: remove after a while since this is deprecated in favour of SRC + OPTIONS.
161
+ proc = run(
162
+ [*base_cmd, *shlex.split(BLACK_ARGS)],
163
+ stdout=PIPE,
164
+ stderr=STDOUT,
165
+ encoding="utf-8",
166
+ )
167
+ else:
168
+ proc = run(
169
+ [*base_cmd, *shlex.split(OPTIONS), *shlex.split(SRC)],
170
+ stdout=PIPE,
171
+ stderr=STDOUT,
172
+ encoding="utf-8",
173
+ )
174
+ shutil.rmtree(ENV_PATH, ignore_errors=True)
175
+ print(proc.stdout)
176
+ sys.exit(proc.returncode)
black/docs/conf.py ADDED
@@ -0,0 +1,218 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Configuration file for the Sphinx documentation builder.
4
+ #
5
+ # This file does only contain a selection of the most common options. For a
6
+ # full list see the documentation:
7
+ # http://www.sphinx-doc.org/en/stable/config
8
+
9
+ # -- Path setup --------------------------------------------------------------
10
+
11
+ # If extensions (or modules to document with autodoc) are in another directory,
12
+ # add these directories to sys.path here. If the directory is relative to the
13
+ # documentation root, use os.path.abspath to make it absolute, like shown here.
14
+ #
15
+
16
+ import os
17
+ import string
18
+ from importlib.metadata import version
19
+ from pathlib import Path
20
+
21
+ CURRENT_DIR = Path(__file__).parent
22
+
23
+
24
+ def make_pypi_svg(version: str) -> None:
25
+ template: Path = CURRENT_DIR / "_static" / "pypi_template.svg"
26
+ target: Path = CURRENT_DIR / "_static" / "pypi.svg"
27
+ with open(str(template), "r", encoding="utf8") as f:
28
+ svg: str = string.Template(f.read()).substitute(version=version)
29
+ with open(str(target), "w", encoding="utf8") as f:
30
+ f.write(svg)
31
+
32
+
33
+ # Necessary so Click doesn't hit an encode error when called by
34
+ # sphinxcontrib-programoutput on Windows.
35
+ os.putenv("pythonioencoding", "utf-8")
36
+
37
+ # -- Project information -----------------------------------------------------
38
+
39
+ project = "Black"
40
+ copyright = "2018-Present, Łukasz Langa and contributors to Black"
41
+ author = "Łukasz Langa and contributors to Black"
42
+
43
+ # Autopopulate version
44
+ # The version, including alpha/beta/rc tags, but not commit hash and datestamps
45
+ release = version("black").split("+")[0]
46
+ # The short X.Y version.
47
+ version = release
48
+ for sp in "abcfr":
49
+ version = version.split(sp)[0]
50
+
51
+ make_pypi_svg(release)
52
+
53
+
54
+ # -- General configuration ---------------------------------------------------
55
+
56
+ # If your documentation needs a minimal Sphinx version, state it here.
57
+ needs_sphinx = "4.4"
58
+
59
+ # Add any Sphinx extension module names here, as strings. They can be
60
+ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
61
+ # ones.
62
+ extensions = [
63
+ "sphinx.ext.autodoc",
64
+ "sphinx.ext.intersphinx",
65
+ "sphinx.ext.napoleon",
66
+ "myst_parser",
67
+ "sphinxcontrib.programoutput",
68
+ "sphinx_copybutton",
69
+ ]
70
+
71
+ # If you need extensions of a certain version or higher, list them here.
72
+ needs_extensions = {"myst_parser": "0.13.7"}
73
+
74
+ # Add any paths that contain templates here, relative to this directory.
75
+ templates_path = ["_templates"]
76
+
77
+ # The suffix(es) of source filenames.
78
+ # You can specify multiple suffix as a list of string:
79
+ source_suffix = [".rst", ".md"]
80
+
81
+ # The master toctree document.
82
+ master_doc = "index"
83
+
84
+ # The language for content autogenerated by Sphinx. Refer to documentation
85
+ # for a list of supported languages.
86
+ #
87
+ # This is also used if you do content translation via gettext catalogs.
88
+ # Usually you set "language" from the command line for these cases.
89
+ language = "en"
90
+
91
+ # List of patterns, relative to source directory, that match files and
92
+ # directories to ignore when looking for source files.
93
+ # This pattern also affects html_static_path and html_extra_path .
94
+
95
+ exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
96
+
97
+ # The name of the Pygments (syntax highlighting) style to use.
98
+ pygments_style = "sphinx"
99
+
100
+ # We need headers to be linkable to so ask MyST-Parser to autogenerate anchor IDs for
101
+ # headers up to and including level 3.
102
+ myst_heading_anchors = 3
103
+
104
+ # Prettier support formatting some MyST syntax but not all, so let's disable the
105
+ # unsupported yet still enabled by default ones.
106
+ myst_disable_syntax = [
107
+ "colon_fence",
108
+ "myst_block_break",
109
+ "myst_line_comment",
110
+ "math_block",
111
+ ]
112
+
113
+ # Optional MyST Syntaxes
114
+ myst_enable_extensions = []
115
+
116
+ # -- Options for HTML output -------------------------------------------------
117
+
118
+ # The theme to use for HTML and HTML Help pages. See the documentation for
119
+ # a list of builtin themes.
120
+ #
121
+ html_theme = "furo"
122
+ html_logo = "_static/logo2-readme.png"
123
+
124
+ # Add any paths that contain custom static files (such as style sheets) here,
125
+ # relative to this directory. They are copied after the builtin static files,
126
+ # so a file named "default.css" will overwrite the builtin "default.css".
127
+ html_static_path = ["_static"]
128
+
129
+ # Custom sidebar templates, must be a dictionary that maps document names
130
+ # to template names.
131
+ #
132
+ # The default sidebars (for documents that don't match any pattern) are
133
+ # defined by theme itself. Builtin themes are using these templates by
134
+ # default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
135
+ # 'searchbox.html']``.
136
+ #
137
+ # html_sidebars = {}
138
+
139
+
140
+ # -- Options for HTMLHelp output ---------------------------------------------
141
+
142
+ # Output file base name for HTML help builder.
143
+ htmlhelp_basename = "blackdoc"
144
+
145
+
146
+ # -- Options for LaTeX output ------------------------------------------------
147
+
148
+ # Grouping the document tree into LaTeX files. List of tuples
149
+ # (source start file, target name, title,
150
+ # author, documentclass [howto, manual, or own class]).
151
+ latex_documents = [(
152
+ master_doc,
153
+ "black.tex",
154
+ "Documentation for Black",
155
+ "Łukasz Langa and contributors to Black",
156
+ "manual",
157
+ )]
158
+
159
+
160
+ # -- Options for manual page output ------------------------------------------
161
+
162
+ # One entry per manual page. List of tuples
163
+ # (source start file, name, description, authors, manual section).
164
+ man_pages = [(master_doc, "black", "Documentation for Black", [author], 1)]
165
+
166
+
167
+ # -- Options for Texinfo output ----------------------------------------------
168
+
169
+ # Grouping the document tree into Texinfo files. List of tuples
170
+ # (source start file, target name, title, author,
171
+ # dir menu entry, description, category)
172
+ texinfo_documents = [(
173
+ master_doc,
174
+ "Black",
175
+ "Documentation for Black",
176
+ author,
177
+ "Black",
178
+ "The uncompromising Python code formatter",
179
+ "Miscellaneous",
180
+ )]
181
+
182
+
183
+ # -- Options for Epub output -------------------------------------------------
184
+
185
+ # Bibliographic Dublin Core info.
186
+ epub_title = project
187
+ epub_author = author
188
+ epub_publisher = author
189
+ epub_copyright = copyright
190
+
191
+ # The unique identifier of the text. This can be a ISBN number
192
+ # or the project homepage.
193
+ #
194
+ # epub_identifier = ''
195
+
196
+ # A unique identification for the text.
197
+ #
198
+ # epub_uid = ''
199
+
200
+ # A list of files that should not be packed into the epub file.
201
+ epub_exclude_files = ["search.html"]
202
+
203
+
204
+ # -- Extension configuration -------------------------------------------------
205
+
206
+ autodoc_member_order = "bysource"
207
+
208
+ # -- sphinx-copybutton configuration ----------------------------------------
209
+ copybutton_prompt_text = (
210
+ r">>> |\.\.\. |> |\$ |\# | In \[\d*\]: | {2,5}\.\.\.: | {5,8}: "
211
+ )
212
+ copybutton_prompt_is_regexp = True
213
+ copybutton_remove_prompts = True
214
+
215
+ # -- Options for intersphinx extension ---------------------------------------
216
+
217
+ # Example configuration for intersphinx: refer to the Python standard library.
218
+ intersphinx_mapping = {"<name>": ("https://docs.python.org/3/", None)}
@@ -0,0 +1,294 @@
1
+ import atexit
2
+ import json
3
+ import subprocess
4
+ import tarfile
5
+ import tempfile
6
+ import traceback
7
+ import venv
8
+ import zipfile
9
+ from argparse import ArgumentParser, Namespace
10
+ from concurrent.futures import ThreadPoolExecutor
11
+ from functools import lru_cache, partial
12
+ from pathlib import Path
13
+ from typing import Generator, List, NamedTuple, Optional, Tuple, Union, cast
14
+ from urllib.request import urlopen, urlretrieve
15
+
16
+ PYPI_INSTANCE = "https://pypi.org/pypi"
17
+ PYPI_TOP_PACKAGES = (
18
+ "https://hugovk.github.io/top-pypi-packages/top-pypi-packages-30-days.min.json"
19
+ )
20
+ INTERNAL_BLACK_REPO = f"{tempfile.gettempdir()}/__black"
21
+
22
+ ArchiveKind = Union[tarfile.TarFile, zipfile.ZipFile]
23
+
24
+ subprocess.run = partial(subprocess.run, check=True) # type: ignore
25
+ # https://github.com/python/mypy/issues/1484
26
+
27
+
28
+ class BlackVersion(NamedTuple):
29
+ version: str
30
+ config: Optional[str] = None
31
+
32
+
33
+ def get_pypi_download_url(package: str, version: Optional[str]) -> str:
34
+ with urlopen(PYPI_INSTANCE + f"/{package}/json") as page:
35
+ metadata = json.load(page)
36
+
37
+ if version is None:
38
+ sources = metadata["urls"]
39
+ else:
40
+ if version in metadata["releases"]:
41
+ sources = metadata["releases"][version]
42
+ else:
43
+ raise ValueError(
44
+ f"No releases found with version ('{version}') tag. "
45
+ f"Found releases: {metadata['releases'].keys()}"
46
+ )
47
+
48
+ for source in sources:
49
+ if source["python_version"] == "source":
50
+ break
51
+ else:
52
+ raise ValueError(f"Couldn't find any sources for {package}")
53
+
54
+ return cast(str, source["url"])
55
+
56
+
57
+ def get_top_packages() -> List[str]:
58
+ with urlopen(PYPI_TOP_PACKAGES) as page:
59
+ result = json.load(page)
60
+
61
+ return [package["project"] for package in result["rows"]]
62
+
63
+
64
+ def get_package_source(package: str, version: Optional[str]) -> str:
65
+ if package == "cpython":
66
+ if version is None:
67
+ version = "main"
68
+ return f"https://github.com/python/cpython/archive/{version}.zip"
69
+ elif package == "pypy":
70
+ if version is None:
71
+ version = "branch/default"
72
+ return (
73
+ f"https://foss.heptapod.net/pypy/pypy/repository/{version}/archive.tar.bz2"
74
+ )
75
+ else:
76
+ return get_pypi_download_url(package, version)
77
+
78
+
79
+ def get_archive_manager(local_file: str) -> ArchiveKind:
80
+ if tarfile.is_tarfile(local_file):
81
+ return tarfile.open(local_file)
82
+ elif zipfile.is_zipfile(local_file):
83
+ return zipfile.ZipFile(local_file)
84
+ else:
85
+ raise ValueError("Unknown archive kind.")
86
+
87
+
88
+ def get_first_archive_member(archive: ArchiveKind) -> str:
89
+ if isinstance(archive, tarfile.TarFile):
90
+ return archive.getnames()[0]
91
+ elif isinstance(archive, zipfile.ZipFile):
92
+ return archive.namelist()[0]
93
+
94
+
95
+ def download_and_extract(package: str, version: Optional[str], directory: Path) -> Path:
96
+ source = get_package_source(package, version)
97
+
98
+ local_file, _ = urlretrieve(source, directory / f"{package}-src")
99
+ with get_archive_manager(local_file) as archive:
100
+ archive.extractall(path=directory)
101
+ result_dir = get_first_archive_member(archive)
102
+ return directory / result_dir
103
+
104
+
105
+ def get_package(
106
+ package: str, version: Optional[str], directory: Path
107
+ ) -> Optional[Path]:
108
+ try:
109
+ return download_and_extract(package, version, directory)
110
+ except Exception:
111
+ print(f"Caught an exception while downloading {package}.")
112
+ traceback.print_exc()
113
+ return None
114
+
115
+
116
+ DEFAULT_SLICE = slice(None) # for flake8
117
+
118
+
119
+ def download_and_extract_top_packages(
120
+ directory: Path,
121
+ workers: int = 8,
122
+ limit: slice = DEFAULT_SLICE,
123
+ ) -> Generator[Path, None, None]:
124
+ with ThreadPoolExecutor(max_workers=workers) as executor:
125
+ bound_downloader = partial(get_package, version=None, directory=directory)
126
+ for package in executor.map(bound_downloader, get_top_packages()[limit]):
127
+ if package is not None:
128
+ yield package
129
+
130
+
131
+ def git_create_repository(repo: Path) -> None:
132
+ subprocess.run(["git", "init"], cwd=repo)
133
+ git_add_and_commit(msg="Initial commit", repo=repo)
134
+
135
+
136
+ def git_add_and_commit(msg: str, repo: Path) -> None:
137
+ subprocess.run(["git", "add", "."], cwd=repo)
138
+ subprocess.run(["git", "commit", "-m", msg, "--allow-empty"], cwd=repo)
139
+
140
+
141
+ def git_switch_branch(
142
+ branch: str, repo: Path, new: bool = False, from_branch: Optional[str] = None
143
+ ) -> None:
144
+ args = ["git", "checkout"]
145
+ if new:
146
+ args.append("-b")
147
+ args.append(branch)
148
+ if from_branch:
149
+ args.append(from_branch)
150
+ subprocess.run(args, cwd=repo)
151
+
152
+
153
+ def init_repos(options: Namespace) -> Tuple[Path, ...]:
154
+ options.output.mkdir(exist_ok=True)
155
+
156
+ if options.top_packages:
157
+ source_directories = tuple(
158
+ download_and_extract_top_packages(
159
+ directory=options.output,
160
+ workers=options.workers,
161
+ limit=slice(None, options.top_packages),
162
+ )
163
+ )
164
+ else:
165
+ source_directories = (
166
+ download_and_extract(
167
+ package=options.pypi_package,
168
+ version=options.version,
169
+ directory=options.output,
170
+ ),
171
+ )
172
+
173
+ for source_directory in source_directories:
174
+ git_create_repository(source_directory)
175
+
176
+ if options.black_repo is None:
177
+ subprocess.run(
178
+ ["git", "clone", "https://github.com/psf/black.git", INTERNAL_BLACK_REPO],
179
+ cwd=options.output,
180
+ )
181
+ options.black_repo = options.output / INTERNAL_BLACK_REPO
182
+
183
+ return source_directories
184
+
185
+
186
+ @lru_cache(8)
187
+ def black_runner(version: str, black_repo: Path) -> Path:
188
+ directory = tempfile.TemporaryDirectory()
189
+ venv.create(directory.name, with_pip=True)
190
+
191
+ python = Path(directory.name) / "bin" / "python"
192
+ subprocess.run([python, "-m", "pip", "install", "-e", black_repo])
193
+
194
+ atexit.register(directory.cleanup)
195
+ return python
196
+
197
+
198
+ def format_repo_with_version(
199
+ repo: Path,
200
+ from_branch: Optional[str],
201
+ black_repo: Path,
202
+ black_version: BlackVersion,
203
+ input_directory: Path,
204
+ ) -> str:
205
+ current_branch = f"black-{black_version.version}"
206
+ git_switch_branch(black_version.version, repo=black_repo)
207
+ git_switch_branch(current_branch, repo=repo, new=True, from_branch=from_branch)
208
+
209
+ format_cmd: List[Union[Path, str]] = [
210
+ black_runner(black_version.version, black_repo),
211
+ (black_repo / "black.py").resolve(),
212
+ ".",
213
+ ]
214
+ if black_version.config:
215
+ format_cmd.extend(["--config", input_directory / black_version.config])
216
+
217
+ subprocess.run(format_cmd, cwd=repo, check=False) # ensure the process
218
+ # continuess to run even it can't format some files. Reporting those
219
+ # should be enough
220
+ git_add_and_commit(f"Format with black:{black_version.version}", repo=repo)
221
+
222
+ return current_branch
223
+
224
+
225
+ def format_repos(repos: Tuple[Path, ...], options: Namespace) -> None:
226
+ black_versions = tuple(
227
+ BlackVersion(*version.split(":")) for version in options.versions
228
+ )
229
+
230
+ for repo in repos:
231
+ from_branch = None
232
+ for black_version in black_versions:
233
+ from_branch = format_repo_with_version(
234
+ repo=repo,
235
+ from_branch=from_branch,
236
+ black_repo=options.black_repo,
237
+ black_version=black_version,
238
+ input_directory=options.input,
239
+ )
240
+ git_switch_branch("main", repo=repo)
241
+
242
+ git_switch_branch("main", repo=options.black_repo)
243
+
244
+
245
+ def main() -> None:
246
+ parser = ArgumentParser(description="""Black Gallery is a script that
247
+ automates the process of applying different Black versions to a selected
248
+ PyPI package and seeing the results between versions.""")
249
+
250
+ group = parser.add_mutually_exclusive_group(required=True)
251
+ group.add_argument("-p", "--pypi-package", help="PyPI package to download.")
252
+ group.add_argument(
253
+ "-t", "--top-packages", help="Top n PyPI packages to download.", type=int
254
+ )
255
+
256
+ parser.add_argument("-b", "--black-repo", help="Black's Git repository.", type=Path)
257
+ parser.add_argument(
258
+ "-v",
259
+ "--version",
260
+ help=(
261
+ "Version for given PyPI package. Will be discarded if used with -t option."
262
+ ),
263
+ )
264
+ parser.add_argument(
265
+ "-w",
266
+ "--workers",
267
+ help=(
268
+ "Maximum number of threads to download with at the same time. "
269
+ "Will be discarded if used with -p option."
270
+ ),
271
+ )
272
+ parser.add_argument(
273
+ "-i",
274
+ "--input",
275
+ default=Path("/input"),
276
+ type=Path,
277
+ help="Input directory to read configuration.",
278
+ )
279
+ parser.add_argument(
280
+ "-o",
281
+ "--output",
282
+ default=Path("/output"),
283
+ type=Path,
284
+ help="Output directory to download and put result artifacts.",
285
+ )
286
+ parser.add_argument("versions", nargs="*", default=("main",), help="")
287
+
288
+ options = parser.parse_args()
289
+ repos = init_repos(options)
290
+ format_repos(repos, options)
291
+
292
+
293
+ if __name__ == "__main__":
294
+ main()