class TargetVersion(Enum):
- PYPY35 = 1
- CPY27 = 2
- CPY33 = 3
- CPY34 = 4
- CPY35 = 5
- CPY36 = 6
- CPY37 = 7
- CPY38 = 8
+ PY27 = 2
+ PY33 = 3
+ PY34 = 4
+ PY35 = 5
+ PY36 = 6
+ PY37 = 7
+ PY38 = 8
def is_python2(self) -> bool:
- return self is TargetVersion.CPY27
+ return self is TargetVersion.PY27
-PY36_VERSIONS = {TargetVersion.CPY36, TargetVersion.CPY37, TargetVersion.CPY38}
+PY36_VERSIONS = {TargetVersion.PY36, TargetVersion.PY37, TargetVersion.PY38}
class Feature(Enum):
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: {
+ TargetVersion.PY27: set(),
+ TargetVersion.PY33: {Feature.UNICODE_LITERALS},
+ TargetVersion.PY34: {Feature.UNICODE_LITERALS},
+ TargetVersion.PY35: {Feature.UNICODE_LITERALS, Feature.TRAILING_COMMA},
+ TargetVersion.PY36: {
Feature.UNICODE_LITERALS,
Feature.F_STRINGS,
Feature.NUMERIC_UNDERSCORES,
Feature.TRAILING_COMMA,
},
- TargetVersion.CPY37: {
+ TargetVersion.PY37: {
Feature.UNICODE_LITERALS,
Feature.F_STRINGS,
Feature.NUMERIC_UNDERSCORES,
Feature.TRAILING_COMMA,
},
- TargetVersion.CPY38: {
+ TargetVersion.PY38: {
Feature.UNICODE_LITERALS,
Feature.F_STRINGS,
Feature.NUMERIC_UNDERSCORES,
depth: int = 0
leaves: List[Leaf] = Factory(list)
- # The LeafID keys of comments must remain ordered by the corresponding leaf's index
- # in leaves
- comments: Dict[LeafID, List[Leaf]] = Factory(dict)
+ comments: Dict[LeafID, List[Leaf]] = Factory(dict) # keys ordered like `leaves`
bracket_tracker: BracketTracker = Factory(BracketTracker)
inside_brackets: bool = False
should_explode: bool = False
if leaf.type == STANDALONE_COMMENT:
if leaf.bracket_depth <= depth_limit:
return True
+ return False
+
+ def contains_inner_type_comments(self) -> bool:
+ ignored_ids = set()
+ try:
+ last_leaf = self.leaves[-1]
+ ignored_ids.add(id(last_leaf))
+ if last_leaf.type == token.COMMA:
+ # When trailing commas are inserted by Black for consistency, comments
+ # after the previous last element are not moved (they don't have to,
+ # rendering will still be correct). So we ignore trailing commas.
+ last_leaf = self.leaves[-2]
+ ignored_ids.add(id(last_leaf))
+ except IndexError:
+ return False
+
+ for leaf_id, comments in self.comments.items():
+ if leaf_id in ignored_ids:
+ continue
+
+ for comment in comments:
+ if is_type_comment(comment):
+ return True
return False
comment.prefix = ""
return False
- else:
- leaf_id = id(self.leaves[-1])
- if leaf_id not in self.comments:
- self.comments[leaf_id] = [comment]
- else:
- self.comments[leaf_id].append(comment)
- return True
+ self.comments.setdefault(id(self.leaves[-1]), []).append(comment)
+ return True
def comments_after(self, leaf: Leaf) -> List[Leaf]:
"""Generate comments that should appear directly after `leaf`."""
def remove_trailing_comma(self) -> None:
"""Remove the trailing comma and moves the comments attached to it."""
- # Remember, the LeafID keys of self.comments are ordered by the
- # corresponding leaf's index in self.leaves
- # If id(self.leaves[-2]) is in self.comments, the order doesn't change.
- # Otherwise, we insert it into self.comments, and it becomes the last entry.
- # However, since we delete id(self.leaves[-1]) from self.comments, the invariant
- # is maintained
- self.comments.setdefault(id(self.leaves[-2]), []).extend(
- self.comments.get(id(self.leaves[-1]), [])
+ trailing_comma = self.leaves.pop()
+ trailing_comma_comments = self.comments.pop(id(trailing_comma), [])
+ self.comments.setdefault(id(self.leaves[-1]), []).extend(
+ trailing_comma_comments
)
- self.comments.pop(id(self.leaves[-1]), None)
- self.leaves.pop()
def is_complex_subscript(self, leaf: Leaf) -> bool:
"""Return True iff `leaf` is part of a slice with non-trivial exprs."""
line_str = str(line).strip("\n")
- # we don't want to split special comments like type annotations
- # https://github.com/python/typing/issues/186
- has_special_comment = False
- for leaf in line.leaves:
- for comment in line.comments_after(leaf):
- if leaf.type == token.COMMA and is_special_comment(comment):
- has_special_comment = True
-
if (
- not has_special_comment
+ not line.contains_inner_type_comments()
and not line.should_explode
and is_line_short_enough(line, line_length=line_length, line_str=line_str)
):
)
-def is_special_comment(leaf: Leaf) -> bool:
+def is_type_comment(leaf: Leaf) -> bool:
"""Return True if the given leaf is a special comment.
Only returns true for type comments for now."""
t = leaf.type
v = leaf.value
- return bool(
- (t == token.COMMENT or t == STANDALONE_COMMENT) and (v.startswith("# type:"))
- )
+ return t in {token.COMMENT, t == STANDALONE_COMMENT} and v.startswith("# type:")
def normalize_prefix(leaf: Leaf, *, inside_brackets: bool) -> None:
def shutdown(loop: BaseEventLoop) -> None:
"""Cancel all pending tasks on `loop`, wait for them, and close the loop."""
try:
+ if sys.version_info[:2] >= (3, 7):
+ all_tasks = asyncio.all_tasks
+ else:
+ all_tasks = asyncio.Task.all_tasks
# This part is borrowed from asyncio/runners.py in Python 3.7b2.
- to_cancel = [task for task in asyncio.Task.all_tasks(loop) if not task.done()]
+ to_cancel = [task for task in all_tasks(loop) if not task.done()]
if not to_cancel:
return