Skip to content

Commit

Permalink
Require python 3.9 or newer (#67)
Browse files Browse the repository at this point in the history
  • Loading branch information
ssbarnea authored Jun 14, 2024
1 parent 2ac36f4 commit 4dc49d0
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 58 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ jobs:
TOX_PARALLEL_NO_SPINNER: 1

steps:
- name: Switch to using Python 3.8 by default
- name: Switch to using Python 3.9 by default
uses: actions/setup-python@v2
with:
python-version: 3.8
python-version: 3.9
- name: Install tox
run: >-
python3 -m
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/tox.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ jobs:
id: generate_matrix
uses: coactions/dynamic-matrix@v1
with:
min_python: "3.9"
platforms: linux,macos
macos: minmax
other_names: |
lint
pkg
Expand Down
33 changes: 12 additions & 21 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,51 +6,42 @@ exclude: |
repos:
- repo: https://github.com/pycontribs/mirrors-prettier
# keep it before yamllint
rev: v3.3.1
rev: v3.3.2
hooks:
- id: prettier
always_run: true
additional_dependencies:
- prettier
- prettier-plugin-toml
- prettier-plugin-sort-json
- repo: https://github.com/PyCQA/isort
rev: 5.13.2
hooks:
- id: isort
- repo: https://github.com/psf/black
rev: 24.4.2
hooks:
- id: black
language_version: python3
- repo: https://github.com/pre-commit/pre-commit-hooks.git
rev: v4.6.0
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace
- id: mixed-line-ending
- id: check-byte-order-marker
- id: fix-byte-order-marker
- id: check-executables-have-shebangs
- id: check-merge-conflict
- id: debug-statements
language_version: python3
- repo: https://github.com/PyCQA/flake8
rev: 7.0.0
hooks:
- id: flake8
additional_dependencies:
- pydocstyle>=5.1.1
- flake8-absolute-import
- flake8-black>=0.1.1
- flake8-docstrings>=1.5.0
language_version: python3
- repo: https://github.com/adrienverge/yamllint.git
rev: v1.35.1
hooks:
- id: yamllint
files: \.(yaml|yml)$
types: [file, yaml]
entry: yamllint --strict
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: "v0.4.7"
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- repo: https://github.com/psf/black
rev: 24.4.2
hooks:
- id: black
language_version: python3
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.10.0
hooks:
Expand Down
25 changes: 22 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"

[project]
# https://peps.python.org/pep-0621/#readme
requires-python = ">=3.8"
requires-python = ">=3.9"
dynamic = ["version", "dependencies", "optional-dependencies"]
name = "enrich"
description = "enrich"
Expand All @@ -29,7 +29,6 @@ classifiers = [
"Operating System :: POSIX :: Linux",
"Operating System :: POSIX",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
Expand All @@ -52,7 +51,7 @@ profile = "black"
known_first_party = "subprocess_tee"

[tool.mypy]
python_version = 3.8
python_version = 3.9
color_output = true
error_summary = true
disallow_any_generics = true
Expand All @@ -73,6 +72,26 @@ filterwarnings = [
# ignore::UserWarning
]

[tool.ruff]
target-version = "py39"
# Same as Black.
line-length = 88

lint.ignore = [
"D203", # incompatible with D211
"D213", # incompatible with D212
"E501", # we use black
"ANN",
"FBT001",
"FBT002",
"FBT003",
"PGH",
]
lint.select = ["ALL"]

[tool.ruff.lint.per-file-ignores]
"test/**/*.py" = ["D", "ERA", "S"]

[tool.setuptools.dynamic]
dependencies = { file = [".config/requirements.in"] }
optional-dependencies.test = { file = [".config/requirements-test.in"] }
Expand Down
1 change: 1 addition & 0 deletions src/enrich/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Enrich module."""
7 changes: 4 additions & 3 deletions src/enrich/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ class Console(rich_console.Console):
"""Extends rich Console class."""

def __init__(self, *args: str, redirect: bool = True, **kwargs: Any) -> None:
"""
enrich console does soft-wrapping by default and this diverge from
"""Enrich constructor.
Enrich console does soft-wrapping by default and this diverge from
original rich console which does not, creating hard-wraps instead.
"""
self.redirect = redirect
Expand All @@ -26,7 +27,7 @@ def __init__(self, *args: str, redirect: bool = True, **kwargs: Any) -> None:
# heuristic to make an informed decision.
if "force_terminal" not in kwargs:
kwargs["force_terminal"] = should_do_markup(
stream=kwargs.get("file", sys.stdout)
stream=kwargs.get("file", sys.stdout),
)

super().__init__(*args, **kwargs)
Expand Down
40 changes: 24 additions & 16 deletions src/enrich/logging.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
"""Implements enriched RichHandler"""
"""Implements enriched RichHandler."""

from datetime import datetime
from typing import TYPE_CHECKING, Any, Iterable, Optional
from __future__ import annotations

from datetime import datetime, timezone
from typing import TYPE_CHECKING, Any

from rich.logging import RichHandler as OriginalRichHandler
from rich.text import Text, TextType

if TYPE_CHECKING:
from collections.abc import Iterable

from rich.console import Console, ConsoleRenderable


Expand All @@ -15,36 +19,38 @@ class FluidLogRender: # pylint: disable=too-few-public-methods
"""Renders log by not using columns and avoiding any wrapping."""

# pylint: disable=too-many-arguments
def __init__(
def __init__( # noqa: PLR0913
self,
show_time: bool = False,
show_level: bool = False,
show_path: bool = True,
time_format: str = "[%x %X]",
omit_repeated_times: bool = True,
) -> None:
"""Construcs instance."""
self.show_time = show_time
self.show_level = show_level
self.show_path = show_path
self.time_format = time_format
self.omit_repeated_times = omit_repeated_times
self._last_time: Optional[str] = None
self._last_time: str | None = None

def __call__( # pylint: disable=too-many-arguments
def __call__( # pylint: disable=too-many-arguments # noqa: PLR0913
self,
console: "Console",
renderables: Iterable["ConsoleRenderable"],
log_time: Optional[datetime] = None,
time_format: Optional[str] = None,
console: Console, # noqa: ARG002
renderables: Iterable[ConsoleRenderable],
log_time: datetime | None = None,
time_format: str | None = None,
level: TextType = "",
path: Optional[str] = None,
line_no: Optional[int] = None,
link_path: Optional[str] = None,
path: str | None = None,
line_no: int | None = None,
link_path: str | None = None,
) -> Text:
"""Call."""
result = Text()
if self.show_time:
if log_time is None:
log_time = datetime.now()
log_time = datetime.now(tz=timezone.utc)
log_time_display = log_time.strftime(time_format or self.time_format) + " "
if self.omit_repeated_times and log_time_display == self._last_time:
result += Text(" " * len(log_time_display))
Expand All @@ -55,7 +61,7 @@ def __call__( # pylint: disable=too-many-arguments
if not isinstance(level, Text):
level = Text(level)
# CRITICAL is the longest identifier from default set.
if len(level) < 9:
if len(level) < 9: # noqa: PLR2004
level += " " * (9 - len(level))
result += level

Expand All @@ -65,7 +71,8 @@ def __call__( # pylint: disable=too-many-arguments
if self.show_path and path:
path_text = Text(" ", style="repr.filename")
path_text.append(
path, style=f"link file://{link_path}" if link_path else ""
path,
style=f"link file://{link_path}" if link_path else "",
)
if line_no:
path_text.append(f":{line_no}")
Expand All @@ -78,6 +85,7 @@ class RichHandler(OriginalRichHandler):
"""Enriched handler that does not wrap."""

def __init__(self, *args: Any, **kwargs: Any) -> None:
"""Create the handler."""
super().__init__(*args, **kwargs)
# RichHandler constructor does not allow custom renderer
# https://github.com/willmcgugan/rich/issues/438
Expand Down
15 changes: 9 additions & 6 deletions test/test_console.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,28 @@
import sys

import pytest
from pytest_mock import MockFixture

from enrich.console import Console, should_do_markup
from pytest_mock import MockFixture # pylint: disable=wrong-import-order


def test_rich_console_ex() -> None:
"""Validate that ConsoleEx can capture output from print() calls."""
console = Console(record=True, redirect=True)
console.print("alpha")
print("beta")
print("beta") # noqa: T201
sys.stdout.write("gamma\n")
sys.stderr.write("delta\n")
# While not supposed to happen we want to be sure that this will not raise
# an exception. Some libraries may still sometimes send bytes to the
# streams, notable example being click.
# sys.stdout.write(b"epsilon\n") # type: ignore
# sys.stdout.write(b"epsilon\n")
text = console.export_text()
assert text == "alpha\nbeta\ngamma\ndelta\n"


def test_rich_console_ex_ansi() -> None:
"""Validate that ANSI sent to sys.stdout does not become garbage in record."""
print()
print() # noqa: T201
console = Console(force_terminal=True, record=True, redirect=True)
console.print("[green]this from Console.print()[/green]", style="red")

Expand All @@ -40,7 +39,11 @@ def test_rich_console_ex_ansi() -> None:
def test_console_soft_wrap() -> None:
"""Assures long prints on console are not wrapped when requested."""
console = Console(
file=io.StringIO(), width=20, record=True, soft_wrap=True, redirect=False
file=io.StringIO(),
width=20,
record=True,
soft_wrap=True,
redirect=False,
)
text = 21 * "x"
console.print(text, end="")
Expand Down
16 changes: 9 additions & 7 deletions test/test_logging.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
"""Tests related to enriched RichHandler"""

from __future__ import annotations

import io
import logging
import re
from typing import Tuple, Union

import pytest

from enrich.console import Console
from enrich.logging import RichHandler


def strip_ansi_escape(text: Union[str, bytes]) -> str:
def strip_ansi_escape(text: str | bytes) -> str:
"""Remove all ANSI escapes from string or bytes.
If bytes is passed instead of string, it will be converted to string
Expand All @@ -24,7 +24,7 @@ def strip_ansi_escape(text: Union[str, bytes]) -> str:


@pytest.fixture(name="rich_logger")
def rich_logger_fixture() -> Tuple[logging.Logger, RichHandler]:
def rich_logger_fixture() -> tuple[logging.Logger, RichHandler]:
"""Returns tuple with logger and handler to be tested."""
rich_handler = RichHandler(
console=Console(
Expand All @@ -38,16 +38,18 @@ def rich_logger_fixture() -> Tuple[logging.Logger, RichHandler]:
)

logging.basicConfig(
level="NOTSET", format="%(message)s", datefmt="[DATE]", handlers=[rich_handler]
level="NOTSET",
format="%(message)s",
datefmt="[DATE]",
handlers=[rich_handler],
)
rich_log = logging.getLogger("rich")
rich_log.addHandler(rich_handler)
return (rich_log, rich_handler)


def test_logging(rich_logger: Tuple[logging.Logger, RichHandler]) -> None:
def test_logging(rich_logger: tuple[logging.Logger, RichHandler]) -> None:
"""Test that logger does not wrap."""

(logger, rich_handler) = rich_logger

text = 10 * "x" # a long text that would likely wrap on a normal console
Expand Down

0 comments on commit 4dc49d0

Please sign in to comment.