Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-pycodestyle for
openSUSE:Factory checked in at 2026-10-27 18:17:59
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pycodestyle (Old)
and /work/SRC/openSUSE:Factory/.python-pycodestyle.new.2252680 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pycodestyle"
Tue Oct 27 18:17:59 2026 rev:27 rq:1392538 version:2.15.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pycodestyle/python-pycodestyle.changes
2025-08-22 17:47:41.675715603 +0200
+++
/work/SRC/openSUSE:Factory/.python-pycodestyle.new.2252680/python-pycodestyle.changes
2026-10-27 18:18:05.158404383 +0100
@@ -1,0 +2,12 @@
+Sun Oct 25 19:55:08 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 2.15.0:
+ * Improve performance of `module_imports_on_top_of_file`. PR
+ * Improve performance of `python_3000_invalid_escape_sequence`.
+ * Improve performacne of `missing_whitespace_after_keyword`.
+ * Drop EOL python 3.9. PR #1300.
+ * Add support for python 3.15. PR #1322.
+ * Fix loading unrelated configuration due to `commonprefix`.
+ PR #1323.
+
+-------------------------------------------------------------------
Old:
----
pycodestyle-2.14.0.tar.gz
New:
----
pycodestyle-2.15.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-pycodestyle.spec ++++++
--- /var/tmp/diff_new_pack.c4HgXQ/_old 2026-10-27 18:18:06.060442004 +0100
+++ /var/tmp/diff_new_pack.c4HgXQ/_new 2026-10-27 18:18:06.062442087 +0100
@@ -1,7 +1,7 @@
#
# spec file for package python-pycodestyle
#
-# Copyright (c) 2025 SUSE LLC and contributors
+# Copyright (c) 2026 SUSE LLC and contributors
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -23,7 +23,7 @@
%endif
%{?sle15_python_module_pythons}
Name: python-pycodestyle
-Version: 2.14.0
+Version: 2.15.0
Release: 0
Summary: Python style guide checker
License: MIT
++++++ pycodestyle-2.14.0.tar.gz -> pycodestyle-2.15.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pycodestyle-2.14.0/.github/workflows/main.yml
new/pycodestyle-2.15.0/.github/workflows/main.yml
--- old/pycodestyle-2.14.0/.github/workflows/main.yml 2025-06-20
20:48:57.000000000 +0200
+++ new/pycodestyle-2.15.0/.github/workflows/main.yml 2026-09-23
00:14:26.000000000 +0200
@@ -11,15 +11,12 @@
matrix:
include:
- os: windows-latest
- py: 3.9
+ py: '3.10'
toxenv: py
- os: ubuntu-latest
py: pypy3.10
toxenv: py
- os: ubuntu-latest
- py: 3.9
- toxenv: py
- - os: ubuntu-latest
py: '3.10'
toxenv: py
- os: ubuntu-latest
@@ -32,10 +29,13 @@
py: '3.13'
toxenv: py
- os: ubuntu-latest
- py: '3.14-dev'
+ py: '3.14'
toxenv: py
- os: ubuntu-latest
- py: 3.9
+ py: '3.15-dev'
+ toxenv: py
+ - os: ubuntu-latest
+ py: '3.10'
toxenv: flake8
runs-on: ${{ matrix.os }}
steps:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pycodestyle-2.14.0/.pre-commit-config.yaml
new/pycodestyle-2.15.0/.pre-commit-config.yaml
--- old/pycodestyle-2.14.0/.pre-commit-config.yaml 2025-06-20
20:48:57.000000000 +0200
+++ new/pycodestyle-2.15.0/.pre-commit-config.yaml 2026-09-23
00:14:26.000000000 +0200
@@ -1,27 +1,27 @@
exclude: ^testing/data/
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v5.0.0
+ rev: v6.0.0
hooks:
- id: check-yaml
- id: debug-statements
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/asottile/reorder-python-imports
- rev: v3.15.0
+ rev: v3.17.0
hooks:
- id: reorder-python-imports
- args: [--py39-plus]
+ args: [--py310-plus]
- repo: https://github.com/asottile/pyupgrade
- rev: v3.20.0
+ rev: v3.21.2
hooks:
- id: pyupgrade
- args: [--py39-plus]
+ args: [--py310-plus]
- repo: https://github.com/asottile/setup-cfg-fmt
- rev: v2.8.0
+ rev: v3.2.0
hooks:
- id: setup-cfg-fmt
- repo: https://github.com/pycqa/flake8
- rev: 7.2.0
+ rev: 7.3.0
hooks:
- id: flake8
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pycodestyle-2.14.0/CHANGES.txt
new/pycodestyle-2.15.0/CHANGES.txt
--- old/pycodestyle-2.14.0/CHANGES.txt 2025-06-20 20:48:57.000000000 +0200
+++ new/pycodestyle-2.15.0/CHANGES.txt 2026-09-23 00:14:26.000000000 +0200
@@ -1,6 +1,18 @@
Changelog
=========
+2.15.0 (2026-09-22)
+-------------------
+
+Changes:
+
+* Improve performance of `module_imports_on_top_of_file`. PR #1292.
+* Improve performance of `python_3000_invalid_escape_sequence`. PR #1292.
+* Improve performacne of `missing_whitespace_after_keyword`. PR #1291.
+* Drop EOL python 3.9. PR #1300.
+* Add support for python 3.15. PR #1322.
+* Fix loading unrelated configuration due to `commonprefix`. PR #1323.
+
2.14.0 (2025-06-20)
-------------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pycodestyle-2.14.0/pycodestyle.py
new/pycodestyle-2.15.0/pycodestyle.py
--- old/pycodestyle-2.14.0/pycodestyle.py 2025-06-20 20:48:57.000000000
+0200
+++ new/pycodestyle-2.15.0/pycodestyle.py 2026-09-23 00:14:26.000000000
+0200
@@ -59,16 +59,10 @@
import warnings
from fnmatch import fnmatch
from functools import lru_cache
+from itertools import pairwise
from optparse import OptionParser
-# this is a performance hack. see https://bugs.python.org/issue43014
-if (
- sys.version_info < (3, 10) and
- callable(getattr(tokenize, '_compile', None))
-): # pragma: no cover (<py310)
- tokenize._compile = lru_cache(tokenize._compile) # type: ignore
-
-__version__ = '2.14.0'
+__version__ = '2.15.0'
DEFAULT_EXCLUDE = '.svn,CVS,.bzr,.hg,.git,__pycache__,.tox'
DEFAULT_IGNORE = 'E121,E123,E126,E226,E24,E704,W503,W504'
@@ -502,7 +496,7 @@
E275: from importable.module import(bar, baz)
E275: if(foo): bar
"""
- for tok0, tok1 in zip(tokens, tokens[1:]):
+ for tok0, tok1 in pairwise(tokens):
# This must exclude the True/False/None singletons, which can
# appear e.g. as "if x is None:", and async/await, which were
# valid identifier names in old Python versions.
@@ -512,7 +506,7 @@
tok0.string not in SINGLETONS and
not (tok0.string == 'except' and tok1.string == '*') and
not (tok0.string == 'yield' and tok1.string == ')') and
- tok1.string not in ':\n'):
+ (tok1.string and tok1.string != ':' and tok1.string != '\n')):
yield tok0.end, "E275 missing whitespace after keyword"
@@ -1160,6 +1154,22 @@
yield found, "E401 multiple imports on one line"
+_STRING_PREFIXES = frozenset(('u', 'U', 'b', 'B', 'r', 'R'))
+
+
+def _is_string_literal(line):
+ if line:
+ first_char = line[0]
+ if first_char in _STRING_PREFIXES:
+ first_char = line[1]
+ return first_char == '"' or first_char == "'"
+ return False
+
+
+_ALLOWED_KEYWORDS_IN_IMPORTS = (
+ 'try', 'except', 'else', 'finally', 'with', 'if', 'elif')
+
+
@register_check
def module_imports_on_top_of_file(
logical_line, indent_level, checker_state, noqa):
@@ -1178,15 +1188,6 @@
Okay: if x:\n import os
""" # noqa
- def is_string_literal(line):
- if line[0] in 'uUbB':
- line = line[1:]
- if line and line[0] in 'rR':
- line = line[1:]
- return line and (line[0] == '"' or line[0] == "'")
-
- allowed_keywords = (
- 'try', 'except', 'else', 'finally', 'with', 'if', 'elif')
if indent_level: # Allow imports in conditional statement/function
return
@@ -1194,25 +1195,28 @@
return
if noqa:
return
- line = logical_line
- if line.startswith('import ') or line.startswith('from '):
+ if logical_line.startswith((
+ 'import ', 'from ',
+ 'lazy import ', 'lazy from ',
+ )):
if checker_state.get('seen_non_imports', False):
yield 0, "E402 module level import not at top of file"
- elif re.match(DUNDER_REGEX, line):
- return
- elif any(line.startswith(kw) for kw in allowed_keywords):
- # Allow certain keywords intermixed with imports in order to
- # support conditional or filtered importing
- return
- elif is_string_literal(line):
- # The first literal is a docstring, allow it. Otherwise, report
- # error.
- if checker_state.get('seen_docstring', False):
- checker_state['seen_non_imports'] = True
+ elif not checker_state.get('seen_non_imports', False):
+ if DUNDER_REGEX.match(logical_line):
+ return
+ elif logical_line.startswith(_ALLOWED_KEYWORDS_IN_IMPORTS):
+ # Allow certain keywords intermixed with imports in order to
+ # support conditional or filtered importing
+ return
+ elif _is_string_literal(logical_line):
+ # The first literal is a docstring, allow it. Otherwise,
+ # report error.
+ if checker_state.get('seen_docstring', False):
+ checker_state['seen_non_imports'] = True
+ else:
+ checker_state['seen_docstring'] = True
else:
- checker_state['seen_docstring'] = True
- else:
- checker_state['seen_non_imports'] = True
+ checker_state['seen_non_imports'] = True
@register_check
@@ -1627,6 +1631,29 @@
prev_start = start
+#
https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals
+_PYTHON_3000_VALID_ESC = frozenset([
+ '\n',
+ '\\',
+ '\'',
+ '"',
+ 'a',
+ 'b',
+ 'f',
+ 'n',
+ 'r',
+ 't',
+ 'v',
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ 'x',
+
+ # Escape sequences only recognized in string literals
+ 'N',
+ 'u',
+ 'U',
+])
+
+
@register_check
def python_3000_invalid_escape_sequence(logical_line, tokens, noqa):
r"""Invalid escape sequences are deprecated in Python 3.6.
@@ -1637,41 +1664,27 @@
if noqa:
return
- #
https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals
- valid = [
- '\n',
- '\\',
- '\'',
- '"',
- 'a',
- 'b',
- 'f',
- 'n',
- 'r',
- 't',
- 'v',
- '0', '1', '2', '3', '4', '5', '6', '7',
- 'x',
-
- # Escape sequences only recognized in string literals
- 'N',
- 'u',
- 'U',
- ]
-
prefixes = []
for token_type, text, start, _, _ in tokens:
- if token_type in {tokenize.STRING, FSTRING_START, TSTRING_START}:
+ if (
+ token_type == tokenize.STRING or
+ token_type == FSTRING_START or
+ token_type == TSTRING_START
+ ):
# Extract string modifiers (e.g. u or r)
prefixes.append(text[:text.index(text[-1])].lower())
- if token_type in {tokenize.STRING, FSTRING_MIDDLE, TSTRING_MIDDLE}:
+ if (
+ token_type == tokenize.STRING or
+ token_type == FSTRING_MIDDLE or
+ token_type == TSTRING_MIDDLE
+ ):
if 'r' not in prefixes[-1]:
start_line, start_col = start
pos = text.find('\\')
while pos >= 0:
pos += 1
- if text[pos] not in valid:
+ if text[pos] not in _PYTHON_3000_VALID_ESC:
line = start_line + text.count('\n', 0, pos)
if line == start_line:
col = start_col + pos
@@ -1683,7 +1696,11 @@
)
pos = text.find('\\', pos + 1)
- if token_type in {tokenize.STRING, FSTRING_END, TSTRING_END}:
+ if (
+ token_type == tokenize.STRING or
+ token_type == FSTRING_END or
+ token_type == TSTRING_END
+ ):
prefixes.pop()
@@ -2549,7 +2566,7 @@
print('user configuration: %s' % USER_CONFIG)
config.read(USER_CONFIG)
- parent = tail = args and os.path.abspath(os.path.commonprefix(args))
+ parent = tail = args and os.path.abspath(os.path.commonpath(args))
while tail:
if config.read(os.path.join(parent, fn) for fn in PROJECT_CONFIG):
local_dir = parent
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pycodestyle-2.14.0/setup.cfg
new/pycodestyle-2.15.0/setup.cfg
--- old/pycodestyle-2.14.0/setup.cfg 2025-06-20 20:48:57.000000000 +0200
+++ new/pycodestyle-2.15.0/setup.cfg 2026-09-23 00:14:26.000000000 +0200
@@ -28,7 +28,7 @@
[options]
py_modules = pycodestyle
-python_requires = >=3.9
+python_requires = >=3.10
include_package_data = True
zip_safe = False
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pycodestyle-2.14.0/testing/data/python315.py
new/pycodestyle-2.15.0/testing/data/python315.py
--- old/pycodestyle-2.14.0/testing/data/python315.py 1970-01-01
01:00:00.000000000 +0100
+++ new/pycodestyle-2.15.0/testing/data/python315.py 2026-09-23
00:14:26.000000000 +0200
@@ -0,0 +1,9 @@
+import os
+lazy from typing import LiteralString
+import sys
+lazy import asyncio
+
+[*y for y in x]
+{*y for y in x}
+(*y for y in x)
+{**y for y in x}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pycodestyle-2.14.0/tests/test_api.py
new/pycodestyle-2.15.0/tests/test_api.py
--- old/pycodestyle-2.14.0/tests/test_api.py 2025-06-20 20:48:57.000000000
+0200
+++ new/pycodestyle-2.15.0/tests/test_api.py 2026-09-23 00:14:26.000000000
+0200
@@ -352,12 +352,7 @@
pep8style.input_file('stdin', lines=lines)
stdout = sys.stdout.getvalue()
- if sys.version_info < (3, 10): # pragma: <3.10 cover
- expected = [
- 'stdin:2:5: E901 TokenError: EOF in multi-line string',
- 'stdin:2:26: E901 SyntaxError: EOF while scanning
triple-quoted string literal', # noqa: E501
- ]
- elif sys.version_info < (3, 12): # pragma: >=3.10 cover # pragma:
<3.12 cover # noqa: E501
+ if sys.version_info < (3, 12): # pragma: <3.12 cover # noqa: E501
expected = [
'stdin:2:5: E901 TokenError: EOF in multi-line string',
'stdin:2:6: E901 SyntaxError: unterminated triple-quoted
string literal (detected at line 2)', # noqa: E501
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pycodestyle-2.14.0/tests/test_pycodestyle.py
new/pycodestyle-2.15.0/tests/test_pycodestyle.py
--- old/pycodestyle-2.14.0/tests/test_pycodestyle.py 2025-06-20
20:48:57.000000000 +0200
+++ new/pycodestyle-2.15.0/tests/test_pycodestyle.py 2026-09-23
00:14:26.000000000 +0200
@@ -6,7 +6,9 @@
from pycodestyle import Checker
from pycodestyle import expand_indent
+from pycodestyle import get_parser
from pycodestyle import mute_string
+from pycodestyle import read_config
@pytest.mark.parametrize(
@@ -42,7 +44,36 @@
checker.tokens = list(tokenize.generate_tokens(io.StringIO(src).readline))
checker.build_tokens_line()
- if sys.version_info >= (3, 12): # pragma: >3.12 cover
+ if sys.version_info >= (3, 12): # pragma: >=3.12 cover
assert checker.logical_line == "f'xxxxxxxxx{thing}xxxxxxxxx'"
- else:
+ else: # pragma: <3.12 cover
assert checker.logical_line == "f'xxxxxxxxxxxxxxxxxxxxxxxxx'"
+
+
+def test_read_config_prefix(tmp_path):
+ tmp_path.joinpath('setup.cfg').write_text('[pycodestyle]\nexclude = root')
+
+ adir = tmp_path.joinpath('aaa')
+ adir.mkdir()
+ adir.joinpath('setup.cfg').write_text('[pycodestyle]\nexclude = aaa')
+
+ bdir = tmp_path.joinpath('aaabbb')
+ bdir.mkdir()
+ b_t = bdir.joinpath('t.py')
+ b_t.touch()
+ bdir.joinpath('setup.cfg').write_text('[pycodestyle]\nexclude = bbb')
+
+ cdir = tmp_path.joinpath('aaaccc')
+ cdir.mkdir()
+ c_t = cdir.joinpath('t.py')
+ c_t.touch()
+ cdir.joinpath('setup.cfg').write_text('[pycodestyle]\nexclude = ccc')
+
+ arglist = [str(b_t), str(c_t)]
+ parser = get_parser()
+ parser.add_option('--config')
+ opts, args = parser.parse_args(arglist)
+
+ # `aaa/setup.cfg` should not be read -- it is not passed on cmdline
+ opts = read_config(opts, args, arglist, parser)
+ assert opts.exclude == ['root']