[Python] Using typeguard decorator: @typechecked in Python, whilst evading circular imports?

Discussão em 'Python' iniciado por Stack, Setembro 26, 2024.

  1. Stack

    Stack Membro Participativo

    Context


    To prevent circular imports in Python when using type-hints, one can use the following construct:

    # controllers.py
    from __future__ import annotations
    from typing import TYPE_CHECKING

    if TYPE_CHECKING:
    from models import Book


    class BookController:
    def __init__(self, book: "Book") -> None:
    self.book = book


    Where the if TYPE_CHECKING: is only executed during type checking, and not during execution of the code.

    Issue


    When one applies active function argument type verification, (based on the type hints of the arguments), typeguard throws the error:


    NameError: name 'Supported_experiment_settings' is not defined
    ​
    MWE I


    # models.py
    from controllers import BookController

    from typeguard import typechecked

    class Book:

    @typechecked
    def get_controller(self, some_bookcontroller:BookController):
    return some_bookcontroller

    some_book=Book()
    BookController("somestring")


    And:

    # controllers.py
    from __future__ import annotations
    from typing import TYPE_CHECKING
    from typeguard import typechecked
    #from models import Book

    if TYPE_CHECKING:
    from models import Book

    class BookController:

    @typechecked
    def __init__(self, book: Book) -> None:
    self.book = book


    Note the #from models import Book is commented out. Now if one runs:

    python models.py


    It throws the error:


    File "/home/name/Documents/eg/models.py", line 13, in BookController("somestring") ... NameError: name 'Book' is not defined. Did you mean: 'bool'? because the typechecking for def __init__(self, book: Book) -> None: does not know what the class Book is.
    ​
    MWE II


    Then if one disables @typechecked in controllers.py with:

    # controllers.py
    from __future__ import annotations
    from typing import TYPE_CHECKING
    from typeguard import typechecked

    if TYPE_CHECKING:
    from models import Book

    class BookController:

    #@typechecked
    def __init__(self, book: Book) -> None:
    self.book = book


    it works. (But no typechecking).

    MWE III


    Then if one re-enables typechecking, and includes the import of book, (with from models import Book) like:

    # controllers.py
    from __future__ import annotations
    from typing import TYPE_CHECKING
    from typeguard import typechecked
    from models import Book

    if TYPE_CHECKING:
    from models import Book

    class BookController:

    @typechecked
    def __init__(self, book: Book) -> None:
    self.book = book


    It throws the circular import error:

    Traceback (most recent call last):
    File "/home/name/Documents/eg/models.py", line 2, in <module>
    from controllers import BookController
    File "/home/name/Documents/eg/controllers.py", line 5, in <module>
    from models import Book
    File "/home/name/Documents/eg/models.py", line 2, in <module>
    from controllers import BookController
    ImportError: cannot import name 'BookController' from partially initialized module 'controllers' (most likely due to a circular import) (/home/name/Documents/eg/controllers.py)

    Question


    How can one evade this circular import whilst still allowing the @typechecked decorator to verify/access the Book import?

    Is there an equivalent TYPE_CHECKING boolean for typeguard?

    Continue reading...

Compartilhe esta Página