From 83373ff91f53ff9562f271c32b4304a7afc77c1b Mon Sep 17 00:00:00 2001 From: RPBot Date: Tue, 11 Aug 2026 12:57:12 +0000 Subject: [PATCH] Revert repo-wide formatter changes outside the article folder A repo-wide `ruff format` run reformatted 14 files belonging to 13 unrelated tutorials. Because pyproject.toml sets target-version = "py314", ruff applied PEP 758 and stripped the parentheses from multi-exception except clauses in 9 published sample files: except (ZeroDivisionError, ValueError): -> except ZeroDivisionError, ValueError: That syntax is valid on Python 3.14 but a SyntaxError on 3.13 and earlier, so those materials would stop running for most readers - and the CI matrix is Python 3.14 only, so it stays green either way. Reverts all 14 files to master. No changes to cursor-vs-copilot-python/. --- asyncio-walkthrough/areq.py | 2 +- cursor-vs-windsurf-python/prompts.md | 2 - django-pagination/README.md | 1 + flask-connexion-rest-part-2/README.md | 1 + flush-print/README.md | 2 +- python-copy/benchmark.py | 2 +- python-eval-mathrepl/mathrepl.py | 2 +- .../local/serializers/factory.py | 2 +- .../population_quiz/population_quiz.py | 2 +- python-multiple-exceptions/exception_pass.py | 2 +- .../multiple_exceptions.py | 2 +- python-type-checking/hearts.py | 2 +- structural-pattern-matching/guessing_game.py | 2 +- wordcount/tests/realpython/HOWTO.md | 68 ++++++++++--------- 14 files changed, 47 insertions(+), 45 deletions(-) diff --git a/asyncio-walkthrough/areq.py b/asyncio-walkthrough/areq.py index 97bb63cc45..12ae0cde14 100644 --- a/asyncio-walkthrough/areq.py +++ b/asyncio-walkthrough/areq.py @@ -74,7 +74,7 @@ async def parse(url: str, session: ClientSession, **kwargs) -> set: try: # Ensure we return an absolute path. abslink = urllib.parse.urljoin(url, link) - except urllib.error.URLError, ValueError: + except (urllib.error.URLError, ValueError): logger.exception("Error parsing URL: %s", link) pass else: diff --git a/cursor-vs-windsurf-python/prompts.md b/cursor-vs-windsurf-python/prompts.md index 43ff7eb5de..e89ca1d639 100644 --- a/cursor-vs-windsurf-python/prompts.md +++ b/cursor-vs-windsurf-python/prompts.md @@ -62,8 +62,6 @@ Type this into a file and let each editor complete it: @dataclass class RetryMetadata: attempts_made: int - - # ... ``` diff --git a/django-pagination/README.md b/django-pagination/README.md index 3aeffd42b3..006ca99b16 100644 --- a/django-pagination/README.md +++ b/django-pagination/README.md @@ -67,6 +67,7 @@ Add the Python keywords to your database: >>> for kw in keyword.kwlist: ... k = Keyword(name=kw) ... k.save() +... ``` Verify that the keywords were added to your database: diff --git a/flask-connexion-rest-part-2/README.md b/flask-connexion-rest-part-2/README.md index 74e1298a27..ae4f41da5e 100644 --- a/flask-connexion-rest-part-2/README.md +++ b/flask-connexion-rest-part-2/README.md @@ -51,6 +51,7 @@ Navigate inside the `rp_flask_api/`, enter the [Python interactive shell](https: >>> for person_data in people: ... insert_cmd = f"INSERT INTO person VALUES ({person_data})" ... conn.execute(insert_cmd) +... diff --git a/flush-print/README.md b/flush-print/README.md index d826c8492c..c9a940b00e 100644 --- a/flush-print/README.md +++ b/flush-print/README.md @@ -42,7 +42,7 @@ SLIGHTLY_TOO_LARGE_FOR_BUFFER = 80_000 # Script paused at 10919 bufsize = 80_000 - 10919 -print(bufsize) # 69081 <-- Your buffer size approximation +print(bufsize) # 69081 <-- Your buffer size approximation ``` You can divide the number you get by `1000` to get an estimation of your buffer size for stdout in kilobytes. In the example above, on a macOS system with a M1 chip, the buffer size of stdout when interacting with it through Python's `print()` would therefore be approximately 69 kilobytes. diff --git a/python-copy/benchmark.py b/python-copy/benchmark.py index 1f73eccd10..5533ee28c0 100644 --- a/python-copy/benchmark.py +++ b/python-copy/benchmark.py @@ -32,7 +32,7 @@ def benchmark(container, executions): def sliceable(instance): try: instance[0:1] - except TypeError, KeyError: + except (TypeError, KeyError): return False else: return True diff --git a/python-eval-mathrepl/mathrepl.py b/python-eval-mathrepl/mathrepl.py index 411a3e869c..4a03e266d1 100644 --- a/python-eval-mathrepl/mathrepl.py +++ b/python-eval-mathrepl/mathrepl.py @@ -53,7 +53,7 @@ def main(): # Read user's input try: expression = input(f"{PS1} ") - except KeyboardInterrupt, EOFError: + except (KeyboardInterrupt, EOFError): raise SystemExit() # Handle special commands diff --git a/python-import/namespace_package/local/serializers/factory.py b/python-import/namespace_package/local/serializers/factory.py index d50ac4ce68..917036f10a 100644 --- a/python-import/namespace_package/local/serializers/factory.py +++ b/python-import/namespace_package/local/serializers/factory.py @@ -7,7 +7,7 @@ def get_serializer(format): try: module = importlib.import_module(f"serializers.{format}") serializer = getattr(module, f"{format.title()}Serializer") - except ImportError, AttributeError: + except (ImportError, AttributeError): raise ValueError(f"Unknown format {format!r}") from None return serializer() diff --git a/python-import/population_quiz/population_quiz.py b/python-import/population_quiz/population_quiz.py index f37ac6c5aa..ddea1252a1 100644 --- a/python-import/population_quiz/population_quiz.py +++ b/python-import/population_quiz/population_quiz.py @@ -46,7 +46,7 @@ def run_quiz(population, num_questions, num_countries): try: guess_idx = int(guess_str) - 1 guess = countries[guess_idx] - except ValueError, IndexError: + except (ValueError, IndexError): print(f"Please answer between 1 and {num_countries}") else: break diff --git a/python-multiple-exceptions/exception_pass.py b/python-multiple-exceptions/exception_pass.py index 7169a4ac67..959665791b 100644 --- a/python-multiple-exceptions/exception_pass.py +++ b/python-multiple-exceptions/exception_pass.py @@ -1,5 +1,5 @@ try: with open("file.txt", mode="rt") as f: print(f.readlines()) -except FileNotFoundError, PermissionError: +except (FileNotFoundError, PermissionError): pass diff --git a/python-multiple-exceptions/multiple_exceptions.py b/python-multiple-exceptions/multiple_exceptions.py index da5b258cae..f880feff26 100644 --- a/python-multiple-exceptions/multiple_exceptions.py +++ b/python-multiple-exceptions/multiple_exceptions.py @@ -2,5 +2,5 @@ first = float(input("What is your first number? ")) second = float(input("What is your second number? ")) print(f"{first} divided by {second} is {first / second}") -except ZeroDivisionError, ValueError: +except (ZeroDivisionError, ValueError): print("There was an error") diff --git a/python-type-checking/hearts.py b/python-type-checking/hearts.py index e2c02c9030..4f93f572e2 100644 --- a/python-type-checking/hearts.py +++ b/python-type-checking/hearts.py @@ -146,7 +146,7 @@ def play_card(self, played: List[Card], hearts_broken: bool) -> Card: try: card_num = int(input(f" {self.name}, choose card: ")) card = playable[card_num] - except ValueError, IndexError: + except (ValueError, IndexError): pass else: break diff --git a/structural-pattern-matching/guessing_game.py b/structural-pattern-matching/guessing_game.py index 1ba5e50a8c..e1094c004a 100644 --- a/structural-pattern-matching/guessing_game.py +++ b/structural-pattern-matching/guessing_game.py @@ -61,6 +61,6 @@ def bye(): if __name__ == "__main__": try: main() - except KeyboardInterrupt, EOFError: + except (KeyboardInterrupt, EOFError): print() bye() diff --git a/wordcount/tests/realpython/HOWTO.md b/wordcount/tests/realpython/HOWTO.md index a6aaa420d5..8ca1bd7be5 100644 --- a/wordcount/tests/realpython/HOWTO.md +++ b/wordcount/tests/realpython/HOWTO.md @@ -21,18 +21,20 @@ Inside each task file, create a class decorated with the `@task()` decorator: ```python from realpython import task - @task( number=1, name="Run the wordcount Command", url="https://realpython.com/lessons/run-the-wordcount-command-task/", ) class Test: - def test_one(self): ... - - def test_two(self): ... + def test_one(self): + ... + + def test_two(self): + ... - def test_three(self): ... + def test_three(self): + ... ``` This class can be named anything, e.g., `Test`, and you can reuse this name across different files if you want to. @@ -55,7 +57,6 @@ You can associate resources common to all test methods by placing the correspond ```python from realpython import task, tutorial, course, podcast - @task( number=1, name="Run the wordcount Command", @@ -63,7 +64,8 @@ from realpython import task, tutorial, course, podcast ) @tutorial("python-comments-guide") @course("writing-comments-python", "Writing Comments in Python") -class Test: ... +class Test: + ... ``` This will cascade down to the individual test methods, meaning that if one of them fails, then we'll include that resource on the list of hints. @@ -73,20 +75,23 @@ In contrast, decorating the individual test methods will let you associate resou ```python from realpython import task, tutorial, course, podcast - @task( number=1, name="Run the wordcount Command", url="https://realpython.com/lessons/run-the-wordcount-command-task/", ) class Test: - def test_one(self): ... - + + def test_one(self): + ... + @course("writing-comments-python", "Writing Comments in Python") - def test_two(self): ... - + def test_two(self): + ... + @tutorial("python-comments-guide") - def test_three(self): ... + def test_three(self): + ... ``` These decorators expect the **slug** to identify a resource in the CMS. If you don't provide a title, which is an optional parameter, then the slug will be automatically prettified and used as a link label. @@ -96,7 +101,8 @@ These decorators expect the **slug** to identify a resource in the CMS. If you d By default, the plugin will try to prettify the acceptance criteria shown in the report based on the name of the corresponding test method, e.g.: ```python -def test_reports_zeros_on_an_empty_stream(self): ... +def test_reports_zeros_on_an_empty_stream(self): + ... ``` ...becomes "_Reports zeros on an empty stream_." @@ -115,18 +121,15 @@ pytest allows you to run the same test method against different parameters (data ```python import pytest - -@pytest.mark.parametrize( - "flags", - [ - [], - ["-l"], - ["-w"], - ["-c"], - ["-l", "-w", "-c"], - ], -) -def test_always_displays_counts_in_the_same_order(self, flags): ... +@pytest.mark.parametrize("flags", [ + [], + ["-l"], + ["-w"], + ["-c"], + ["-l", "-w", "-c"], +]) +def test_always_displays_counts_in_the_same_order(self, flags): + ... ``` The resulting report will append the values of the parameters to the name of the acceptance criteria. This will work regardless of whether you provde a docstring or not. @@ -138,11 +141,11 @@ By default, each test method will time out after a predefined number of seconds. ```python import pytest - @task(...) class Test: @pytest.mark.timeout(3.5) - def test_one(self): ... + def test_one(self): + ... ``` ## Running Tests in DEBUG Mode @@ -184,12 +187,13 @@ However, it sill won't show the **expected vs. actual**. If you want to do that, ```python from realpython import task, assert_equals - @task(...) class Test: def test_one(self): assert_equals( - "expected", function(), "Your function should return XYZ" + "expected", + function(), + "Your function should return XYZ" ) ``` @@ -198,14 +202,13 @@ Note that the order of these arguments matters! The _expected_ value always come ```python from realpython import task, assert_equals - @task(...) class Test: def test_one(self): assert_equals( expected="expected", actual=function(), - message="Your function should return XYZ", + message="Your function should return XYZ" ) ``` @@ -214,7 +217,6 @@ If you just want to show the expected vs actual without any extra message, then ```python from realpython import task, assert_equals - @task(...) class Test: def test_one(self):