Repository navigation
Differing behavior between linux and macos #285
Copy link
Copy link
Open
Labels
C: conventionRelates to docstring format conventionRelates to docstring format conventionP: bugPEP 257 violation or existing functionality that doesn't work as documentedPEP 257 violation or existing functionality that doesn't work as documentedU: highhelp wanted
Description
if __name__ == "__main__":
"""Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaa┘."""
# This is a commentdocformatter -i test.py on macOS (arm) results in no changes being made. docformatter -i test.py on linux aarch64 results in the following changes:
if __name__ == "__main__":
"""Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaa┘."""
# This is a commentRepeatedly executing docformatter -i on linux adds another line on every call which breaks idempotency.
if __name__ == "__main__":
"""Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaa┘."""
# This is a commentThis might be related to ┘ being in the string.
breaking env on linux:
(base) mambauser@79d93f1c012c:/mnt/abc$ micromamba list -p /home/mambauser/.cache/pre-commit/repotqc_4xjd/conda-default
List of packages in environment: "/home/mambauser/.cache/pre-commit/repotqc_4xjd/conda-default"
Name Version Build Channel
────────────────────────────────────────────────────────────────────
_openmp_mutex 4.5 2_gnu conda-forge
bzip2 1.0.8 h31becfc_5 conda-forge
ca-certificates 2024.6.2 hcefe29a_0 conda-forge
charset-normalizer 3.3.2 pyhd8ed1ab_0 conda-forge
docformatter 1.7.5 pyhd8ed1ab_0 conda-forge
ld_impl_linux-aarch64 2.40 h9fc2d93_7 conda-forge
libexpat 2.6.2 h2f0025b_0 conda-forge
libffi 3.4.2 h3557bc0_5 conda-forge
libgcc-ng 14.1.0 he277a41_0 conda-forge
libgomp 14.1.0 he277a41_0 conda-forge
libnsl 2.0.1 h31becfc_0 conda-forge
libsqlite 3.46.0 hf51ef55_0 conda-forge
libuuid 2.38.1 hb4cce97_0 conda-forge
libxcrypt 4.4.36 h31becfc_1 conda-forge
libzlib 1.3.1 h68df207_1 conda-forge
ncurses 6.5 h0425590_0 conda-forge
openssl 3.3.1 h68df207_1 conda-forge
pip 24.0 pyhd8ed1ab_0 conda-forge
python 3.12.4 h829453d_0_cpython conda-forge
readline 8.2 h8fc344f_1 conda-forge
setuptools 70.1.1 pyhd8ed1ab_0 conda-forge
tk 8.6.13 h194ca79_0 conda-forge
tomli 2.0.1 pyhd8ed1ab_0 conda-forge
tzdata 2024a h0c530f3_0 conda-forge
untokenize 0.1.1 py_0 conda-forge
wheel 0.43.0 pyhd8ed1ab_1 conda-forge
xz 5.2.6 h9cdd2b7_0 conda-forge
working env on macos:
❯ micromamba list -p /Users/pavel/.cache/pre-commit/repox0xmrc5j/conda-default
List of packages in environment: "/Users/pavel/.cache/pre-commit/repox0xmrc5j/conda-default"
Name Version Build Channel
─────────────────────────────────────────────────────────────────────
bzip2 1.0.8 h93a5062_5 conda-forge
ca-certificates 2024.2.2 hf0a4a13_0 conda-forge
charset-normalizer 3.3.2 pyhd8ed1ab_0 conda-forge
docformatter 1.7.5 pyhd8ed1ab_0 conda-forge
libexpat 2.6.2 hebf3989_0 conda-forge
libffi 3.4.2 h3422bc3_5 conda-forge
libsqlite 3.45.2 h091b4b1_0 conda-forge
libzlib 1.2.13 h53f4e23_5 conda-forge
ncurses 6.4.20240210 h078ce10_0 conda-forge
openssl 3.2.1 h0d3ecfb_1 conda-forge
pip 24.0 pyhd8ed1ab_0 conda-forge
python 3.12.2 hdf0ec26_0_cpython conda-forge
readline 8.2 h92ec313_1 conda-forge
setuptools 69.2.0 pyhd8ed1ab_0 conda-forge
tk 8.6.13 h5083fa2_1 conda-forge
tomli 2.0.1 pyhd8ed1ab_0 conda-forge
tzdata 2024a h0c530f3_0 conda-forge
untokenize 0.1.1 py_0 conda-forge
wheel 0.43.0 pyhd8ed1ab_0 conda-forge
xz 5.2.6 h57fd34a_0 conda-forge
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
C: conventionRelates to docstring format conventionRelates to docstring format conventionP: bugPEP 257 violation or existing functionality that doesn't work as documentedPEP 257 violation or existing functionality that doesn't work as documentedU: highhelp wanted
Activity
weibullguy commentedon May 11, 2025
I do not have access to a macos machine to be able to test this.
jamesbraza commentedon Oct 30, 2025
Fwiw one could use GitHub Actions runners (e.g.
ubuntu-latest,macos-latest) or Docker containers to test thisjaysheeldodia commentedon Oct 6, 2026
Cause
I was able to reproduce this issue on my Mac as well as in Linux Docker containers. As far as I can tell, this issue is specific to Python 3.12.4.
Reproducing the issue
I tested two Python versions, 3.12.4 and 3.14.7, and two docformatter versions, 1.7.5 and 1.7.8. Since I work on macOS, it was easy to reproduce the issue using just
uv. The bug appears, in different forms, only when the Python version is 3.12.4. With Python 3.12.4 and docformatter 1.7.5, a new line is added below the docstring every timedocformatter -i filename.pyruns. With docformatter 1.7.8, a whitespace is added after the docstring instead.With Python 3.14.7 (one version behind the latest), the issue does not appear with either docformatter version.
I also tested this on Linux and was able to reproduce the issue with Python 3.12.4 and both docformatter 1.7.5 and 1.7.8. The issue does not appear with Python 3.14.7 and docformatter 1.7.8.
Because of an unknown problem in the Linux Docker container, I wasn't able to test Python 3.14.7 with docformatter 1.7.5, but I don't think that test is needed at this point.
Findings
The issue appears with Python 3.12.4 on every docformatter version I tested, and goes away when Python is updated to the latest version.
Fix
The fix is simple: if you're not limited to Python 3.12.4, use the latest Python version.
If you have to use Python 3.12.4, you can apply this fix locally. docformatter reads the column numbers Python gives each token and uses them to copy the gap between tokens when it writes the file back. On Python 3.12.4, after a multiline docstring that contains a character like
┘, those numbers are byte counts instead of character counts. The gap is two columns wide and the copied text is a line break, so every run adds another blank line.The fix runs immediately after tokenization. For each token on a single line, it checks that the column really points at that token's text. If the column is a byte count, it converts it to a character count, but only when the new column matches the token. On Python versions where the columns are already correct, nothing changes.
Changes
@weibullguy, if docformatter needs a code fix for this specific case, I have already implemented one and I'm happy to raise a PR with all test cases passing and the GitHub workflows running successfully.
If it's decided that a fix specific to this Python version isn't needed, we can keep updating Python to the latest version as the fix and close this issue.