Package: debmutate
Version: 0.71
Severity: important
Tags: python3.13

The python3-debmutate package fails to handle UTF-8 encoded files when
running under Python 3.13. This affects multiple Debian packaging
tools including lintian-brush, making it impossible to process
packages containing non-ASCII characters.

When running lintian-brush, the following error occurs:

UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position
705: ordinal not in range(128)

This error occurs in debmutate/reformatting.py, line 223:
self._orig_content = f.read()

The error appears when processing both debian/copyright and
debian/changelog files that contain UTF-8 characters. Setting
PYTHONIOENCODING=utf8 does not resolve the issue.

Steps to reproduce:

1. Have a package with non-ASCII characters in debian/copyright or
debian/changelog
2. Run: lintian-brush --no-update-changelog --modern --uncertain
3. Observe the UnicodeDecodeError

The error occurs with multiple lintian-brush fixers:
- global-files-wildcard-not-first-paragraph-in-dep5-copyright
- new-package-uses-date-based-version-number

This appears to be a regression in Python 3.13 handling, as these
files are valid UTF-8 and can be processed correctly by iconv and
other tools.


Full log of lintian-brush on package MariaDB in Debian unstable for
further reference below.

***************
# dpkg -l | grep -E 'debmutate|lintian'
ii  lintian                             2.121.1
 all          Debian package checker
ii  lintian-brush                       0.160+b2
 amd64        automatically fix lintian problems
ii  python3-debmutate                   0.71
 all          Format-preserving manipulation of Debian control files
in Python

# file -i debian/copyright debian/changelog
debian/copyright: text/plain; charset=utf-8
debian/changelog: text/plain; charset=utf-8

# PYTHONIOENCODING=utf8 lintian-brush --no-update-changelog --modern --uncertain
[    ] 154/154No changes made.
Some fixer scripts failed to run:
  copyright-missing-upstream-info: Script failed:
/usr/share/lintian-brush/fixers/copyright-missing-upstream-info.py
(exit code 1) (stderr:
Traceback (most recent call last):
  File "/usr/share/lintian-brush/fixers/copyright-missing-upstream-info.py",
line 80, in <module>
    ), CopyrightEditor() as updater:
       ~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3/dist-packages/debmutate/reformatting.py",
line 223, in __enter__
    self._orig_content = f.read()
                         ~~~~~~^^
  File "/usr/lib/python3.13/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position
5806: ordinal not in range(128)
)
  debian-changelog-line-too-long: Script failed:
/usr/share/lintian-brush/fixers/debian-changelog-line-too-long.py
(exit code 1) (stderr:
Traceback (most recent call last):
  File "/usr/share/lintian-brush/fixers/debian-changelog-line-too-long.py",
line 42, in <module>
    with ChangelogEditor() as updater:
         ~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3/dist-packages/debmutate/reformatting.py",
line 223, in __enter__
    self._orig_content = f.read()
                         ~~~~~~^^
  File "/usr/lib/python3.13/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position
705: ordinal not in range(128)
)
  obsolete-field-in-dep5-copyright: Script failed:
/usr/share/lintian-brush/fixers/obsolete-field-in-dep5-copyright.py
(exit code 1) (stderr:
Traceback (most recent call last):
  File "/usr/share/lintian-brush/fixers/obsolete-field-in-dep5-copyright.py",
line 47, in <module>
    ), CopyrightEditor() as updater:
       ~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3/dist-packages/debmutate/reformatting.py",
line 223, in __enter__
    self._orig_content = f.read()
                         ~~~~~~^^
  File "/usr/lib/python3.13/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position
5806: ordinal not in range(128)
)
  upstream-metadata-file: Timeout after 10s
  initial-upload-closes-no-bugs: Script failed:
/usr/share/lintian-brush/fixers/initial-upload-closes-no-bugs.py (exit
code 1) (stderr:
Traceback (most recent call last):
  File "/usr/share/lintian-brush/fixers/initial-upload-closes-no-bugs.py",
line 21, in <module>
    with ChangelogEditor() as editor:
         ~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3/dist-packages/debmutate/reformatting.py",
line 223, in __enter__
    self._orig_content = f.read()
                         ~~~~~~^^
  File "/usr/lib/python3.13/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position
705: ordinal not in range(128)
)
  ancient-maintscript-entry: Script failed:
/usr/share/lintian-brush/fixers/ancient-maintscript-entry.py (exit
code 1) (stderr:
Traceback (most recent call last):
  File "/usr/share/lintian-brush/fixers/ancient-maintscript-entry.py",
line 52, in <module>
    with ChangelogEditor() as cl:
         ~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3/dist-packages/debmutate/reformatting.py",
line 223, in __enter__
    self._orig_content = f.read()
                         ~~~~~~^^
  File "/usr/lib/python3.13/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position
705: ordinal not in range(128)
)
  space-in-std-shortname-in-dep5-copyright: Script failed:
/usr/share/lintian-brush/fixers/space-in-std-shortname-in-dep5-copyright.py
(exit code 1) (stderr:
Traceback (most recent call last):
  File 
"/usr/share/lintian-brush/fixers/space-in-std-shortname-in-dep5-copyright.py",
line 68, in <module>
    ), CopyrightEditor() as updater:
       ~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3/dist-packages/debmutate/reformatting.py",
line 223, in __enter__
    self._orig_content = f.read()
                         ~~~~~~^^
  File "/usr/lib/python3.13/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position
5806: ordinal not in range(128)
)
  debian-changelog-has-wrong-day-of-week: Script failed:
/usr/share/lintian-brush/fixers/debian-changelog-has-wrong-day-of-week.py
(exit code 1) (stderr:
Traceback (most recent call last):
  File 
"/usr/share/lintian-brush/fixers/debian-changelog-has-wrong-day-of-week.py",
line 12, in <module>
    with ChangelogEditor() as updater:
         ~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3/dist-packages/debmutate/reformatting.py",
line 223, in __enter__
    self._orig_content = f.read()
                         ~~~~~~^^
  File "/usr/lib/python3.13/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position
705: ordinal not in range(128)
)
  invalid-short-name-in-dep5-copyright: Script failed:
/usr/share/lintian-brush/fixers/invalid-short-name-in-dep5-copyright.py
(exit code 1) (stderr:
Traceback (most recent call last):
  File 
"/usr/share/lintian-brush/fixers/invalid-short-name-in-dep5-copyright.py",
line 50, in <module>
    ), CopyrightEditor() as updater:
       ~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3/dist-packages/debmutate/reformatting.py",
line 223, in __enter__
    self._orig_content = f.read()
                         ~~~~~~^^
  File "/usr/lib/python3.13/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position
5806: ordinal not in range(128)
)
  dep5-file-paragraph-references-header-paragraph: Script failed:
/usr/share/lintian-brush/fixers/dep5-file-paragraph-references-header-paragraph.py
(exit code 1) (stderr:
Traceback (most recent call last):
  File 
"/usr/share/lintian-brush/fixers/dep5-file-paragraph-references-header-paragraph.py",
line 41, in <module>
    with CopyrightEditor() as updater:
         ~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3/dist-packages/debmutate/reformatting.py",
line 223, in __enter__
    self._orig_content = f.read()
                         ~~~~~~^^
  File "/usr/lib/python3.13/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position
5806: ordinal not in range(128)
)
  out-of-date-standards-version: Script failed:
/usr/share/lintian-brush/fixers/out-of-date-standards-version.py (exit
code 1) (stderr:
Traceback (most recent call last):
  File "/usr/share/lintian-brush/fixers/out-of-date-standards-version.py",
line 301, in <module>
    svs = dict(iter_standards_versions())
  File "/usr/lib/python3/dist-packages/lintian_brush/standards_version.py",
line 41, in iter_standards_versions
    data = json.load(f)
  File "/usr/lib/python3.13/json/__init__.py", line 293, in load
    return loads(fp.read(),
                 ~~~~~~~^^
  File "/usr/lib/python3.13/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position
4315: ordinal not in range(128)
)
  global-files-wildcard-not-first-paragraph-in-dep5-copyright: Script
failed: 
/usr/share/lintian-brush/fixers/global-files-wildcard-not-first-paragraph-in-dep5-copyright.py
(exit code 1) (stderr:
Traceback (most recent call last):
  File 
"/usr/share/lintian-brush/fixers/global-files-wildcard-not-first-paragraph-in-dep5-copyright.py",
line 11, in <module>
    ), CopyrightEditor() as editor:
       ~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3/dist-packages/debmutate/reformatting.py",
line 223, in __enter__
    self._orig_content = f.read()
                         ~~~~~~^^
  File "/usr/lib/python3.13/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position
5806: ordinal not in range(128)
)
  common-license: Script failed:
/usr/share/lintian-brush/fixers/common-license.py (exit code 1)
(stderr:
Traceback (most recent call last):
  File "/usr/share/lintian-brush/fixers/common-license.py", line 271,
in <module>
    ), CopyrightEditor() as updater:
       ~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3/dist-packages/debmutate/reformatting.py",
line 223, in __enter__
    self._orig_content = f.read()
                         ~~~~~~^^
  File "/usr/lib/python3.13/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position
5806: ordinal not in range(128)
)
  changelog-has-duplicate-line: Script failed:
/usr/share/lintian-brush/fixers/changelog-has-duplicate-line.py (exit
code 1) (stderr:
Traceback (most recent call last):
  File "/usr/share/lintian-brush/fixers/changelog-has-duplicate-line.py",
line 7, in <module>
    with ChangelogEditor() as updater:
         ~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3/dist-packages/debmutate/reformatting.py",
line 223, in __enter__
    self._orig_content = f.read()
                         ~~~~~~^^
  File "/usr/lib/python3.13/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position
705: ordinal not in range(128)
)
  possible-missing-colon-in-closes: Script failed:
/usr/share/lintian-brush/fixers/possible-missing-colon-in-closes.py
(exit code 1) (stderr:
Traceback (most recent call last):
  File "/usr/share/lintian-brush/fixers/possible-missing-colon-in-closes.py",
line 88, in <module>
    with ChangelogEditor() as updater:
         ~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3/dist-packages/debmutate/reformatting.py",
line 223, in __enter__
    self._orig_content = f.read()
                         ~~~~~~^^
  File "/usr/lib/python3.13/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position
705: ordinal not in range(128)
)
  unnecessary-team-upload: Script failed:
/usr/share/lintian-brush/fixers/unnecessary-team-upload.py (exit code
1) (stderr:
Traceback (most recent call last):
  File "/usr/share/lintian-brush/fixers/unnecessary-team-upload.py",
line 19, in <module>
    with ChangelogEditor() as updater:
         ~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3/dist-packages/debmutate/reformatting.py",
line 223, in __enter__
    self._orig_content = f.read()
                         ~~~~~~^^
  File "/usr/lib/python3.13/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position
705: ordinal not in range(128)
)
  unused-license-paragraph-in-dep5-copyright: Script failed:
/usr/share/lintian-brush/fixers/unused-license-paragraph-in-dep5-copyright.py
(exit code 1) (stderr:
Traceback (most recent call last):
  File 
"/usr/share/lintian-brush/fixers/unused-license-paragraph-in-dep5-copyright.py",
line 33, in <module>
    with CopyrightEditor() as updater:
         ~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3/dist-packages/debmutate/reformatting.py",
line 223, in __enter__
    self._orig_content = f.read()
                         ~~~~~~^^
  File "/usr/lib/python3.13/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position
5806: ordinal not in range(128)
)
  copyright-refers-to-symlink-license: Script failed:
/usr/share/lintian-brush/fixers/copyright-refers-to-symlink-license.py
(exit code 1) (stderr:
Traceback (most recent call last):
  File "/usr/share/lintian-brush/fixers/copyright-refers-to-symlink-license.py",
line 42, in <module>
    ), CopyrightEditor() as updater:
       ~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3/dist-packages/debmutate/reformatting.py",
line 223, in __enter__
    self._orig_content = f.read()
                         ~~~~~~^^
  File "/usr/lib/python3.13/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position
5806: ordinal not in range(128)
)
  debian-rules-uses-unnecessary-dh-argument: Script failed:
/usr/share/lintian-brush/fixers/debian-rules-uses-unnecessary-dh-argument.py
(exit code 1) (stderr:
Traceback (most recent call last):
  File 
"/usr/share/lintian-brush/fixers/debian-rules-uses-unnecessary-dh-argument.py",
line 20, in <module>
    compat_version = get_debhelper_compat_level()
  File "/usr/lib/python3/dist-packages/debmutate/debhelper.py", line
123, in get_debhelper_compat_level
    control = Deb822(f)
  File "/usr/lib/python3/dist-packages/debian/deb822.py", line 673, in __init__
    self._internal_parser(iterable, fields, strict)
    ~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/debian/deb822.py", line 817, in
_internal_parser
    for linebytes in self._gpg_stripped_paragraph(
                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
            self._skip_useless_lines(sequence), strict):
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/debian/deb822.py", line 1184,
in _gpg_stripped_paragraph
    return cls._split_gpg_and_payload(sequence, strict)[1]
           ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/debian/deb822.py", line 1133,
in _split_gpg_and_payload
    for line in sequence:
                ^^^^^^^^
  File "/usr/lib/python3/dist-packages/debian/deb822.py", line 773, in
_skip_useless_lines
    for line in sequence:
                ^^^^^^^^
  File "/usr/lib/python3.13/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position
152: ordinal not in range(128)
)
  license-file-listed-in-debian-copyright: Script failed:
/usr/share/lintian-brush/fixers/license-file-listed-in-debian-copyright.py
(exit code 1) (stderr:
Traceback (most recent call last):
  File 
"/usr/share/lintian-brush/fixers/license-file-listed-in-debian-copyright.py",
line 19, in <module>
    with CopyrightEditor() as updater:
         ~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3/dist-packages/debmutate/reformatting.py",
line 223, in __enter__
    self._orig_content = f.read()
                         ~~~~~~^^
  File "/usr/lib/python3.13/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position
5806: ordinal not in range(128)
)
  invalid-standards-version: Script failed:
/usr/share/lintian-brush/fixers/invalid-standards-version.py (exit
code 1) (stderr:
Traceback (most recent call last):
  File "/usr/share/lintian-brush/fixers/invalid-standards-version.py",
line 11, in <module>
    release_dates = dict(iter_standards_versions())
  File "/usr/lib/python3/dist-packages/lintian_brush/standards_version.py",
line 41, in iter_standards_versions
    data = json.load(f)
  File "/usr/lib/python3.13/json/__init__.py", line 293, in load
    return loads(fp.read(),
                 ~~~~~~~^^
  File "/usr/lib/python3.13/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position
4315: ordinal not in range(128)
)
  new-package-uses-date-based-version-number: Script failed:
/usr/share/lintian-brush/fixers/new-package-uses-date-based-version-number.py
(exit code 1) (stderr:
Traceback (most recent call last):
  File 
"/usr/share/lintian-brush/fixers/new-package-uses-date-based-version-number.py",
line 11, in <module>
    with ChangelogEditor() as editor:
         ~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3/dist-packages/debmutate/reformatting.py",
line 223, in __enter__
    self._orig_content = f.read()
                         ~~~~~~^^
  File "/usr/lib/python3.13/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position
705: ordinal not in range(128)
)
Run with --verbose for details.

Reply via email to