Skip to content

Commit

Permalink
Update utilities module [add event handler logging]
Browse files Browse the repository at this point in the history
  • Loading branch information
CoolCat467 committed Oct 13, 2024
1 parent 9d5c900 commit c4ccfea
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 16 deletions.
8 changes: 4 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ ci:

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
Expand All @@ -28,17 +28,17 @@ repos:
types: [file]
types_or: [python, pyi]
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 24.4.2
rev: 24.10.0
hooks:
- id: black
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.0
rev: v0.6.9
hooks:
- id: ruff
types: [file]
types_or: [python, pyi, toml]
- repo: https://github.com/CoolCat467/badgie
rev: v0.9.5
rev: v0.9.6
hooks:
- id: badgie
- repo: https://github.com/codespell-project/codespell
Expand Down
7 changes: 2 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@ authors = [
description = "Emacs Align by Regular Expression for IDLE"
readme = {file = "README.md", content-type = "text/markdown"}
license = {file = "LICENSE"}
requires-python = ">=3.8"
requires-python = ">=3.9"
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"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 Down Expand Up @@ -110,7 +109,6 @@ select = [
"SIM", # flake8-simplify
"SLOT", # flake8-slots
"TCH", # flake8-type-checking
"TRIO", # flake8-trio
"UP", # pyupgrade
"W", # Warning
"YTT", # flake8-2020
Expand Down Expand Up @@ -170,12 +168,11 @@ partial_branches = [
[tool.tox]
legacy_tox_ini = """
[tox]
envlist = py38, py39, py310, py311, py312, mypy, pytest
envlist = py39, py310, py311, py312, mypy, pytest
isolated_build = false
[gh-actions]
python =
3.8: py38, mypy, pytest
3.9: py39, mypy, pytest
3.10: py310, mypy, pytest
3.11: py311, mypy, pytest
Expand Down
1 change: 1 addition & 0 deletions src/idlealign/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def check_installed() -> bool:
return utils.check_installed(__title__, __version__, idlealign)


utils.set_title(__title__)
idlealign.reload()


Expand Down
19 changes: 14 additions & 5 deletions src/idlealign/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
from re import Pattern


class AlignDialog(SearchDialogBase): # type: ignore[misc]
class AlignDialog(SearchDialogBase):
"""Dialog for aligning by a pattern in text."""

__slots__ = (
Expand Down Expand Up @@ -100,7 +100,7 @@ def store_prefs(self) -> None:
self.search_params = utils.get_search_engine_params(self.engine)
utils.set_search_engine_params(self.engine, self.global_search_params)

def open(
def open( # type: ignore # "override"
self,
searchphrase: str | None = None,
insert_tags: str | list[str] | tuple[str, ...] = (),
Expand Down Expand Up @@ -173,7 +173,7 @@ def default_command(self, _event: Event[Any] | None = None) -> bool:

pattern = self.engine.getprog()
if not pattern:
return False
return False # type: ignore # "unreachable"

space_wrap: bool = self.space_wrap_var.get()
align_side: bool = self.align_side_var.get()
Expand Down Expand Up @@ -220,6 +220,7 @@ def window(self) -> AlignDialog:
"""Window for current text widget."""
return self.create_window()

@utils.log_exceptions
def create_window(self) -> AlignDialog:
"""Create align dialog window."""
root: Tk
Expand All @@ -228,9 +229,17 @@ def create_window(self) -> AlignDialog:
engine: searchengine.SearchEngine = searchengine.get(root)

if not hasattr(engine, "_aligndialog"):
engine._aligndialog = AlignDialog(root, engine, self)
return cast(AlignDialog, engine._aligndialog)
engine._aligndialog = AlignDialog( # type: ignore[attr-defined]
root,
engine,
self,
)
return cast(
AlignDialog,
engine._aligndialog, # type: ignore[attr-defined]
)

@utils.log_exceptions
def align_selection(
self,
selection: tuple[str, str],
Expand Down
47 changes: 45 additions & 2 deletions src/idlealign/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,49 @@
__license__ = "GNU General Public License Version 3"

import sys
import time
import traceback
from contextlib import contextmanager
from functools import wraps
from idlelib import search, searchengine
from idlelib.config import idleConf
from os.path import abspath
from pathlib import Path
from tkinter import TclError, Text, Tk, messagebox
from typing import TYPE_CHECKING, ClassVar, NamedTuple
from typing import TYPE_CHECKING, ClassVar, NamedTuple, TypeVar

if TYPE_CHECKING:
from collections.abc import Generator, Sequence
from collections.abc import Callable, Generator, Sequence
from idlelib.editor import EditorWindow
from idlelib.format import FormatRegion
from idlelib.iomenu import IOBinding
from idlelib.pyshell import PyShellEditorWindow, PyShellFileList
from idlelib.undo import UndoDelegator

from typing_extensions import ParamSpec

PS = ParamSpec("PS")

T = TypeVar("T")

LOGS_PATH = Path(idleConf.userdir) / "logs"
TITLE: str = __title__


def set_title(title: str) -> None:
"""Set program title."""
global TITLE
TITLE = title


def get_required_config(
values: dict[str, str],
bind_defaults: dict[str, str],
extension_title: str,
) -> str:
"""Get required configuration file data."""
if __title__ == TITLE:
set_title(extension_title)
config = ""
# Get configuration defaults
settings = "\n".join(
Expand Down Expand Up @@ -309,6 +330,28 @@ def undo_block(undo: UndoDelegator) -> Generator[None, None, None]:
undo.undo_block_stop()


def log_exceptions(function: Callable[PS, T]) -> Callable[PS, T]:
"""Log any exceptions raised."""

@wraps(function)
def wrapper(*args: PS.args, **kwargs: PS.kwargs) -> T:
"""Catch Exceptions, log them to log file, and re-raise."""
try:
return function(*args, **kwargs)
except Exception as exc:
if not LOGS_PATH.exists():
LOGS_PATH.mkdir(exist_ok=True)
log_file = LOGS_PATH / f"{TITLE}.log"
with log_file.open("a", encoding="utf-8") as fp:
format_time = time.strftime("[%Y-%m-%d %H:%M:%S] ")
exception_text = "".join(traceback.format_exception(exc))
for line in exception_text.splitlines(keepends=True):
fp.write(f"{format_time}{line}")
raise

return wrapper


class Comment(NamedTuple):
"""Represents one comment."""

Expand Down
1 change: 1 addition & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import pytest

from idlealign import utils


Expand Down

0 comments on commit c4ccfea

Please sign in to comment.