Skip to content

Commit

Permalink
Added docstrings, linter and type fixes
Browse files Browse the repository at this point in the history
- Changed ruff's docstring style to "google"
- Removed ignored ruff rules, that are already satisfied
- Ignore missing cls annotations (ANN102)
- Added docstrings to Config and ConfigBase (+Engine)
  • Loading branch information
Root-Core committed Dec 17, 2024
1 parent d738146 commit ad470d5
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 20 deletions.
2 changes: 1 addition & 1 deletion .github/scripts/check_verbs.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
import re
import subprocess

from typing import Generator
from pathlib import Path
from tempfile import mkdtemp
from collections.abc import Generator

# 'gui' is a virtual verb for opening the Winetricks GUI
# 'vd=1280x720' is a setting for the virtual desktop and valid
Expand Down
18 changes: 18 additions & 0 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,32 @@
from pathlib import Path

class Config(ConfigBase):
"""Configuration for umu-protonfix"""

@dataclass
class MainSection:
"""General parameters
Attributes:
enable_checks (bool): Run checks (`checks.py`) before the fix is executed.
enable_splash (bool): Enables splash screen, that is shown while a fix is executed.
enable_global_fixes (bool): Enables included fixes. If deactivated, only local fixes (`~/.config/protonfixes/localfixes`) are executed.
"""

enable_checks: bool = True
enable_splash: bool = False
enable_global_fixes: bool = True

@dataclass
class PathSection:
"""Path parameters
Attributes:
cache_dir (Path): The path that should be used to create temporary and cached files.
"""

cache_dir: Path = Path.home() / '.cache/protonfixes'

main: MainSection
Expand Down
57 changes: 54 additions & 3 deletions config_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import re

from configparser import ConfigParser
from dataclasses import dataclass, is_dataclass
from dataclasses import is_dataclass
from pathlib import Path

from typing import Any
Expand All @@ -12,11 +12,25 @@
from logger import log, LogLevelType

class ConfigBase:
"""Base class for configuration objects.
This reflects a given config file and populates the object with it's values.
It also injects attributes from the sub classes, this isn't compatible with static type checking though.
You can define the attributes accordingly to satisfy type checkers.
"""

__CAMEL_CASE_PATTERN: re.Pattern = re.compile('((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))')

@classmethod
def snake_case(cls, input: str) -> str:
# Convert CamelCase to snake_case
"""Converts CamelCase to snake_case.
Args:
input (str): The string to convert.
Returns:
str: The converted string.
"""
return cls.__CAMEL_CASE_PATTERN.sub(r'_\1', input).lower()


Expand All @@ -26,6 +40,17 @@ def __log(message: str, level: LogLevelType = 'INFO') -> None:


def __init__(self, path: Path) -> None:
"""Initialize the instance from a given config file.
Defaults will be used if the file doesn't exist.
The file will also be created in this case.
Args:
path (Path): The reflected config file's path.
Raises:
IsADirectoryError: If the path exists, but isn't a file.
"""
assert path
if path.is_file():
self.parse_config_file(path)
Expand All @@ -37,6 +62,14 @@ def __init__(self, path: Path) -> None:


def init_sections(self, force: bool = False) -> None:
"""Find sub-classes and initialize them as attributes.
Sub-classes are initialized and injected as attributes.
Example: `MainSection` will be injected as `main` to the config (this) object.
Args:
force (bool, optional): Force initialization? This results in a reset. Defaults to False.
"""
for (member_name, member) in self.__class__.__dict__.items():
# Find non private section definitions
if not member_name.endswith('Section') or member_name.startswith('_'):
Expand All @@ -57,6 +90,16 @@ def init_sections(self, force: bool = False) -> None:


def parse_config_file(self, file: Path) -> bool:
"""Parse a config file.
This resets the data in the sections, regardless if the file exists or is loaded.
Args:
file (Path): The reflected config file's path.
Returns:
bool: True, if the config file was successfully loaded.
"""
# Initialize / reset sections to defaults
self.init_sections(True)

Expand Down Expand Up @@ -88,7 +131,7 @@ def _get_parse_function(type_name: str) -> Callable[[str], Any]:
}.get(type_name, None)
if not value:
value = parser_items.get
self.__log(f'Unknown type "{type_name}", falling back to "str".')
self.__log(f'Unknown type "{type_name}", falling back to "str".', 'WARN')
return value

# Iterate over the option objects in this section
Expand All @@ -105,6 +148,14 @@ def _get_parse_function(type_name: str) -> Callable[[str], Any]:


def write_config_file(self, file: Path) -> bool:
"""Write the current config to a file.
Args:
file (Path): The file path to write to.
Returns:
bool: True, if the file was successfully written.
"""
# Only precede if the parent directory exists
if not file.parent.is_dir():
self.__log(f'Parent directory "{file.parent}" does not exist. Abort.', 'WARN')
Expand Down
3 changes: 2 additions & 1 deletion engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
class Engine:
"""Game engines"""

def __init__(self) -> None: # noqa: D107
def __init__(self) -> None:
"""Trys to figure out which engine is used in the current game"""
self.engine_name = ''
self.supported = {
'Dunia 2': 'https://pcgamingwiki.com/wiki/Engine:Dunia_2',
Expand Down
22 changes: 9 additions & 13 deletions ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,8 @@ select = [
"D"
]
ignore = [
# Requiring a type for self is deprecated
"ANN101",
# Ignore starting with 'This'
"D404",
# Ignore imperative mood
"D401",
# Ignore period endings
"D400",
# Ignore punctuation
"D415",
# Ignore '1 blank line required before class docstring'
"D203",
# Ignore 'Multi-line docstring summary should start at the second line'
"D213",
# Ignore 'Missing docstring in public package'
"D104",
]
Expand All @@ -82,9 +70,17 @@ unfixable = []
# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"

[lint.pydocstyle]
convention = "google"

[lint.per-file-ignores]
# Relax docstring-related lint rules for gamefixes
"gamefixes-*" = ["D103", "D205"]
"gamefixes-*" = [
# Ignore 'Missing docstring in public function'
"D103",
# Ignore '1 blank line required between summary line and description'
"D205"
]

[format]
# Like Black, use double quotes for strings.
Expand Down
4 changes: 2 additions & 2 deletions util.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
from io import TextIOWrapper
from pathlib import Path
from socket import socket, AF_INET, SOCK_DGRAM
from typing import Literal, Any, Callable, Union, Optional
from collections.abc import Mapping, Generator
from typing import Literal, Any, Union, Optional
from collections.abc import Mapping, Generator, Callable

try:
from .logger import log
Expand Down

0 comments on commit ad470d5

Please sign in to comment.