Skip to main content

Break from multiple loops at once in Python

Project description

breakall

Logo

Break from multiple loops at once in Python

Why isn't this a thing in Python?

PyPI version Downloads PyPI - Downloads PyPI - Python Version PyPI - Status GitHub - License GitHub Top Language CI Code Coverage Code Size Repo Size Issues

Index

Getting Started

Prerequisites

You will need Python +3.10 to run this module

# vermin output
Minimum required versions: 3.10
Incompatible versions:     2

[!NOTE]
The heavy use of ast.unparse makes this incompatible with Python version lower than 3.10

Installation

pip install git+https://github.com/Animenosekai/breakall.git

Basic Usage

You can use breakall by decorating your function with @enable_breakall:

>>> from breakall import enable_breakall
>>>
>>> @enable_breakall
... def main():
...     for i in range(10):
...         for j in range(10):
...             for k in range(10):
...                 if k == 5:
...                     breakall
...                 print(i, j, k)

Alternatively, you can enable breakall for all functions in your module by calling enable_breakall() without arguments:

from breakall import enable_breakall

def main():
    for i in range(10):
        for j in range(10):
            for k in range(10):
                if k == 5:
                    breakall
                print(i, j, k)

# This will enable breakall for all functions in the current scope
enable_breakall()

main()

Or you can use the command-line interface:

python -m breakall script.py
# or
breakall script.py

This will automatically enable breakall for all functions in the file.

Keyword

You can use breakall to break from multiple loops at once.

There is different syntax to determine how many loops to break from.

breakall

The breakall keyword will break from all loops in the current scope.

>>> from breakall import enable_breakall
>>>
>>> @enable_breakall
... def test():
...     for i in range(3):
...         for j in range(3):
...             if j == 1:
...                 breakall
...             print(f"  ({i}, {j})")
...         print(f"  End of i={i}")
...     print("Done")
>>>
>>> test()
  (0, 0)
Done

As you can see, the loops are completely exited without printing the rest of the iterations or "End of i={i}" message.

breakall: n

The breakall: n keyword will break from n loops in the current scope.

>>> from breakall import enable_breakall
>>>
>>> @enable_breakall
... def test():
...     for i in range(3):
...         for j in range(3):
...             for k in range(3):
...                 if k == 1:
...                     breakall: 2
...                 print(f"    ({i}, {j}, {k})")
...             print(f"  End of j={j}")
...         print(f"End of i={i}")
...     print("Done")
>>>
>>> test()
    (0, 0, 0)
End of i=0
    (1, 0, 0)
End of i=1
    (2, 0, 0)
End of i=2
Done

In this example, breakall: 2 breaks from the innermost two loops (the j and k loops), but the outermost i loop continues.

breakall @ n

The breakall @ n keyword will break from all loops up to and including the n-th loop from the inside.

>>> from breakall import enable_breakall
>>>
>>> @enable_breakall
... def test():
...     for i in range(3):  # Loop 1
...         for j in range(3):  # Loop 2
...             for k in range(3):  # Loop 3
...                 if k == 1:
...                     breakall @ 2
...                 print(f"    ({i}, {j}, {k})")
...             print(f"  End of k loop")
...         print(f"End of j loop")
...     print("Done")
>>>
>>> test()
    (0, 0, 0)
    (1, 0, 0)
    (2, 0, 0)
Done

In this example, breakall @ 2 targets loop 2 (the j loop), so it breaks from both loop 3 (k) and loop 2 (j), but loop 1 (i) continues.

[!NOTE] The breakall @ n keyword is 1-indexed, where 1 is the innermost loop

Linter and Type Checker Configuration

When using breakall, certain linting and type-checking tools may report false positives since they don't understand the breakall statement transformation.

Importing breakall

You can import the breakall statement to make your type checker happy, but it is not necessary for the code to work:

from breakall import breakall

def hello():
    for i in range(n):
        for j in range(m):
            if j == 1:
                breakall
            print(i, j)

Ruff

Add to your module using breakall:

# ruff: noqa: B018, F842

Where:

  • B018 - Useless expression (for bare breakall statements)
  • F842 - Local variable name is assigned to but never used (for breakall: n statements where the loop counter variable is not used)

Pyright

Add to your module using breakall:

# pyright: reportUnusedExpression=false, reportUnusedVariable=false, reportGeneralTypeIssues=false

Where:

  • reportUnusedExpression=false - Allow unused expressions (for breakall statements)
  • reportUnusedVariable=false - Allow unused variables (for breakall: n statements where the loop counter variable is not used)
  • reportGeneralTypeIssues=false - Suppress general type issues that may when using breakall: n statements (n is an integer, not a type)

Command-Line Interface

You can use the breakall module directly from the command line without decorating functions:

python -m breakall script.py

The CLI automatically applies enable_breakall to all functions in the target file:

# my_script.py
def nested_loops():
    for i in range(3):
        for j in range(3):
            if j == 1:
                breakall
            print(f"  ({i}, {j})")
        print(f"  End of i={i}")
    print("Done")

nested_loops()
$ python -m breakall my_script.py
  (0, 0)
Done

Tracing Imported Modules

By default, the CLI only applies enable_breakall to functions in the main file. If you're using breakall in imported modules, use the --trace flag:

python -m breakall script.py --trace

Example: Without --trace

If your imported module contains breakall, it will fail without --trace:

# helper.py
def helper_function():
    for i in range(3):
        for j in range(3):
            if j == 1:
                breakall
            print(f"  ({i}, {j})")
        print(f"  End of i={i}")
    print("Done")
# main.py
from helper import helper_function

helper_function()
$ python -m breakall main.py
  (0, 0)
Traceback (most recent call last):
  File "main.py", line 3, in <module>
    helper_function()
  File "helper.py", line 6, in helper_function
    breakall
NameError: name 'breakall' is not defined

Example: With --trace

Using --trace enables import hooking, so all imported modules support breakall:

$ python -m breakall main.py --trace
  (0, 0)
Done

The --trace flag automatically applies the enable_breakall decorator to all functions in imported modules, allowing them to use the breakall statement.

Saving Transformed Code

You can save the transformed Python code to a file using the --output option:

python -m breakall script.py --output transformed.py

This generates a new file with all breakall statements converted to proper loop-breaking logic.

Deployment

This module is currently in development and might contain bugs.

Feel free to use it in production if you feel like it is suitable for your production even if you may encounter issues.

Contributing

Pull requests are welcome. For major changes, please open an discussion first to discuss what you would like to change.

Please make sure to update the tests as appropriate.

Authors

Disclaimer

This project is not affiliated with the Python Software Foundation.

License

This project is licensed under the MIT License - see the LICENSE file for details

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

breakall-1.0rc1.tar.gz (738.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

breakall-1.0rc1-py3-none-any.whl (19.3 kB view details)

Uploaded Python 3

File details

Details for the file breakall-1.0rc1.tar.gz.

File metadata

  • Download URL: breakall-1.0rc1.tar.gz
  • Upload date:
  • Size: 738.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for breakall-1.0rc1.tar.gz
Algorithm Hash digest
SHA256 bf1802e7e250066024f911a8072177cd0f7bce7a5a6bbb3df0b631f03692c342
MD5 2e022d484df8b1321f59d838e852a349
BLAKE2b-256 84ed2e65b015545a5a6b9d37853520e0cfc848b50e53b790e8628a8dda9cc00f

See more details on using hashes here.

File details

Details for the file breakall-1.0rc1-py3-none-any.whl.

File metadata

  • Download URL: breakall-1.0rc1-py3-none-any.whl
  • Upload date:
  • Size: 19.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for breakall-1.0rc1-py3-none-any.whl
Algorithm Hash digest
SHA256 e8daad311158e3337e76c8d2794197ecaf468a93f224144baec3ef2e05e33cff
MD5 e483419c3a92668d0f107c7c2f043297
BLAKE2b-256 53ae7eb28d0c94c3116aba1bbe439cadae09085f06c50e182fa1ffd2e7eb8631

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page