]> 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:

Unpacking on flow constructs (return/yield) now implies 3.8+ (#2700)
[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     UNPACKING_ON_FLOW = 12
53     FORCE_OPTIONAL_PARENTHESES = 50
54
55     # __future__ flags
56     FUTURE_ANNOTATIONS = 51
57
58     # temporary for Python 2 deprecation
59     PRINT_STMT = 200
60     EXEC_STMT = 201
61     AUTOMATIC_PARAMETER_UNPACKING = 202
62     COMMA_STYLE_EXCEPT = 203
63     COMMA_STYLE_RAISE = 204
64     LONG_INT_LITERAL = 205
65     OCTAL_INT_LITERAL = 206
66     BACKQUOTE_REPR = 207
67
68
69 FUTURE_FLAG_TO_FEATURE: Final = {
70     "annotations": Feature.FUTURE_ANNOTATIONS,
71 }
72
73
74 VERSION_TO_FEATURES: Dict[TargetVersion, Set[Feature]] = {
75     TargetVersion.PY27: {
76         Feature.ASYNC_IDENTIFIERS,
77         Feature.PRINT_STMT,
78         Feature.EXEC_STMT,
79         Feature.AUTOMATIC_PARAMETER_UNPACKING,
80         Feature.COMMA_STYLE_EXCEPT,
81         Feature.COMMA_STYLE_RAISE,
82         Feature.LONG_INT_LITERAL,
83         Feature.OCTAL_INT_LITERAL,
84         Feature.BACKQUOTE_REPR,
85     },
86     TargetVersion.PY33: {Feature.UNICODE_LITERALS, Feature.ASYNC_IDENTIFIERS},
87     TargetVersion.PY34: {Feature.UNICODE_LITERALS, Feature.ASYNC_IDENTIFIERS},
88     TargetVersion.PY35: {
89         Feature.UNICODE_LITERALS,
90         Feature.TRAILING_COMMA_IN_CALL,
91         Feature.ASYNC_IDENTIFIERS,
92     },
93     TargetVersion.PY36: {
94         Feature.UNICODE_LITERALS,
95         Feature.F_STRINGS,
96         Feature.NUMERIC_UNDERSCORES,
97         Feature.TRAILING_COMMA_IN_CALL,
98         Feature.TRAILING_COMMA_IN_DEF,
99         Feature.ASYNC_IDENTIFIERS,
100     },
101     TargetVersion.PY37: {
102         Feature.UNICODE_LITERALS,
103         Feature.F_STRINGS,
104         Feature.NUMERIC_UNDERSCORES,
105         Feature.TRAILING_COMMA_IN_CALL,
106         Feature.TRAILING_COMMA_IN_DEF,
107         Feature.ASYNC_KEYWORDS,
108         Feature.FUTURE_ANNOTATIONS,
109     },
110     TargetVersion.PY38: {
111         Feature.UNICODE_LITERALS,
112         Feature.F_STRINGS,
113         Feature.NUMERIC_UNDERSCORES,
114         Feature.TRAILING_COMMA_IN_CALL,
115         Feature.TRAILING_COMMA_IN_DEF,
116         Feature.ASYNC_KEYWORDS,
117         Feature.FUTURE_ANNOTATIONS,
118         Feature.ASSIGNMENT_EXPRESSIONS,
119         Feature.POS_ONLY_ARGUMENTS,
120         Feature.UNPACKING_ON_FLOW,
121     },
122     TargetVersion.PY39: {
123         Feature.UNICODE_LITERALS,
124         Feature.F_STRINGS,
125         Feature.NUMERIC_UNDERSCORES,
126         Feature.TRAILING_COMMA_IN_CALL,
127         Feature.TRAILING_COMMA_IN_DEF,
128         Feature.ASYNC_KEYWORDS,
129         Feature.FUTURE_ANNOTATIONS,
130         Feature.ASSIGNMENT_EXPRESSIONS,
131         Feature.RELAXED_DECORATORS,
132         Feature.POS_ONLY_ARGUMENTS,
133         Feature.UNPACKING_ON_FLOW,
134     },
135     TargetVersion.PY310: {
136         Feature.UNICODE_LITERALS,
137         Feature.F_STRINGS,
138         Feature.NUMERIC_UNDERSCORES,
139         Feature.TRAILING_COMMA_IN_CALL,
140         Feature.TRAILING_COMMA_IN_DEF,
141         Feature.ASYNC_KEYWORDS,
142         Feature.FUTURE_ANNOTATIONS,
143         Feature.ASSIGNMENT_EXPRESSIONS,
144         Feature.RELAXED_DECORATORS,
145         Feature.POS_ONLY_ARGUMENTS,
146         Feature.UNPACKING_ON_FLOW,
147         Feature.PATTERN_MATCHING,
148     },
149 }
150
151
152 def supports_feature(target_versions: Set[TargetVersion], feature: Feature) -> bool:
153     return all(feature in VERSION_TO_FEATURES[version] for version in target_versions)
154
155
156 @dataclass
157 class Mode:
158     target_versions: Set[TargetVersion] = field(default_factory=set)
159     line_length: int = DEFAULT_LINE_LENGTH
160     string_normalization: bool = True
161     is_pyi: bool = False
162     is_ipynb: bool = False
163     magic_trailing_comma: bool = True
164     experimental_string_processing: bool = False
165
166     def get_cache_key(self) -> str:
167         if self.target_versions:
168             version_str = ",".join(
169                 str(version.value)
170                 for version in sorted(self.target_versions, key=attrgetter("value"))
171             )
172         else:
173             version_str = "-"
174         parts = [
175             version_str,
176             str(self.line_length),
177             str(int(self.string_normalization)),
178             str(int(self.is_pyi)),
179             str(int(self.is_ipynb)),
180             str(int(self.magic_trailing_comma)),
181             str(int(self.experimental_string_processing)),
182         ]
183         return ".".join(parts)