]> git.madduck.net Git - etc/vim.git/blob - src/black/mode.py

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

use valid package-ecosystem key (#2694)
[etc/vim.git] / src / black / mode.py
1 """Data structures configuring Black behavior.
2
3 Mostly around Python language feature support per version and Black configuration
4 chosen by the user.
5 """
6
7 import sys
8
9 from dataclasses import dataclass, field
10 from enum import Enum
11 from operator import attrgetter
12 from typing import Dict, Set
13
14 if sys.version_info < (3, 8):
15     from typing_extensions import Final
16 else:
17     from typing import Final
18
19 from black.const import DEFAULT_LINE_LENGTH
20
21
22 class TargetVersion(Enum):
23     PY27 = 2
24     PY33 = 3
25     PY34 = 4
26     PY35 = 5
27     PY36 = 6
28     PY37 = 7
29     PY38 = 8
30     PY39 = 9
31     PY310 = 10
32
33     def is_python2(self) -> bool:
34         return self is TargetVersion.PY27
35
36
37 class Feature(Enum):
38     # All string literals are unicode
39     UNICODE_LITERALS = 1
40     F_STRINGS = 2
41     NUMERIC_UNDERSCORES = 3
42     TRAILING_COMMA_IN_CALL = 4
43     TRAILING_COMMA_IN_DEF = 5
44     # The following two feature-flags are mutually exclusive, and exactly one should be
45     # set for every version of python.
46     ASYNC_IDENTIFIERS = 6
47     ASYNC_KEYWORDS = 7
48     ASSIGNMENT_EXPRESSIONS = 8
49     POS_ONLY_ARGUMENTS = 9
50     RELAXED_DECORATORS = 10
51     PATTERN_MATCHING = 11
52     FORCE_OPTIONAL_PARENTHESES = 50
53
54     # __future__ flags
55     FUTURE_ANNOTATIONS = 51
56
57     # temporary for Python 2 deprecation
58     PRINT_STMT = 200
59     EXEC_STMT = 201
60     AUTOMATIC_PARAMETER_UNPACKING = 202
61     COMMA_STYLE_EXCEPT = 203
62     COMMA_STYLE_RAISE = 204
63     LONG_INT_LITERAL = 205
64     OCTAL_INT_LITERAL = 206
65     BACKQUOTE_REPR = 207
66
67
68 FUTURE_FLAG_TO_FEATURE: Final = {
69     "annotations": Feature.FUTURE_ANNOTATIONS,
70 }
71
72
73 VERSION_TO_FEATURES: Dict[TargetVersion, Set[Feature]] = {
74     TargetVersion.PY27: {
75         Feature.ASYNC_IDENTIFIERS,
76         Feature.PRINT_STMT,
77         Feature.EXEC_STMT,
78         Feature.AUTOMATIC_PARAMETER_UNPACKING,
79         Feature.COMMA_STYLE_EXCEPT,
80         Feature.COMMA_STYLE_RAISE,
81         Feature.LONG_INT_LITERAL,
82         Feature.OCTAL_INT_LITERAL,
83         Feature.BACKQUOTE_REPR,
84     },
85     TargetVersion.PY33: {Feature.UNICODE_LITERALS, Feature.ASYNC_IDENTIFIERS},
86     TargetVersion.PY34: {Feature.UNICODE_LITERALS, Feature.ASYNC_IDENTIFIERS},
87     TargetVersion.PY35: {
88         Feature.UNICODE_LITERALS,
89         Feature.TRAILING_COMMA_IN_CALL,
90         Feature.ASYNC_IDENTIFIERS,
91     },
92     TargetVersion.PY36: {
93         Feature.UNICODE_LITERALS,
94         Feature.F_STRINGS,
95         Feature.NUMERIC_UNDERSCORES,
96         Feature.TRAILING_COMMA_IN_CALL,
97         Feature.TRAILING_COMMA_IN_DEF,
98         Feature.ASYNC_IDENTIFIERS,
99     },
100     TargetVersion.PY37: {
101         Feature.UNICODE_LITERALS,
102         Feature.F_STRINGS,
103         Feature.NUMERIC_UNDERSCORES,
104         Feature.TRAILING_COMMA_IN_CALL,
105         Feature.TRAILING_COMMA_IN_DEF,
106         Feature.ASYNC_KEYWORDS,
107         Feature.FUTURE_ANNOTATIONS,
108     },
109     TargetVersion.PY38: {
110         Feature.UNICODE_LITERALS,
111         Feature.F_STRINGS,
112         Feature.NUMERIC_UNDERSCORES,
113         Feature.TRAILING_COMMA_IN_CALL,
114         Feature.TRAILING_COMMA_IN_DEF,
115         Feature.ASYNC_KEYWORDS,
116         Feature.FUTURE_ANNOTATIONS,
117         Feature.ASSIGNMENT_EXPRESSIONS,
118         Feature.POS_ONLY_ARGUMENTS,
119     },
120     TargetVersion.PY39: {
121         Feature.UNICODE_LITERALS,
122         Feature.F_STRINGS,
123         Feature.NUMERIC_UNDERSCORES,
124         Feature.TRAILING_COMMA_IN_CALL,
125         Feature.TRAILING_COMMA_IN_DEF,
126         Feature.ASYNC_KEYWORDS,
127         Feature.FUTURE_ANNOTATIONS,
128         Feature.ASSIGNMENT_EXPRESSIONS,
129         Feature.RELAXED_DECORATORS,
130         Feature.POS_ONLY_ARGUMENTS,
131     },
132     TargetVersion.PY310: {
133         Feature.UNICODE_LITERALS,
134         Feature.F_STRINGS,
135         Feature.NUMERIC_UNDERSCORES,
136         Feature.TRAILING_COMMA_IN_CALL,
137         Feature.TRAILING_COMMA_IN_DEF,
138         Feature.ASYNC_KEYWORDS,
139         Feature.FUTURE_ANNOTATIONS,
140         Feature.ASSIGNMENT_EXPRESSIONS,
141         Feature.RELAXED_DECORATORS,
142         Feature.POS_ONLY_ARGUMENTS,
143         Feature.PATTERN_MATCHING,
144     },
145 }
146
147
148 def supports_feature(target_versions: Set[TargetVersion], feature: Feature) -> bool:
149     return all(feature in VERSION_TO_FEATURES[version] for version in target_versions)
150
151
152 @dataclass
153 class Mode:
154     target_versions: Set[TargetVersion] = field(default_factory=set)
155     line_length: int = DEFAULT_LINE_LENGTH
156     string_normalization: bool = True
157     is_pyi: bool = False
158     is_ipynb: bool = False
159     magic_trailing_comma: bool = True
160     experimental_string_processing: bool = False
161
162     def get_cache_key(self) -> str:
163         if self.target_versions:
164             version_str = ",".join(
165                 str(version.value)
166                 for version in sorted(self.target_versions, key=attrgetter("value"))
167             )
168         else:
169             version_str = "-"
170         parts = [
171             version_str,
172             str(self.line_length),
173             str(int(self.string_normalization)),
174             str(int(self.is_pyi)),
175             str(int(self.is_ipynb)),
176             str(int(self.magic_trailing_comma)),
177             str(int(self.experimental_string_processing)),
178         ]
179         return ".".join(parts)