def get_features_used(node: Node) -> Set[Feature]: """Return a set of (relatively) new Python features used in this file. Currently looking for: - f-strings; - underscores in numeric literals; - trailing commas after * or ** in function signatures and calls; - positional only arguments in function signatures and lambdas; - assignment expression; - relaxed decorator syntax; """ features: Set[Feature] = set() for n in node.pre_order(): if n.type == token.STRING: value_head = n.value[:2] # type: ignore if value_head in {'f"', 'F"', "f'", "F'", "rf", "fr", "RF", "FR"}: features.add(Feature.F_STRINGS) elif n.type == token.NUMBER: if "_" in n.value: # type: ignore features.add(Feature.NUMERIC_UNDERSCORES) elif n.type == token.SLASH: if n.parent and n.parent.type in {syms.typedargslist, syms.arglist}: features.add(Feature.POS_ONLY_ARGUMENTS) elif n.type == token.COLONEQUAL: features.add(Feature.ASSIGNMENT_EXPRESSIONS) elif n.type == syms.decorator: if len(n.children) > 1 and not is_simple_decorator_expression( n.children[1] ): features.add(Feature.RELAXED_DECORATORS) elif ( n.type in {syms.typedargslist, syms.arglist} and n.children and n.children[-1].type == token.COMMA ): if n.type == syms.typedargslist: feature = Feature.TRAILING_COMMA_IN_DEF else: feature = Feature.TRAILING_COMMA_IN_CALL for ch in n.children: if ch.type in STARS: features.add(feature) if ch.type == syms.argument: for argch in ch.children: if argch.type in STARS: features.add(feature) return features
def get_features_used(node: Node) -> Set[Feature]: # noqa: C901 """Return a set of (relatively) new Python features used in this file. Currently looking for: - f-strings; - underscores in numeric literals; - trailing commas after * or ** in function signatures and calls; - positional only arguments in function signatures and lambdas; - assignment expression; - relaxed decorator syntax; - print / exec statements; """ features: Set[Feature] = set() for n in node.pre_order(): if n.type == token.STRING: value_head = n.value[:2] # type: ignore if value_head in {'f"', 'F"', "f'", "F'", "rf", "fr", "RF", "FR"}: features.add(Feature.F_STRINGS) elif n.type == token.NUMBER: assert isinstance(n, Leaf) if "_" in n.value: features.add(Feature.NUMERIC_UNDERSCORES) elif n.value.endswith(("L", "l")): # Python 2: 10L features.add(Feature.LONG_INT_LITERAL) elif len(n.value ) >= 2 and n.value[0] == "0" and n.value[1].isdigit(): # Python 2: 0123; 00123; ... if not all(char == "0" for char in n.value): # although we don't want to match 0000 or similar features.add(Feature.OCTAL_INT_LITERAL) elif n.type == token.SLASH: if n.parent and n.parent.type in { syms.typedargslist, syms.arglist, syms.varargslist, }: features.add(Feature.POS_ONLY_ARGUMENTS) elif n.type == token.COLONEQUAL: features.add(Feature.ASSIGNMENT_EXPRESSIONS) elif n.type == syms.decorator: if len(n.children) > 1 and not is_simple_decorator_expression( n.children[1]): features.add(Feature.RELAXED_DECORATORS) elif (n.type in {syms.typedargslist, syms.arglist} and n.children and n.children[-1].type == token.COMMA): if n.type == syms.typedargslist: feature = Feature.TRAILING_COMMA_IN_DEF else: feature = Feature.TRAILING_COMMA_IN_CALL for ch in n.children: if ch.type in STARS: features.add(feature) if ch.type == syms.argument: for argch in ch.children: if argch.type in STARS: features.add(feature) # Python 2 only features (for its deprecation) except for integers, see above elif n.type == syms.print_stmt: features.add(Feature.PRINT_STMT) elif n.type == syms.exec_stmt: features.add(Feature.EXEC_STMT) elif n.type == syms.tfpdef: # def set_position((x, y), value): # ... features.add(Feature.AUTOMATIC_PARAMETER_UNPACKING) elif n.type == syms.except_clause: # try: # ... # except Exception, err: # ... if len(n.children) >= 4: if n.children[-2].type == token.COMMA: features.add(Feature.COMMA_STYLE_EXCEPT) elif n.type == syms.raise_stmt: # raise Exception, "msg" if len(n.children) >= 4: if n.children[-2].type == token.COMMA: features.add(Feature.COMMA_STYLE_RAISE) elif n.type == token.BACKQUOTE: # `i'm surprised this ever existed` features.add(Feature.BACKQUOTE_REPR) return features
def get_features_used( # noqa: C901 node: Node, *, future_imports: Optional[Set[str]] = None) -> Set[Feature]: """Return a set of (relatively) new Python features used in this file. Currently looking for: - f-strings; - underscores in numeric literals; - trailing commas after * or ** in function signatures and calls; - positional only arguments in function signatures and lambdas; - assignment expression; - relaxed decorator syntax; - usage of __future__ flags (annotations); - print / exec statements; """ features: Set[Feature] = set() if future_imports: features |= { FUTURE_FLAG_TO_FEATURE[future_import] for future_import in future_imports if future_import in FUTURE_FLAG_TO_FEATURE } for n in node.pre_order(): if is_string_token(n): value_head = n.value[:2] if value_head in {'f"', 'F"', "f'", "F'", "rf", "fr", "RF", "FR"}: features.add(Feature.F_STRINGS) elif n.type == token.NUMBER: assert isinstance(n, Leaf) if "_" in n.value: features.add(Feature.NUMERIC_UNDERSCORES) elif n.type == token.SLASH: if n.parent and n.parent.type in { syms.typedargslist, syms.arglist, syms.varargslist, }: features.add(Feature.POS_ONLY_ARGUMENTS) elif n.type == token.COLONEQUAL: features.add(Feature.ASSIGNMENT_EXPRESSIONS) elif n.type == syms.decorator: if len(n.children) > 1 and not is_simple_decorator_expression( n.children[1]): features.add(Feature.RELAXED_DECORATORS) elif (n.type in {syms.typedargslist, syms.arglist} and n.children and n.children[-1].type == token.COMMA): if n.type == syms.typedargslist: feature = Feature.TRAILING_COMMA_IN_DEF else: feature = Feature.TRAILING_COMMA_IN_CALL for ch in n.children: if ch.type in STARS: features.add(feature) if ch.type == syms.argument: for argch in ch.children: if argch.type in STARS: features.add(feature) elif (n.type in {syms.return_stmt, syms.yield_expr} and len(n.children) >= 2 and n.children[1].type == syms.testlist_star_expr and any(child.type == syms.star_expr for child in n.children[1].children)): features.add(Feature.UNPACKING_ON_FLOW) elif (n.type == syms.annassign and len(n.children) >= 4 and n.children[3].type == syms.testlist_star_expr): features.add(Feature.ANN_ASSIGN_EXTENDED_RHS) elif (n.type == syms.except_clause and len(n.children) >= 2 and n.children[1].type == token.STAR): features.add(Feature.EXCEPT_STAR) return features