-
-
Notifications
You must be signed in to change notification settings - Fork 209
add deduplication of types #1004
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
eca6277
dc9d5d9
1fae34a
90dfcab
1316d66
2bbe9d7
2a064a8
94931ad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,22 +14,36 @@ | |
| from pyupgrade._data import register | ||
| from pyupgrade._data import State | ||
| from pyupgrade._data import TokenFunc | ||
| from pyupgrade._token_helpers import find_closing_bracket | ||
| from pyupgrade._token_helpers import _OPENING | ||
| from pyupgrade._token_helpers import find_op | ||
| from pyupgrade._token_helpers import is_close | ||
| from pyupgrade._token_helpers import is_open | ||
|
|
||
|
|
||
| def _fix_optional(i: int, tokens: list[Token]) -> None: | ||
| j = find_op(tokens, i, '[') | ||
| k = find_closing_bracket(tokens, j) | ||
| k, contains_none = _find_closing_bracket_and_if_contains_none(tokens, j) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typically I add helper functions above where they're called rather than below |
||
| if tokens[j].line == tokens[k].line: | ||
| tokens[k] = Token('CODE', ' | None') | ||
| if contains_none: | ||
| del tokens[k] | ||
| else: | ||
| tokens[k:k + 1] = [ | ||
| Token('UNIMPORTANT_WS', ' '), | ||
| Token('CODE', '| '), | ||
| Token('CODE', 'None'), | ||
| ] | ||
|
Comment on lines
+30
to
+34
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reason for changing the single token containing
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it might be helpful to do all the tokens then -- |
||
| del tokens[i:j + 1] | ||
| else: | ||
| tokens[j] = tokens[j]._replace(src='(') | ||
| tokens[k] = tokens[k]._replace(src=')') | ||
| tokens[i:j] = [Token('CODE', 'None | ')] | ||
| if contains_none: | ||
| del tokens[i:j] | ||
| else: | ||
| tokens[i:j] = [ | ||
| Token('CODE', 'None'), | ||
| Token('UNIMPORTANT_WS', ' '), | ||
| Token('CODE', '| '), | ||
| ] | ||
|
|
||
|
|
||
| def _fix_union( | ||
|
|
@@ -43,6 +57,8 @@ def _fix_union( | |
| open_parens = [] | ||
| commas = [] | ||
| coding_depth = None | ||
| top_level_breaks = [] | ||
| lines_with_comments = [] | ||
|
|
||
| j = find_op(tokens, i, '[') | ||
| k = j + 1 | ||
|
|
@@ -70,11 +86,16 @@ def _fix_union( | |
| parens_done.append((paren_depth, (open_paren, k))) | ||
|
|
||
| depth -= 1 | ||
| elif tokens[k].src == ',': | ||
| commas.append((depth, k)) | ||
|
|
||
| elif tokens[k].src.strip() in [',', '|']: | ||
| if tokens[k].src.strip() == ',': | ||
| commas.append((depth, k)) | ||
| if depth == 1: | ||
| top_level_breaks.append(k) | ||
| elif tokens[k].name == 'COMMENT': | ||
| lines_with_comments.append(tokens[k].line) | ||
| k += 1 | ||
| k -= 1 | ||
| top_level_breaks.append(k) | ||
|
|
||
| assert coding_depth is not None | ||
| assert not open_parens, open_parens | ||
|
|
@@ -95,12 +116,15 @@ def _fix_union( | |
| else: | ||
| comma_positions = [] | ||
|
|
||
| to_delete.sort() | ||
| to_delete += _find_duplicated_types( | ||
| tokens, j, top_level_breaks, lines_with_comments, | ||
| ) | ||
|
|
||
| if tokens[j].line == tokens[k].line: | ||
| del tokens[k] | ||
| for comma in comma_positions: | ||
| tokens[comma] = Token('CODE', ' |') | ||
| to_delete.sort() | ||
| for paren in reversed(to_delete): | ||
| del tokens[paren] | ||
| del tokens[i:j + 1] | ||
|
|
@@ -110,11 +134,80 @@ def _fix_union( | |
|
|
||
| for comma in comma_positions: | ||
| tokens[comma] = Token('CODE', ' |') | ||
| to_delete += _remove_consecutive_unimportant_ws( | ||
| tokens, [x for x in range(j, k) if x not in to_delete], | ||
| ) | ||
|
Comment on lines
+137
to
+139
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not convinced this is the best approach to remove whitespace, but not sure about what to do in situations where lines are completely deleted other than comments. I have written a niche test for this situation in test |
||
| to_delete.sort() | ||
| for paren in reversed(to_delete): | ||
| del tokens[paren] | ||
| del tokens[i:j] | ||
|
|
||
|
|
||
| def _find_closing_bracket_and_if_contains_none( | ||
| tokens: list[Token], | ||
| i: int, | ||
| ) -> tuple[int, bool]: | ||
| assert tokens[i].src in _OPENING | ||
| depth = 1 | ||
| i += 1 | ||
| contains_none = False | ||
| while depth: | ||
| if is_open(tokens[i]): | ||
| depth += 1 | ||
| elif is_close(tokens[i]): | ||
| depth -= 1 | ||
| elif depth == 1 and tokens[i].matches(name='NAME', src='None'): | ||
| contains_none = True | ||
| i += 1 | ||
| return i - 1, contains_none | ||
|
|
||
|
|
||
| def _find_duplicated_types( | ||
| tokens: list[Token], | ||
| opening_bracket: int, | ||
| depth_1_commas: list[int], | ||
| lines_with_comments: list[int], | ||
| ) -> list[int]: | ||
| unique_names = [] | ||
| to_delete = [] | ||
| i = opening_bracket + 1 | ||
| for d1c in depth_1_commas: | ||
| important_tokens = [ | ||
| x | ||
| for x in range(i, d1c) | ||
| if tokens[x].name | ||
| not in ( | ||
| ['COMMENT'] | ||
| if tokens[x].line not in lines_with_comments | ||
| else ['COMMENT', 'NL', 'UNIMPORTANT_WS'] | ||
| ) | ||
| ] | ||
| type_ = ''.join([tokens[k].src.lstrip() for k in important_tokens]) | ||
| if type_[0] in [',', '|']: | ||
| type_ = type_[1:].lstrip() | ||
| if type_ in unique_names: | ||
| to_delete += important_tokens | ||
| else: | ||
| unique_names.append(type_) | ||
| i = d1c | ||
| return to_delete | ||
|
|
||
|
|
||
| def _remove_consecutive_unimportant_ws( | ||
| tokens: list[Token], idxs: list[int], | ||
| ) -> list[int]: | ||
| to_delete = [] | ||
| prev_name = '' | ||
| for kk in idxs: | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why kk? |
||
| if prev_name == 'UNIMPORTANT_WS': | ||
| if tokens[kk].name == 'UNIMPORTANT_WS': | ||
| to_delete.append(kk) | ||
| elif tokens[kk].src == ' |': | ||
| tokens[kk] = Token('CODE', '|') | ||
| prev_name = tokens[kk].name | ||
| return to_delete | ||
|
|
||
|
|
||
| def _supported_version(state: State) -> bool: | ||
| return ( | ||
| state.in_annotation and ( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modified the general
find_closing_bracketfunction to also check for whether the optional block already containsNonein the same pass.