autopep8: Problem with formatting f-strings on python3.12

Hey. There is an issue with autopep8 and python3.12 that autopep8 tries to format the content inside the f-strings. For instance connector = f"socks5://{user}:{password}:{url}:{port}" gets formatted to: connector = f"socks5: //{user}: {password}: {url}: {port}"

The following is another sample: image

About this issue

  • Original URL
  • State: closed
  • Created 8 months ago
  • Reactions: 12
  • Comments: 18 (5 by maintainers)

Commits related to this issue

Most upvoted comments

I see the same problem with the newline after { in f strings but only with the pre-commit v2.0.4 hook. If run autopep8 v2.0.4 from the CLI everything seems to be okay. I have the following line in my code:

url=f'https://gitlab.example.example.example.com/api/v4/projects/{project_id}'

With the autopep8 CLI (just the whitespaces will be fixed, e.g. what I would expect):

url = f'https://gitlab.example.example.example.com/api/v4/projects/{project_id}'

With autopep8 pre-commit (the linebreak after { will be inserted):

url = f'https://gitlab.example.example.example.com/api/v4/projects/{
    project_id}'

My pre-commit config is:

  - repo: https://github.com/hhatto/autopep8
    rev: v2.0.4
    hooks:
      - id: autopep8

I don’t mean to come across as alarmist, but this is a serious bug. The https://github.com/apache/trafficserver project relies upon autopep8 to format its Python files, and this single issue keeping us from moving to fedora:39 because its system version of Python is 3.12. We use a pre-commit hook to block any commits that fail autopep8, so Apache Traffic Server devs currently cannot upgrade to fedora:39. Can the maintainer (@hhatto) please comment on the status of this issue and provide an ETA for the landing of its fix?

Thank you for this tool. It has been helpful to the Apache Traffic Server project.

Kind regards, Brian

I have been hitting similar problem like @mvo5 . I have had identical version in use autopep8 2.0.4 (pycodestyle: 2.10.0). For me solution was to upgrade pycodestyle to 2.11.1 which is bringing python3.12 support [1].

[1] https://github.com/PyCQA/pycodestyle/blob/main/CHANGES.txt

@hhatto: Thanks for the reply.

Just in case it’s helpful, autopep8 on Python 3.12 greatly changes the white space within format strings, not only the addition of newlines. It adds or subtracts a variety of types of white space in various places. Here’s some samples of a run of it on the apache/trafficserver code base:

Removing white space

diff --git a/tests/gold_tests/bad_http_fmt/bad_http_fmt.test.py b/tests/gold_tests/bad_http_fmt/bad_http_fmt.test.py
index 8e84e18e1..059971175 100644
--- a/tests/gold_tests/bad_http_fmt/bad_http_fmt.test.py
+++ b/tests/gold_tests/bad_http_fmt/bad_http_fmt.test.py
@@ -45,7 +45,7 @@ ts.Disk.ip_allow_yaml.AddLines([
     '    action: allow',
     '    methods:',
     '      - GET',
-    f'      - {random_method}',
+    f' - {random_method}',
 ])

Adding spaces around colons:

diff --git a/tests/gold_tests/autest-site/trafficserver.test.ext b/tests/gold_tests/autest-site/trafficserver.test.ext
index 1ebf39e0d..59de90f49 100755
--- a/tests/gold_tests/autest-site/trafficserver.test.ext
+++ b/tests/gold_tests/autest-site/trafficserver.test.ext
@@ -409,9 +409,9 @@ def MakeATSProcess(obj, name, command='traffic_server', select_ports=True,
             port_str += " {ssl_port}:quic {ssl_portv6}:quic:ipv6".format(
                 ssl_port=p.Variables.ssl_port, ssl_portv6=p.Variables.ssl_portv6)
         if enable_proxy_protocol:
-            port_str += f" {p.Variables.proxy_protocol_port}:pp {p.Variables.proxy_protocol_portv6}:pp:ipv6"
+            port_str += f" {p.Variables.proxy_protocol_port}: pp {p.Variables.proxy_protocol_portv6}: pp: ipv6"
             if enable_tls:
-                port_str += f" {p.Variables.proxy_protocol_ssl_port}:pp:ssl {p.Variables.proxy_protocol_ssl_portv6}:pp:ssl:ipv6"
+                port_str += f" {p.Variables.proxy_protocol_ssl_port}: pp: ssl {p.Variables.proxy_protocol_ssl_portv6}: pp: ssl: ipv6"
         #p.Env['PROXY_CONFIG_HTTP_SERVER_PORTS'] = port_str
         p.Disk.records_config.update({
             'proxy.config.http.server_ports': port_str,

Addition of newlines

You mentioned this above:

@@ -73,8 +74,9 @@ tr = Test.AddTestRun()
 tr.Processes.Default.StartBefore(ts)
 tr.Processes.Default.Command = (
     server_cmd(1) +
-    fr" ; printf 'GET {random_path}HTTP/1.0\r\n" +
-    fr"Host: localhost:{Test.Variables.server_port}\r\n" +
+    fr"
+printf 'GET {random_path}HTTP/1.0\r\n" +
+    fr"Host: localhost: {Test.Variables.server_port}\r\n" +
     r"X-Req-Id: 0\r\n\r\n'" +
     f" | nc localhost {ts.Variables.port} >> client.log" +
     " ; echo '======' >> client.log"

Here’s a particularly extreme example:

diff --git a/tests/gold_tests/cache/background_fill.test.py b/tests/gold_tests/cache/background_fill.test.py
index 56125a492..c0d02fbb4 100644
--- a/tests/gold_tests/cache/background_fill.test.py
+++ b/tests/gold_tests/cache/background_fill.test.py
@@ -58,7 +58,7 @@ class BackgroundFillTest:
                 "dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key")
 
             self.ts[name].Disk.records_config.update({
-                "proxy.config.http.server_ports": f"{self.ts[name].Variables.port} {self.ts[name].Variables.ssl_port}:ssl",
+                "proxy.config.http.server_ports": f"{self.ts[name].Variables.port} {self.ts[name].Variables.ssl_port}: ssl",
                 "proxy.config.http.background_fill_active_timeout": "0",
                 "proxy.config.http.background_fill_completed_threshold": "0.0",
                 "proxy.config.http.cache.required_headers": 0,  # Force cache
@@ -111,15 +111,41 @@ class BackgroundFillTest:
         tr = Test.AddTestRun()
         self.__checkProcessBefore(tr)
         tr.Processes.Default.Command = f"""
-curl -X PURGE --http1.1 -vs http://127.0.0.1:{self.ts['for_httpbin'].Variables.port}/drip?duration=4;
-timeout 2 curl --http1.1 -vs http://127.0.0.1:{self.ts['for_httpbin'].Variables.port}/drip?duration=4;
+curl -X PURGE --http1.1 -vs http: //127.0.0.1: {self.ts['for_httpbin'].Variables.port}/drip?duration=4;
+timeout 2 curl --http1.1 -vs http://127.0.0.1:{self.ts['for_httpbin         '                                                        ]
+        .
+                                               V
+                                               a
+                                               r
+                                               i
+                                               a
+                                               b
+                                               l
+                                               e
+                                               s
+                                               .
+                                               p
+                                               o
+                                               r
+                                               t
+                                               } /
+        d
+        r
+        i
+        p
+        ?
+        d
+        u
+        r
+        a
+        t
+        i
+        o
+        n
+        =
+        4
 sleep 4;

Removing newlines

In addition to additional newlines, it seems to also sometimes remove newlines:

-curl --http1.1 -vsk https://127.0.0.1:{self.ts['for_httpbin'].Variables.ssl_port}/drip?duration=4
-"""
-        tr.Processes.Default.ReturnCode = 0
-        tr.Processes.Default.Streams.stderr = "gold/background_fill_1_stderr.gold"
-        self.__checkProcessAfter(tr)
-
+curl --http1.1 -vsk https://127.0.0.1:{self.ts['for_httpbin'].Variables.ssl_port}/drip?d uration=4 """         tr.Processes.Default.ReturnCode = 0         tr.Processes.Default.Streams.stderr = "gold/background_fill_1_stderr.gold"         self.__checkProcessAfter(tr) 

Adding trailing white space

Note that the previous patch actually adds a trailing space at the end, which may not render in github.