From afd79c21a41d611a3d49a014643c382fbed1c423 Mon Sep 17 00:00:00 2001 From: Hideo Hattori Date: Wed, 22 May 2024 21:07:58 +0900 Subject: [PATCH 01/19] fix: package publish --- .github/workflows/python-publish.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 0216f893..01b1c8f5 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -19,12 +19,12 @@ jobs: python-version: '3.x' - name: Install dependencies run: | - python -m pip install --upgrade pip + python -m pip install --upgrade pip build pip install setuptools wheel twine - name: Build and publish env: TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} run: | - python setup.py sdist bdist_wheel - twine upload dist/* + python -m build + python -m twine upload dist/* From 4b9c99f6104d14e3c00de40c3853bdf194f6abfb Mon Sep 17 00:00:00 2001 From: Hideo Hattori Date: Tue, 28 May 2024 23:11:35 +0900 Subject: [PATCH 02/19] ignore inc/dec bracket depth when paring in f-string --- autopep8.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/autopep8.py b/autopep8.py index 8c516e43..e311001e 100755 --- a/autopep8.py +++ b/autopep8.py @@ -2045,6 +2045,7 @@ def __init__(self, max_line_length): self._bracket_depth = 0 self._prev_item = None self._prev_prev_item = None + self._in_fstring = False def __repr__(self): return self.emit() @@ -2172,6 +2173,11 @@ def _add_item(self, item, indent_amt): inserted inside of a container or not. """ + if item.is_fstring_start: + self._in_fstring = True + elif self._prev_item and self._prev_item.is_fstring_end: + self._in_fstring = False + if self._prev_item and self._prev_item.is_string and item.is_string: # Place consecutive string literals on separate lines. self._lines.append(self._LineBreak()) @@ -2198,10 +2204,10 @@ def _add_item(self, item, indent_amt): self._lines.append(item) self._prev_item, self._prev_prev_item = item, self._prev_item - if item_text in '([{': + if item_text in '([{' and not self._in_fstring: self._bracket_depth += 1 - elif item_text in '}])': + elif item_text in '}])' and not self._in_fstring: self._bracket_depth -= 1 assert self._bracket_depth >= 0 @@ -2400,6 +2406,14 @@ def is_keyword(self): def is_string(self): return self._atom.token_type == tokenize.STRING + @property + def is_fstring_start(self): + return self._atom.token_type == tokenize.FSTRING_START + + @property + def is_fstring_end(self): + return self._atom.token_type == tokenize.FSTRING_END + @property def is_name(self): return self._atom.token_type == tokenize.NAME From a3228c99e37746579c0c37ab2cdcd47b68565a30 Mon Sep 17 00:00:00 2001 From: Hideo Hattori Date: Tue, 28 May 2024 23:11:54 +0900 Subject: [PATCH 03/19] add unit test --- test/test_autopep8.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/test_autopep8.py b/test/test_autopep8.py index 64031066..ea96caa1 100755 --- a/test/test_autopep8.py +++ b/test/test_autopep8.py @@ -7333,6 +7333,13 @@ def test_e501_experimental_with_in(self): with autopep8_context(line, options=['--experimental']) as result: self.assertEqual(fixed, result) + def test_e501_experimental_not_effect_with_fstring(self): + line = """\ +fstring = {"some_key": f"There is a string value inside of an f string, which itself is a dictionary value {s})"} +""" + with autopep8_context(line, options=['--experimental']) as result: + self.assertEqual(line, result) + def fix_e266(source): with autopep8_context(source, options=['--select=E266']) as result: From e9f42c054b3dfbc22632156c2a499acdcade4ad8 Mon Sep 17 00:00:00 2001 From: Hideo Hattori Date: Tue, 28 May 2024 23:12:01 +0900 Subject: [PATCH 04/19] fix typo --- test/test_autopep8.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/test_autopep8.py b/test/test_autopep8.py index ea96caa1..ad270a81 100755 --- a/test/test_autopep8.py +++ b/test/test_autopep8.py @@ -3948,7 +3948,7 @@ def test_e701_with_escaped_newline(self): with autopep8_context(line) as result: self.assertEqual(fixed, result) - @unittest.skipIf(sys.version_info >= (3, 12), 'not detech in Python3.12+') + @unittest.skipIf(sys.version_info >= (3, 12), 'not detect in Python3.12+') def test_e701_with_escaped_newline_and_spaces(self): line = 'if True: \\ \nprint(True)\n' fixed = 'if True:\n print(True)\n' @@ -4446,7 +4446,7 @@ def test_e731_with_default_arguments(self): with autopep8_context(line, options=['--select=E731']) as result: self.assertEqual(fixed, result) - @unittest.skipIf(sys.version_info >= (3, 12), 'not detech in Python3.12+') + @unittest.skipIf(sys.version_info >= (3, 12), 'not detect in Python3.12+') def test_e901_should_cause_indentation_screw_up(self): line = """\ def tmp(g): @@ -5495,7 +5495,7 @@ def test_help(self): stdout=PIPE) self.assertIn('usage:', p.communicate()[0].decode('utf-8').lower()) - @unittest.skipIf(sys.version_info >= (3, 12), 'not detech in Python3.12+') + @unittest.skipIf(sys.version_info >= (3, 12), 'not detect in Python3.12+') def test_verbose(self): line = 'bad_syntax)' with temporary_file_context(line) as filename: @@ -7236,7 +7236,7 @@ def f(self): with autopep8_context(line, options=['--experimental']) as result: self.assertEqual(fixed, result) - @unittest.skipIf(sys.version_info >= (3, 12), 'not detech in Python3.12+') + @unittest.skipIf(sys.version_info >= (3, 12), 'not detect in Python3.12+') def test_e501_experimental_parsing_dict_with_comments(self): line = """\ self.display['xxxxxxxxxxxx'] = [{'title': _('Library'), #. This is the first comment. @@ -7260,7 +7260,7 @@ def test_e501_experimental_parsing_dict_with_comments(self): with autopep8_context(line, options=['--experimental']) as result: self.assertEqual(fixed, result) - @unittest.skipIf(sys.version_info >= (3, 12), 'not detech in Python3.12+') + @unittest.skipIf(sys.version_info >= (3, 12), 'not detect in Python3.12+') def test_e501_experimental_if_line_over_limit(self): line = """\ if not xxxxxxxxxxxx(aaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbb, cccccccccccccc, dddddddddddddddddddddd): From e14f33d71532f9856fd14de59ff7383f17506e2f Mon Sep 17 00:00:00 2001 From: Hideo Hattori Date: Tue, 28 May 2024 23:35:08 +0900 Subject: [PATCH 05/19] fstring start/end is support Python3.12+ --- autopep8.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/autopep8.py b/autopep8.py index e311001e..8ddf8bba 100755 --- a/autopep8.py +++ b/autopep8.py @@ -154,6 +154,10 @@ class documentation for more information. MAX_PYTHON_FILE_DETECTION_BYTES = 1024 +IS_SUPPORT_TOKEN_FSTRING = False +if sys.version_info >= (3, 12): + IS_SUPPORT_TOKEN_FSTRING = True + def open_with_encoding(filename, mode='r', encoding=None, limit_byte_check=-1): """Return opened file with a specific encoding.""" @@ -2408,10 +2412,14 @@ def is_string(self): @property def is_fstring_start(self): + if not IS_SUPPORT_TOKEN_FSTRING: + return False return self._atom.token_type == tokenize.FSTRING_START @property def is_fstring_end(self): + if not IS_SUPPORT_TOKEN_FSTRING: + return False return self._atom.token_type == tokenize.FSTRING_END @property From faea0992c12bb24587c980a11e5140ad723882ba Mon Sep 17 00:00:00 2001 From: Hideo Hattori Date: Tue, 28 May 2024 23:35:44 +0900 Subject: [PATCH 06/19] fix test --- test/test_autopep8.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_autopep8.py b/test/test_autopep8.py index ad270a81..3d591d52 100755 --- a/test/test_autopep8.py +++ b/test/test_autopep8.py @@ -7333,6 +7333,7 @@ def test_e501_experimental_with_in(self): with autopep8_context(line, options=['--experimental']) as result: self.assertEqual(fixed, result) + @unittest.skipIf(sys.version_info < (3, 12), 'not support in Python3.11 and lower version') def test_e501_experimental_not_effect_with_fstring(self): line = """\ fstring = {"some_key": f"There is a string value inside of an f string, which itself is a dictionary value {s})"} From 6f5f04d56c6655111677d03363d0353c71cdaeec Mon Sep 17 00:00:00 2001 From: Hideo Hattori Date: Tue, 28 May 2024 23:43:00 +0900 Subject: [PATCH 07/19] no cover --- autopep8.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autopep8.py b/autopep8.py index 8ddf8bba..3875690a 100755 --- a/autopep8.py +++ b/autopep8.py @@ -155,7 +155,7 @@ class documentation for more information. MAX_PYTHON_FILE_DETECTION_BYTES = 1024 IS_SUPPORT_TOKEN_FSTRING = False -if sys.version_info >= (3, 12): +if sys.version_info >= (3, 12): # pgrama: no cover IS_SUPPORT_TOKEN_FSTRING = True From 719923b7b5a31e56a5474ecf587bfe090c249610 Mon Sep 17 00:00:00 2001 From: Hideo Hattori Date: Tue, 28 May 2024 23:57:45 +0900 Subject: [PATCH 08/19] version 2.1.2 --- autopep8.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autopep8.py b/autopep8.py index 3875690a..e3666bc9 100755 --- a/autopep8.py +++ b/autopep8.py @@ -89,7 +89,7 @@ class documentation for more information. import pycodestyle -__version__ = '2.1.1' +__version__ = '2.1.2' CR = '\r' From 9bdde91e36e18683dd654cd70d607f2e33ffc6aa Mon Sep 17 00:00:00 2001 From: Hideo Hattori Date: Wed, 29 May 2024 00:03:41 +0900 Subject: [PATCH 09/19] fix: upload pacakge to pypi --- .github/workflows/python-publish.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 01b1c8f5..8683d0b2 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -21,10 +21,11 @@ jobs: run: | python -m pip install --upgrade pip build pip install setuptools wheel twine - - name: Build and publish + - name: Build env: TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} run: | python -m build - python -m twine upload dist/* + - name: Publish distribution 📦 to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 From e238c83c6fcee0b01204b52a385e850fdd38a752 Mon Sep 17 00:00:00 2001 From: Hideo Hattori Date: Wed, 29 May 2024 07:40:07 +0900 Subject: [PATCH 10/19] use pypa/gh-action-pypi-publish --- .github/workflows/python-publish.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 8683d0b2..9df78a1f 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -10,7 +10,11 @@ on: jobs: deploy: runs-on: ubuntu-latest - + environment: + name: ${{ secrets.PYPI_USERNAME }} + url: https://pypi.org/p/autopep8 + permissions: + id-token: write steps: - uses: actions/checkout@v4 - name: Set up Python @@ -22,9 +26,6 @@ jobs: python -m pip install --upgrade pip build pip install setuptools wheel twine - name: Build - env: - TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} - TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} run: | python -m build - name: Publish distribution 📦 to PyPI From f0289518ac3c676db2ae1cd4632fa86fad449728 Mon Sep 17 00:00:00 2001 From: Hideo Hattori Date: Wed, 29 May 2024 07:42:03 +0900 Subject: [PATCH 11/19] pypi --- .github/workflows/python-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 9df78a1f..06ba095a 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -11,7 +11,7 @@ jobs: deploy: runs-on: ubuntu-latest environment: - name: ${{ secrets.PYPI_USERNAME }} + name: pypi url: https://pypi.org/p/autopep8 permissions: id-token: write From 75557c061f22d52f896f8a1c732de658c41757de Mon Sep 17 00:00:00 2001 From: Hideo Hattori Date: Wed, 29 May 2024 08:28:53 +0900 Subject: [PATCH 12/19] add test for specific case of e271 and w504 --- test/test_autopep8.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/test_autopep8.py b/test/test_autopep8.py index 3d591d52..bf73fa08 100755 --- a/test/test_autopep8.py +++ b/test/test_autopep8.py @@ -2256,6 +2256,18 @@ def test_e271_with_multiline(self): with autopep8_context(line) as result: self.assertEqual(fixed, result) + def test_e271_and_w504_with_affects_another_result_line(self): + line = """\ +cm_opts = ([1] + + [d for d in [3,4]]) +""" + fixed = """\ +cm_opts = ([1] + + [d for d in [3,4]]) +""" + with autopep8_context(line, options=["--select=E271,W504"]) as result: + self.assertEqual(fixed, result) + def test_e272(self): line = 'True and False\n' fixed = 'True and False\n' From 91b15c58b25c8afa2b048bffdcc54f6392f96522 Mon Sep 17 00:00:00 2001 From: Hideo Hattori Date: Wed, 29 May 2024 08:32:45 +0900 Subject: [PATCH 13/19] fix: skip if fixed-method result to another result --- autopep8.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/autopep8.py b/autopep8.py index e3666bc9..da86c58e 100755 --- a/autopep8.py +++ b/autopep8.py @@ -477,6 +477,7 @@ def __init__(self, filename, self.source = sio.readlines() self.options = options self.indent_word = _get_indentword(''.join(self.source)) + self.original_source = copy.copy(self.source) # collect imports line self.imports = {} @@ -540,6 +541,14 @@ def _fix_source(self, results): if result['line'] in completed_lines: continue + # NOTE: Skip if the correction by the fixed-method affects + # the number of lines of another remark. + _line_index = result['line'] - 1 + _target = self.source[_line_index] + _original_target = self.original_source[_line_index] + if _target != _original_target: + continue + fixed_methodname = 'fix_' + result['id'].lower() if hasattr(self, fixed_methodname): fix = getattr(self, fixed_methodname) From 765402a0289dd4f3f8a7b56c9e36e33f77fe656b Mon Sep 17 00:00:00 2001 From: Hideo Hattori Date: Wed, 29 May 2024 08:41:21 +0900 Subject: [PATCH 14/19] fix: check just before fixed-method --- autopep8.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/autopep8.py b/autopep8.py index da86c58e..437c92f7 100755 --- a/autopep8.py +++ b/autopep8.py @@ -529,6 +529,13 @@ def __init__(self, filename, self.fix_w292 = self.fix_w291 self.fix_w293 = self.fix_w291 + def _check_affected_anothers(self, result) -> bool: + """Check if the fix affects the number of lines of another remark.""" + line_index = result['line'] - 1 + target = self.source[line_index] + original_target = self.original_source[line_index] + return target != original_target + def _fix_source(self, results): try: (logical_start, logical_end) = _find_logical(self.source) @@ -541,14 +548,6 @@ def _fix_source(self, results): if result['line'] in completed_lines: continue - # NOTE: Skip if the correction by the fixed-method affects - # the number of lines of another remark. - _line_index = result['line'] - 1 - _target = self.source[_line_index] - _original_target = self.original_source[_line_index] - if _target != _original_target: - continue - fixed_methodname = 'fix_' + result['id'].lower() if hasattr(self, fixed_methodname): fix = getattr(self, fixed_methodname) @@ -570,8 +569,12 @@ def _fix_source(self, results): completed_lines): continue + if self._check_affected_anothers(result): + continue modified_lines = fix(result, logical) else: + if self._check_affected_anothers(result): + continue modified_lines = fix(result) if modified_lines is None: From 53a993ec09142414a8798b197781e11a2f97bbfb Mon Sep 17 00:00:00 2001 From: Hideo Hattori Date: Wed, 29 May 2024 09:32:16 +0900 Subject: [PATCH 15/19] experimental option is deprecated --- autopep8.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/autopep8.py b/autopep8.py index 437c92f7..31be3a48 100755 --- a/autopep8.py +++ b/autopep8.py @@ -3965,6 +3965,9 @@ def parse_args(arguments, apply_config=False): 'to the second', ) + if args.experimental: + warnings.warn("`experimental` option is deprecated and will be removed in a future version.", DeprecationWarning) + return args From f516b2971af28c5ffa218694d94d9af318c79666 Mon Sep 17 00:00:00 2001 From: Hideo Hattori Date: Wed, 29 May 2024 09:51:37 +0900 Subject: [PATCH 16/19] use custom warning format --- autopep8.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/autopep8.py b/autopep8.py index 31be3a48..78d2e7dc 100755 --- a/autopep8.py +++ b/autopep8.py @@ -159,6 +159,10 @@ class documentation for more information. IS_SUPPORT_TOKEN_FSTRING = True +def _custom_formatwarning(message, category, _, __, line=None): + return f"{category.__name__}: {message}\n" + + def open_with_encoding(filename, mode='r', encoding=None, limit_byte_check=-1): """Return opened file with a specific encoding.""" if not encoding: @@ -3965,8 +3969,11 @@ def parse_args(arguments, apply_config=False): 'to the second', ) + original_formatwarning = warnings.formatwarning + warnings.formatwarning = _custom_formatwarning if args.experimental: warnings.warn("`experimental` option is deprecated and will be removed in a future version.", DeprecationWarning) + warnings.formatwarning = original_formatwarning return args From 725337599eb3c977aa21f4dc05dc5ae416ac4d1d Mon Sep 17 00:00:00 2001 From: Hideo Hattori Date: Wed, 29 May 2024 09:52:09 +0900 Subject: [PATCH 17/19] strict lint --- autopep8.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/autopep8.py b/autopep8.py index 78d2e7dc..bcd36e15 100755 --- a/autopep8.py +++ b/autopep8.py @@ -3972,7 +3972,11 @@ def parse_args(arguments, apply_config=False): original_formatwarning = warnings.formatwarning warnings.formatwarning = _custom_formatwarning if args.experimental: - warnings.warn("`experimental` option is deprecated and will be removed in a future version.", DeprecationWarning) + warnings.warn( + "`experimental` option is deprecated and will be " + "removed in a future version.", + DeprecationWarning, + ) warnings.formatwarning = original_formatwarning return args From b4c586d7ecabddb40a35fdcdd7892bd045660ab0 Mon Sep 17 00:00:00 2001 From: Hideo Hattori Date: Thu, 30 May 2024 00:13:21 +0900 Subject: [PATCH 18/19] skip e501 fixed method for f-string line(s) --- autopep8.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/autopep8.py b/autopep8.py index bcd36e15..fb25adf0 100755 --- a/autopep8.py +++ b/autopep8.py @@ -3376,13 +3376,20 @@ def multiline_string_lines(source, include_docstrings=False): """ line_numbers = set() previous_token_type = '' + _check_target_tokens = [tokenize.STRING] + if IS_SUPPORT_TOKEN_FSTRING: + _check_target_tokens.extend([ + tokenize.FSTRING_START, + tokenize.FSTRING_MIDDLE, + tokenize.FSTRING_END, + ]) try: for t in generate_tokens(source): token_type = t[0] start_row = t[2][0] end_row = t[3][0] - if token_type == tokenize.STRING and start_row != end_row: + if token_type in _check_target_tokens and start_row != end_row: if ( include_docstrings or previous_token_type != tokenize.INDENT From d5bbdbc53d161200282959d0ffbfbf2f66da2921 Mon Sep 17 00:00:00 2001 From: Hideo Hattori Date: Thu, 30 May 2024 22:08:39 +0900 Subject: [PATCH 19/19] version 2.2.0 --- autopep8.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autopep8.py b/autopep8.py index fb25adf0..fd59890c 100755 --- a/autopep8.py +++ b/autopep8.py @@ -89,7 +89,7 @@ class documentation for more information. import pycodestyle -__version__ = '2.1.2' +__version__ = '2.2.0' CR = '\r'