+class TargetVersion(Enum):
+ PYPY35 = 1
+ CPY27 = 2
+ CPY33 = 3
+ CPY34 = 4
+ CPY35 = 5
+ CPY36 = 6
+ CPY37 = 7
+ CPY38 = 8
+
+ def is_python2(self) -> bool:
+ return self is TargetVersion.CPY27
+
+
+PY36_VERSIONS = {TargetVersion.CPY36, TargetVersion.CPY37, TargetVersion.CPY38}
+
+
+class Feature(Enum):
+ # All string literals are unicode
+ UNICODE_LITERALS = 1
+ F_STRINGS = 2
+ NUMERIC_UNDERSCORES = 3
+ TRAILING_COMMA = 4
+
+
+VERSION_TO_FEATURES: Dict[TargetVersion, Set[Feature]] = {
+ TargetVersion.CPY27: set(),
+ TargetVersion.PYPY35: {Feature.UNICODE_LITERALS, Feature.F_STRINGS},
+ TargetVersion.CPY33: {Feature.UNICODE_LITERALS},
+ TargetVersion.CPY34: {Feature.UNICODE_LITERALS},
+ TargetVersion.CPY35: {Feature.UNICODE_LITERALS, Feature.TRAILING_COMMA},
+ TargetVersion.CPY36: {
+ Feature.UNICODE_LITERALS,
+ Feature.F_STRINGS,
+ Feature.NUMERIC_UNDERSCORES,
+ Feature.TRAILING_COMMA,
+ },
+ TargetVersion.CPY37: {
+ Feature.UNICODE_LITERALS,
+ Feature.F_STRINGS,
+ Feature.NUMERIC_UNDERSCORES,
+ Feature.TRAILING_COMMA,
+ },
+ TargetVersion.CPY38: {
+ Feature.UNICODE_LITERALS,
+ Feature.F_STRINGS,
+ Feature.NUMERIC_UNDERSCORES,
+ Feature.TRAILING_COMMA,
+ },
+}
+
+
+@dataclass
+class FileMode:
+ target_versions: Set[TargetVersion] = Factory(set)
+ line_length: int = DEFAULT_LINE_LENGTH
+ string_normalization: bool = True
+ is_pyi: bool = False
+
+ def get_cache_key(self) -> str:
+ if self.target_versions:
+ version_str = ",".join(
+ str(version.value)
+ for version in sorted(self.target_versions, key=lambda v: v.value)
+ )
+ else:
+ version_str = "-"
+ parts = [
+ version_str,
+ str(self.line_length),
+ str(int(self.string_normalization)),
+ str(int(self.is_pyi)),
+ ]
+ return ".".join(parts)