Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 15 additions & 16 deletions src/tctools/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
import sys
from abc import ABC, abstractmethod
from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser, Namespace
from collections.abc import Generator
from pathlib import Path
from typing import Any, Dict, Generator, List, Optional
from typing import Any

from lxml import etree

Expand All @@ -20,7 +21,7 @@


# The result of a files argument - like: `provided_path: [files]`
FileGroups = Dict[Path, List[Path]]
FileGroups = dict[Path, list[Path]]


# Path.glob() only allows symlink recursion from Python 3.13:
Expand All @@ -33,14 +34,14 @@ class Tool(ABC):
``argparse`` is done in the constructor, CLI arguments should be passed there.
"""

LOGGER_NAME: Optional[str] = None
LOGGER_NAME: str | None = None

# Default value for file filter argument:
FILTER_DEFAULT: List[str]
FILTER_DEFAULT: list[str]

CONFIG_KEY: Optional[str] = None
CONFIG_KEY: str | None = None

PATH_VARIABLES: List[str] = [] # Names of options that are considered file paths
PATH_VARIABLES: list[str] = [] # Names of options that are considered file paths

def __init__(self, *args):
"""Pass e.g. ``sys.args[1:]`` (skipping the script part of the arguments).
Expand All @@ -54,7 +55,7 @@ def __init__(self, *args):
action.dest for action in parser._actions if action.dest != "help" # noqa
}

self.config_file: Optional[Path] = None
self.config_file: Path | None = None

config = self.make_config()
if self.CONFIG_KEY:
Expand Down Expand Up @@ -114,7 +115,7 @@ def set_arguments(cls, parser):
default="INFO",
)

def make_config(self) -> Dict[str, Any]:
def make_config(self) -> dict[str, Any]:
"""Get configuration from possible files."""
config = {}
self.config_file = self._find_files_upwards(
Expand All @@ -131,9 +132,7 @@ def make_config(self) -> Dict[str, Any]:
return config

@classmethod
def _find_files_upwards(
cls, directory: Path, filenames: List[str]
) -> Optional[Path]:
def _find_files_upwards(cls, directory: Path, filenames: list[str]) -> Path | None:
"""Find a file with a given name in the directory or it's parents.

First hit on any of the filenames is returned.
Expand Down Expand Up @@ -194,7 +193,7 @@ def __init__(self, *args):
# Preserve `CDATA` XML flags:
self.xml_parser = etree.XMLParser(strip_cdata=False)

self.header_before: Optional[str] = None # Header of the last XML path
self.header_before: str | None = None # Header of the last XML path

self.files_checked = 0 # Files read by parser
self.files_to_alter = 0 # Files that seem to require changes
Expand Down Expand Up @@ -242,7 +241,7 @@ def set_main_argument(cls, parser):
)

@staticmethod
def get_xml_header(file: str) -> Optional[str]:
def get_xml_header(file: str) -> str | None:
"""Get raw XML header as string."""
with open(file, "r") as fh:
# Search only the start of the path, otherwise give up
Expand All @@ -264,8 +263,8 @@ def get_xml_tree(self, path: str | Path) -> ElementTree:
@classmethod
def find_files(
cls,
targets: str | List[str],
filters: None | List[str] = None,
targets: str | list[str],
filters: None | list[str] = None,
recursive: bool = True,
skip_check: bool = False,
) -> FileGroups:
Expand All @@ -292,7 +291,7 @@ def find_files(
if isinstance(targets, (str, Path)):
targets = [targets]

def add_file(g: List[Path], f: Path):
def add_file(g: list[Path], f: Path):
"""Little local method to prevent duplicate paths."""
if f not in files_unique:
files_unique.add(f)
Expand Down
27 changes: 13 additions & 14 deletions src/tctools/format/format_class.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import re
from collections import OrderedDict
from typing import List, Optional, Tuple, Type

from editorconfig import get_properties

Expand All @@ -16,8 +15,8 @@
FormatVariablesAlign,
)

RowCol = Tuple[int, int]
Segment = Tuple[Kind, List[str], str]
RowCol = tuple[int, int]
Segment = tuple[Kind, list[str], str]


class XmlMachine:
Expand All @@ -31,9 +30,9 @@ def __init__(self):
self._row = 0 # Line number inside path
self._col = 0 # Position inside line

self.regions: List[Tuple[RowCol, Kind, str]] = []
self.regions: list[tuple[RowCol, Kind, str]] = []

def parse(self, content: List[str]):
def parse(self, content: list[str]):
"""Progress machine line by line."""
self._kind = Kind.XML
self._row = 0
Expand Down Expand Up @@ -97,7 +96,7 @@ class Formatter(TcTool):

CONFIG_KEY = "format"

_RULE_CLASSES: List[Type[FormattingRule]] = []
_RULE_CLASSES: list[type[FormattingRule]] = []

def __init__(self, *args):
super().__init__(*args)
Expand All @@ -106,7 +105,7 @@ def __init__(self, *args):
# them between methods
self._file = ""
self._properties = OrderedDict()
self._rules: List[FormattingRule] = []
self._rules: list[FormattingRule] = []

self._number_corrections = 0 # Track number of changes for the current file

Expand All @@ -119,7 +118,7 @@ def set_arguments(cls, parser):
return parser

@classmethod
def register_rule(cls, new_rule: Type[FormattingRule]):
def register_rule(cls, new_rule: type[FormattingRule]):
"""Incorporate a new formatting rule (accounting for its priority)."""
cls._RULE_CLASSES.append(new_rule)
sorted(cls._RULE_CLASSES, key=lambda item: item.PRIORITY)
Expand Down Expand Up @@ -170,7 +169,7 @@ def format_file(self, path: str):
if rule.WHOLE_FILE:
self.apply_rule(rule, content)

segments: List[Segment] = list(self.split_code_segments(content))
segments: list[Segment] = list(self.split_code_segments(content))

for kind, segment, _ in segments:
# Changes are done in-place
Expand All @@ -188,7 +187,7 @@ def format_file(self, path: str):
self.files_resaved += 1

@staticmethod
def split_code_segments(content: List[str]):
def split_code_segments(content: list[str]):
"""Copy content, split into XML and code sections.

Function is a generator, each pair is yielded.
Expand All @@ -197,7 +196,7 @@ def split_code_segments(content: List[str]):
directly, without extra newlines.

:param: File content as list
:return: List[Segment]
:return: list[Segment]
"""
if not content:
return # Nothing to yield
Expand Down Expand Up @@ -230,11 +229,11 @@ def split_code_segments(content: List[str]):

yield kind_prev, lines, name_prev

def format_segment(self, content: List[str], kind: Kind):
def format_segment(self, content: list[str], kind: Kind):
"""Format a specific segment of code.

:param content: Text to reformat (changed in place!)
:param kind: Type of the content
:param kind: type of the content
"""
if kind == Kind.XML:
return # Do nothing
Expand All @@ -243,7 +242,7 @@ def format_segment(self, content: List[str], kind: Kind):
if not rule.WHOLE_FILE: # Skip otherwise
self.apply_rule(rule, content, kind)

def apply_rule(self, rule, content, kind: Optional[Kind] = None):
def apply_rule(self, rule, content, kind: Kind | None = None):
"""Run a rule over some content and handle results."""
rule.format(content, kind)
corrections = rule.consume_corrections()
Expand Down
39 changes: 20 additions & 19 deletions src/tctools/format/format_rules.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import math
import re
from abc import ABC, abstractmethod
from typing import Any, Dict, List, Optional, OrderedDict, Tuple, Type
from collections import OrderedDict
from typing import Any

from .format_extras import Kind

Correction = Tuple[int, str]
Correction = tuple[int, str]


class FormattingRule(ABC):
Expand All @@ -23,7 +24,7 @@ class FormattingRule(ABC):

def __init__(self, properties: OrderedDict):
self._properties = properties
self._corrections: List[Correction] = []
self._corrections: list[Correction] = []

# Universal properties:

Expand All @@ -34,13 +35,13 @@ def __init__(self, properties: OrderedDict):
"tab_width", default=self._indent_size, value_type=int
)

self._indent_style: Optional[str] = self._properties.get("indent_style", None)
self._indent_style: str | None = self._properties.get("indent_style", None)

self._indent_str: str = " " * self._indent_size
if self._indent_style and self._indent_style == "tab":
self._indent_str = "\t"

self._end_of_line: Optional[str] = self._properties.get("end_of_line", None)
self._end_of_line: str | None = self._properties.get("end_of_line", None)
options = {"lf": "\n", "cr": "\r", "crlf": "\r\n"}
self._line_ending: str = options.get(self._end_of_line, "\n")

Expand All @@ -58,7 +59,7 @@ def get_property(
self,
name: str,
default: Any = None,
value_type: Optional[Type] = None,
value_type: type | None = None,
) -> Any:
"""Get item from ``_properties``, parsing as needed.

Expand All @@ -81,7 +82,7 @@ def get_property(
return value_type(value)

@abstractmethod
def format(self, content: List[str], kind: Optional[Kind] = None):
def format(self, content: list[str], kind: Kind | None = None):
"""Fun rule to format text.

:param content: Text to format (changed in place!)
Expand All @@ -96,7 +97,7 @@ def add_correction(self, message: str, line_nr: int):
"""
self._corrections.append((line_nr, message))

def consume_corrections(self) -> List[Correction]:
def consume_corrections(self) -> list[Correction]:
"""Return listed corrections and reset list."""
corrections = self._corrections
self._corrections = []
Expand All @@ -112,7 +113,7 @@ class FormatTabs(FormattingRule):
def __init__(self, *args):
super().__init__(*args)

def format(self, content: List[str], kind: Optional[Kind] = None):
def format(self, content: list[str], kind: Kind | None = None):
if self._indent_style == "tab":
re_search = self._re_spaces
elif self._indent_style == "space":
Expand Down Expand Up @@ -165,7 +166,7 @@ def __init__(self, *args):
"trim_trailing_whitespace", False, value_type=bool
)

def format(self, content: List[str], kind: Optional[Kind] = None):
def format(self, content: list[str], kind: Kind | None = None):
if not self._remove_tr_ws:
return # Nothing to do
for i, line in enumerate(content):
Expand All @@ -185,7 +186,7 @@ def __init__(self, *args):
"insert_final_newline", False, value_type=bool
)

def format(self, content: List[str], kind: Optional[Kind] = None):
def format(self, content: list[str], kind: Kind | None = None):
if not self._insert_final_newline:
return

Expand Down Expand Up @@ -232,7 +233,7 @@ def __init__(self, *args):
else:
raise ValueError(f"Unrecognized file ending `{self._line_ending}`")

def format(self, content: List[str], kind: Optional[Kind] = None):
def format(self, content: list[str], kind: Kind | None = None):
if self._end_of_line is None:
return # Nothing specified

Expand Down Expand Up @@ -279,7 +280,7 @@ def __init__(self, *args):

self._re_newlines = re.compile(r"[\r\n]+$")

def format(self, content: List[str], kind: Optional[Kind] = None):
def format(self, content: list[str], kind: Kind | None = None):
if not self._align:
return # Disabled by config

Expand All @@ -288,14 +289,14 @@ def format(self, content: List[str], kind: Optional[Kind] = None):

self.format_argument_list(content)

def format_argument_list(self, content: List[str]):
def format_argument_list(self, content: list[str]):
"""Format entire declaration section"""

# Get variable definitions, split up and keyed by content index:
variable_definitions: Dict[int, List[Optional[str]]] = {}
variable_definitions: dict[int, list[str]] | None = {}

# Biggest size of each chunk across all lines:
max_chunk_sizes: List[Optional[int]] = [None] * 3
max_chunk_sizes: list[int | None] = [None] * 3

for i, line in enumerate(content):
match = self._re_variable.match(line)
Expand Down Expand Up @@ -414,7 +415,7 @@ def __init__(self, *args):
re.VERBOSE | re.MULTILINE,
)

def format(self, content: List[str], kind: Optional[Kind] = None):
def format(self, content: list[str], kind: Kind | None = None):
if self._parentheses is None:
return # Nothing to do

Expand Down Expand Up @@ -465,12 +466,12 @@ def format(self, content: List[str], kind: Optional[Kind] = None):
@staticmethod
def find_and_match_braces(
text: str, brace_left: str = "(", brace_right: str = ")"
) -> Tuple[int, int]:
) -> tuple[int, int]:
"""Step through braces in a string.

Note that levels can step into negative.

:return: Tuple of (strpos, level), where strpos is the zero-index position of
:return: tuple of (strpos, level), where strpos is the zero-index position of
the brace itself and level is the nested level it indicates
"""
level = 0
Expand Down
3 changes: 1 addition & 2 deletions src/tctools/git_info/git_info_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from datetime import datetime
from logging import Logger
from pathlib import Path
from typing import Optional, Set

from git import GitCommandError, Repo

Expand All @@ -22,7 +21,7 @@ class GitSetter:
_DATETIME_FORMAT = "%d-%m-%Y %H:%M:%S"

def __init__(
self, repo: Repo, logger: Logger, tolerate_dirty: Optional[Set[Path]] = None
self, repo: Repo, logger: Logger, tolerate_dirty: set[Path] | None = None
):
self._repo: Repo = repo
self._logger = logger
Expand Down
Loading