Skip to content

Commit

Permalink
Moved logging-related code to cleo.logging package, tests to test_l…
Browse files Browse the repository at this point in the history
…ogging.py, and renamed Handler subclass to CleoHandler
  • Loading branch information
dylan-robins committed Jan 4, 2025
1 parent 280f693 commit 0846085
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 83 deletions.
18 changes: 4 additions & 14 deletions src/cleo/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
from cleo.io.inputs.definition import Definition
from cleo.io.inputs.option import Option
from cleo.io.io import IO
from cleo.io.output_handler import OutputHandler
from cleo.io.outputs.output import Verbosity
from cleo.io.outputs.stream_output import StreamOutput
from cleo.logging.cleo_handler import CleoHandler
from cleo.terminal import Terminal
from cleo.ui.ui import UI

Expand Down Expand Up @@ -537,21 +537,11 @@ def _configure_logging(self, io: IO) -> None:
"""
Configures the built-in logging package to write it's output via Cleo's output class.
"""
handler = CleoHandler(io.output)
handler.setLevel(io.output.verbosity)
root = logging.getLogger()

level_mapping = {
Verbosity.QUIET: logging.CRITICAL, # Nothing gets emitted to the output anyway
Verbosity.NORMAL: logging.WARNING,
Verbosity.VERBOSE: logging.INFO,
Verbosity.VERY_VERBOSE: logging.DEBUG,
Verbosity.DEBUG: logging.DEBUG,
}

root.setLevel(level_mapping[io.output.verbosity])

handler = OutputHandler(io.output)
handler.setLevel(level_mapping[io.output.verbosity])
root.addHandler(handler)
root.setLevel(handler.level)

@property
def _default_definition(self) -> Definition:
Expand Down
Empty file added src/cleo/logging/__init__.py
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
from typing import TYPE_CHECKING
from typing import ClassVar

from cleo.io.outputs.output import Verbosity


if TYPE_CHECKING:
from cleo.io.outputs.output import Output


class OutputHandler(logging.Handler):
class CleoHandler(logging.Handler):
"""
A handler class which writes logging records, appropriately formatted,
to a Cleo output stream.
Expand Down Expand Up @@ -44,3 +46,17 @@ def emit(self, record: logging.LogRecord) -> None:

except Exception:
self.handleError(record)

def setLevel(self, verbosity: Verbosity) -> None: # noqa: N802
"""
Set the logging level of this handler. verbosity must be an instance of Cleo's Verbosity enum.
This level is then mapped to it's corresponding `logging` level.
"""
level_mapping = {
Verbosity.QUIET: logging.CRITICAL, # Nothing gets emitted to the output anyway
Verbosity.NORMAL: logging.WARNING,
Verbosity.VERBOSE: logging.INFO,
Verbosity.VERY_VERBOSE: logging.DEBUG,
Verbosity.DEBUG: logging.DEBUG,
}
return super().setLevel(level_mapping[verbosity])
68 changes: 0 additions & 68 deletions tests/test_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
from tests.fixtures.foo1_command import Foo1Command
from tests.fixtures.foo2_command import Foo2Command
from tests.fixtures.foo3_command import Foo3Command
from tests.fixtures.foo4_command import Foo4Command
from tests.fixtures.foo_command import FooCommand
from tests.fixtures.foo_sub_namespaced1_command import FooSubNamespaced1Command
from tests.fixtures.foo_sub_namespaced2_command import FooSubNamespaced2Command
Expand Down Expand Up @@ -381,70 +380,3 @@ def test_run_with_input_and_non_interactive(cmd: Command) -> None:

assert status_code == 0
assert tester.io.fetch_output() == "default input\n"


@pytest.mark.parametrize("cmd", (Foo4Command(),))
def test_run_with_logging_integration_normal(cmd: Command) -> None:
app = Application()

app.add(cmd)

tester = ApplicationTester(app)
status_code = tester.execute(f"{cmd.name}")

expected = "This is an warning log record\n" "This is an error log record\n"

assert status_code == 0
assert tester.io.fetch_output() == expected


@pytest.mark.parametrize("cmd", (Foo4Command(),))
def test_run_with_logging_integration_quiet(cmd: Command) -> None:
app = Application()

app.add(cmd)

tester = ApplicationTester(app)
status_code = tester.execute(f"{cmd.name} -q")

assert status_code == 0
assert tester.io.fetch_output() == ""


@pytest.mark.parametrize("cmd", (Foo4Command(),))
def test_run_with_logging_integration_verbose(cmd: Command) -> None:
app = Application()

app.add(cmd)

tester = ApplicationTester(app)
status_code = tester.execute(f"{cmd.name} -v")

expected = (
"This is an info log record\n"
"This is an warning log record\n"
"This is an error log record\n"
)

assert status_code == 0
assert tester.io.fetch_output() == expected


@pytest.mark.parametrize("cmd", (Foo4Command(),))
def test_run_with_logging_integration_very_verbose(cmd: Command) -> None:
app = Application()

app.add(cmd)

tester = ApplicationTester(app)
status_code = tester.execute(f"{cmd.name} -vv")

expected = (
"This is an debug log record\n"
"This is an info log record\n"
"This is an warning log record\n"
"This is an error log record\n"
)

assert status_code == 0
assert tester.io.fetch_output() == expected
76 changes: 76 additions & 0 deletions tests/test_logging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
from __future__ import annotations

from typing import TYPE_CHECKING

import pytest

from cleo.application import Application
from cleo.testers.application_tester import ApplicationTester
from tests.fixtures.foo4_command import Foo4Command


if TYPE_CHECKING:
from cleo.commands.command import Command


@pytest.mark.parametrize("cmd", (Foo4Command(),))
def test_run_with_logging_integration_normal(cmd: Command) -> None:
app = Application()
app.add(cmd)

tester = ApplicationTester(app)
status_code = tester.execute(f"{cmd.name}")

expected = "This is an warning log record\n" "This is an error log record\n"

assert status_code == 0
assert tester.io.fetch_output() == expected


@pytest.mark.parametrize("cmd", (Foo4Command(),))
def test_run_with_logging_integration_quiet(cmd: Command) -> None:
app = Application()
app.add(cmd)

tester = ApplicationTester(app)
status_code = tester.execute(f"{cmd.name} -q")

assert status_code == 0
assert tester.io.fetch_output() == ""


@pytest.mark.parametrize("cmd", (Foo4Command(),))
def test_run_with_logging_integration_verbose(cmd: Command) -> None:
app = Application()
app.add(cmd)

tester = ApplicationTester(app)
status_code = tester.execute(f"{cmd.name} -v")

expected = (
"This is an info log record\n"
"This is an warning log record\n"
"This is an error log record\n"
)

assert status_code == 0
assert tester.io.fetch_output() == expected


@pytest.mark.parametrize("cmd", (Foo4Command(),))
def test_run_with_logging_integration_very_verbose(cmd: Command) -> None:
app = Application()
app.add(cmd)

tester = ApplicationTester(app)
status_code = tester.execute(f"{cmd.name} -vv")

expected = (
"This is an debug log record\n"
"This is an info log record\n"
"This is an warning log record\n"
"This is an error log record\n"
)

assert status_code == 0
assert tester.io.fetch_output() == expected

0 comments on commit 0846085

Please sign in to comment.