Skip to content

Cannot import loguru.Writable #130

@phillipuniverse

Description

@phillipuniverse

I'm trying to write a simplified type hint for what defines a sink like this:

LoguruSink = Union[TextIO, loguru.Writable, Callable[[loguru.Message], None], logging.Handler]

When I do this, I get the following error:

  File "...myproj/config/logging.py", line 109, in <module>
    LoguruSink = Union[TextIO, loguru.Writable, Callable[[loguru.Message], None], logging.Handler]
AttributeError: module 'loguru' has no attribute 'Writable'

I am doing this because I have a dynamic expression like this to define a sink:

def some_other_sink(message: loguru.Message) -> None:
    ...

default_sink = sys.stdout if settings.is_local_dev else some_other_sink

logger.add(
    default_sink, level=settings.log_level.upper(), format=log_fmt
)

When I lint this with Mypy though, it does not quite see the correct type of default_sink, admittedly a bit of a mypy limitation IMO:

myproj/config/logging.py:129: error: No overload variant of "add" of "Logger"
matches argument types "object", "str", "str", "Dict[str, str]"  [call-overload]
        logger.add(
        ^
myproj/config/logging.py:129: note: Possible overload variants:
myproj/config/logging.py:129: note:     def add(self, sink: Union[TextIO, Writable, Callable[[Message], None], Handler], *, level: Union[str, int] = ..., format: Union[str, Callable[[Record], str]] = ..., filter: Union[str, Callable[[Record], bool], Dict[Optional[str], Union[str, int, bool]], None] = ..., colorize: Optional[bool] = ..., serialize: bool = ..., backtrace: bool = ..., diagnose: bool = ..., enqueue: bool = ..., catch: bool = ...) -> int
myproj/config/logging.py:129: note:     def add(self, sink: Callable[[Message], Awaitable[None]], *, level: Union[str, int] = ..., format: Union[str, Callable[[Record], str]] = ..., filter: Union[str, Callable[[Record], bool], Dict[Optional[str], Union[str, int, bool]], None] = ..., colorize: Optional[bool] = ..., serialize: bool = ..., backtrace: bool = ..., diagnose: bool = ..., enqueue: bool = ..., catch: bool = ..., loop: Optional[AbstractEventLoop] = ...) -> int
myproj/config/logging.py:129: note:     def add(self, sink: Union[str, _PathLike[str]], *, level: Union[str, int] = ..., format: Union[str, Callable[[Record], str]] = ..., filter: Union[str, Callable[[Record], bool], Dict[Optional[str], Union[str, int, bool]], None] = ..., colorize: Optional[bool] = ..., serialize: bool = ..., backtrace: bool = ..., diagnose: bool = ..., enqueue: bool = ..., catch: bool = ..., rotation: Union[str, int, time, timedelta, Callable[[Message, TextIO], bool], None] = ..., retention: Union[str, int, timedelta, Callable[[List[str]], None], None] = ..., compression: Union[str, Callable[[str], None], None] = ..., delay: bool = ..., mode: str = ..., buffering: int = ..., encoding: str = ..., **kwargs: Any) -> int
Found 1 error in 1 file (checked 122 source files)
make: *** [lint] Error 1

But if I type hint default_sink like this, mypy is happy with no errors:

def some_other_sink(message: loguru.Message) -> None:
    ...

default_sink: default_sink: Union[TextIO, Callable[[loguru.Message], None]] = sys.stdout if settings.is_local_dev else some_other_sink

logger.add(
    default_sink, level=settings.log_level.upper(), format=log_fmt
)

Buuuut.... my completionist brain is not happy. So 2 questions:

  1. Is there a reason that loguru.Writable is not exposed?
  2. If you want to keep loguru.Writable un-importable, would you consider exposing a new Sink type that I could use like loguru.Sink?

FWIW I am already using loguru-mypy.

Thanks for the great library! I have dug in pretty deep to it and appreciate the very intentional design decisions that have been made.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions