Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: prefer pathlib when possible #183

Open
wants to merge 33 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
7cc7b8e
ruff: add PTH rule
R1kaB3rN Dec 19, 2024
27c12c0
checks: refactor to use pathlib
R1kaB3rN Dec 19, 2024
694095b
config: refactor to use pathlib
R1kaB3rN Dec 19, 2024
4d963e7
download: refactor to use pathlib
R1kaB3rN Dec 19, 2024
de0138d
engine: refactor to use pathlib
R1kaB3rN Dec 19, 2024
6818b6b
fix: refactor to use pathlib
R1kaB3rN Dec 19, 2024
4493863
logger: refactor to use pathlib
R1kaB3rN Dec 19, 2024
87a9b84
steamhelper: refactor to use pathlib
R1kaB3rN Dec 19, 2024
325242b
util: refactor util.which
R1kaB3rN Dec 19, 2024
3215ea1
util: refactor util.protondir
R1kaB3rN Dec 19, 2024
52a9352
util: refactor util.protonprefix
R1kaB3rN Dec 19, 2024
5d5dbe0
util: refactor util.protontimeversion
R1kaB3rN Dec 19, 2024
e1baf78
util: refactor util.once
R1kaB3rN Dec 19, 2024
ac892cc
util: refactor util._killhanging
R1kaB3rN Dec 19, 2024
32d7d45
util: refactor util._forceinstalled
R1kaB3rN Dec 19, 2024
3a24d9f
util: refactor util._checkinstalled
R1kaB3rN Dec 19, 2024
47cc736
util: refactor util.is_custom_verb
R1kaB3rN Dec 19, 2024
0bd33ac
util: add noqa for util.protontricks
R1kaB3rN Dec 19, 2024
e52d935
util: refactor util.patch_libcuda
R1kaB3rN Dec 19, 2024
45c5dd2
util: refactor util.disable_uplay_overlay
R1kaB3rN Dec 19, 2024
427ea03
util: add noqa for util.create_dosbox_conf
R1kaB3rN Dec 19, 2024
efe0915
util: add noqa for util._get_case_insensitive_name
R1kaB3rN Dec 19, 2024
95b96fa
util: refactor util._get_config_full_path
R1kaB3rN Dec 19, 2024
2a5128e
util: refactor util.set_ini_options
R1kaB3rN Dec 19, 2024
7ec6ed8
util: refactor util.set_xml_options
R1kaB3rN Dec 19, 2024
2492ddf
util: add noqa for util.get_resolution
R1kaB3rN Dec 19, 2024
aae2e64
util: refactor util.set_dxvk_option
R1kaB3rN Dec 19, 2024
5647852
util: refactor util.install_all_from_tgz
R1kaB3rN Dec 19, 2024
b84fa75
util: refactor util.install_from_zip
R1kaB3rN Dec 19, 2024
ab03aaf
util: add noqa for util.is_smt_enabled
R1kaB3rN Dec 19, 2024
290c6d7
util: update format
R1kaB3rN Dec 19, 2024
c1bb4a9
Revert "fix: refactor to use pathlib"
R1kaB3rN Dec 19, 2024
ae0265f
fix: add noqa directives
R1kaB3rN Dec 19, 2024
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
3 changes: 2 additions & 1 deletion checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ def esync_file_limits() -> bool:
https://github.com/zfigura/wine/blob/esync/README.esync
"""

with open('/proc/sys/fs/file-max', encoding='ascii') as fsmax:
# open is OK here
with open('/proc/sys/fs/file-max', encoding='ascii') as fsmax: # noqa: PTH123
max_files = fsmax.readline()
if int(max_files) < 8192:
log.warn(warning)
Expand Down
11 changes: 7 additions & 4 deletions config.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Load configuration settings for protonfixes"""

import os
from configparser import ConfigParser
from pathlib import Path

try:
from .logger import log
Expand All @@ -25,7 +25,7 @@
CONF.read_string(DEFAULT_CONF)

try:
CONF.read(os.path.expanduser(CONF_FILE))
CONF.read(Path(CONF_FILE).expanduser())

except Exception:
log.debug('Unable to read config file ' + CONF_FILE)
Expand All @@ -38,9 +38,12 @@ def opt_bool(opt: str) -> bool:

locals().update({x: opt_bool(y) for x, y in CONF['main'].items() if 'enable' in x})

locals().update({x: os.path.expanduser(y) for x, y in CONF['path'].items()})
locals().update({x: Path(y).expanduser() for x, y in CONF['path'].items()})

try:
[os.makedirs(os.path.expanduser(d)) for n, d in CONF['path'].items()]
[
Path(d).expanduser().mkdir(parents=True, exist_ok=True)
for n, d in CONF['path'].items()
]
except OSError:
pass
9 changes: 5 additions & 4 deletions download.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"""Module with helper functions to download from file-hosting providers"""

import os
import hashlib
import urllib.request
import http.cookiejar
from pathlib import Path


GDRIVE_URL = 'https://drive.google.com/uc?id={}&export=download'
Expand Down Expand Up @@ -36,16 +36,17 @@ def gdrive_download(gdrive_id: str, path: str) -> None:
req = urllib.request.Request(f'{url}&confirm={confirm}')
with urllib.request.urlopen(req, timeout=10) as resp:
filename = get_filename(resp.getheaders())
with open(os.path.join(path, filename), 'wb') as save_file:
with Path(path, filename).open('wb') as save_file:
save_file.write(resp.read())


def sha1sum(filename: str) -> str:
"""Computes the sha1sum of the specified file"""
if not os.path.isfile(filename):
path = Path(filename)
if not path.is_file():
return ''
hasher = hashlib.sha1()
with open(filename, 'rb') as hash_file:
with path.open('rb') as hash_file:
buf = hash_file.read(HASH_BLOCK_SIZE)
while len(buf) > 0:
hasher.update(buf)
Expand Down
9 changes: 4 additions & 5 deletions engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import os
import sys
from .logger import log
from pathlib import Path


class Engine:
Expand Down Expand Up @@ -48,7 +49,7 @@ def _is_unity(self) -> bool:

# Check .../Gamename_Data/Mono/etc/ dir
for data_dir in data_list:
if os.path.exists(os.path.join(os.environ['PWD'], data_dir, 'Mono/etc')):
if Path(os.environ['PWD'], data_dir, 'Mono/etc').exists():
return True

return False
Expand All @@ -60,9 +61,7 @@ def _is_dunia2(self) -> bool:

# Check .../data_win*/worlds/multicommon dir
for data_dir in data_list:
if os.path.exists(
os.path.join(os.environ['PWD'], data_dir, 'worlds/multicommon')
):
if Path(os.environ['PWD'], data_dir, 'worlds/multicommon').exists():
return True

return False
Expand All @@ -75,7 +74,7 @@ def _is_rage(self) -> bool:
# for data_dir in dir_list:
# if os.path.exists(os.path.join(os.environ['PWD'], data_dir, 'pc/data/cdimages')):
# return True
if os.path.exists(os.path.join(os.environ['PWD'], 'pc/data/cdimages')):
if Path(os.environ['PWD'], 'pc/data/cdimages'):
return True

return False
Expand Down
35 changes: 19 additions & 16 deletions fix.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,42 +42,45 @@ def get_game_id() -> str:
def get_game_name() -> str:
"""Trys to return the game name from environment variables"""
pfx = os.environ.get('WINEPREFIX') or protonmain.g_session.env.get('WINEPREFIX')
script_dir = os.path.dirname(os.path.abspath(__file__))
script_dir = os.path.dirname(os.path.abspath(__file__)) # noqa: PTH100, PTH120

if os.environ.get('UMU_ID'):

if os.path.isfile(f'{pfx}/game_title'):
with open(f'{pfx}/game_title', encoding='utf-8') as file:
if os.path.isfile(f'{pfx}/game_title'): # noqa: PTH113
with open(f'{pfx}/game_title', encoding='utf-8') as file: # noqa: PTH123
return file.readline()

umu_id = os.environ['UMU_ID']
store = os.getenv('STORE', 'none')
csv_file_path = os.path.join(script_dir, 'umu-database.csv')
csv_file_path = os.path.join(script_dir, 'umu-database.csv') # noqa: PTH118

try:
with open(csv_file_path, newline='', encoding='utf-8') as csvfile:
with open(csv_file_path, newline='', encoding='utf-8') as csvfile: # noqa: PTH123
csvreader = csv.reader(csvfile)
Copy link
Member Author

@R1kaB3rN R1kaB3rN Dec 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CSV is a database for us so we should handle the CSV by assuming it's very large.

TODO: Refactor to prefer memory mapping the CSV in a future PR.

for row in csvreader:
# Check if the row has enough columns and matches both UMU_ID and STORE
if len(row) > 3 and row[3] == umu_id and row[1] == store:
title = row[0] # Title is the first entry
with open(os.path.join(script_dir, 'game_title'), 'w', encoding='utf-8') as file:
with open( # noqa: PTH123
os.path.join(script_dir, 'game_title'), # noqa: PTH118
'w',
encoding='utf-8',
) as file:
file.write(title)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be a bug. If I'm understanding correctly, we're not writing the title in the user's prefix. Instead, the title's is in the protonfixes directory.

return title
except FileNotFoundError:
log.warn(f"CSV file not found: {csv_file_path}")
log.warn(f'CSV file not found: {csv_file_path}')
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just style changes applied from ruff format.

except Exception as ex:
log.debug(f"Error reading CSV file: {ex}")
log.debug(f'Error reading CSV file: {ex}')

log.warn("Game title not found in CSV")
log.warn('Game title not found in CSV')
return 'UNKNOWN'

try:
log.debug('UMU_ID is not in environment')
game_library = re.findall(r'.*/steamapps', os.environ['PWD'], re.IGNORECASE)[-1]
game_manifest = os.path.join(game_library, f'appmanifest_{get_game_id()}.acf')
game_manifest = os.path.join(game_library, f'appmanifest_{get_game_id()}.acf') # noqa: PTH118

with open(game_manifest, encoding='utf-8') as appmanifest:
with open(game_manifest, encoding='utf-8') as appmanifest: # noqa: PTH123
for xline in appmanifest.readlines():
if 'name' in xline.strip():
name = re.findall(r'"[^"]+"', xline, re.UNICODE)[-1]
Expand Down Expand Up @@ -129,16 +132,16 @@ def get_module_name(game_id: str, default: bool = False, local: bool = False) ->

def _run_fix_local(game_id: str, default: bool = False) -> bool:
"""Check if a local gamefix is available first and run it"""
localpath = os.path.expanduser('~/.config/protonfixes/localfixes')
localpath = os.path.expanduser('~/.config/protonfixes/localfixes') # noqa: PTH111
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactoring this line and below as pathlib.Path objects should be safe.

module_name = game_id if not default else 'default'

# Check if local gamefix exists
if not os.path.isfile(os.path.join(localpath, module_name + '.py')):
if not os.path.isfile(os.path.join(localpath, module_name + '.py')): # noqa: PTH113, PTH118
return False

# Ensure local gamefixes are importable as modules via PATH
with open(os.path.join(localpath, '__init__.py'), 'a', encoding='utf-8'):
sys.path.append(os.path.expanduser('~/.config/protonfixes'))
with open(os.path.join(localpath, '__init__.py'), 'a', encoding='utf-8'): # noqa: PTH118, PTH123
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we even have to open the file here?

sys.path.append(os.path.expanduser('~/.config/protonfixes')) # noqa: PTH111

# Run fix
return _run_fix(game_id, default, True)
Expand Down
6 changes: 4 additions & 2 deletions logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import os
import sys
from pathlib import Path


class Log:
Expand Down Expand Up @@ -30,8 +31,9 @@ def log(self, msg: str = '', level: str = 'INFO') -> None:
fulltext = color + pfx + str(msg) + reset + os.linesep
sys.stderr.write(fulltext)
sys.stderr.flush()
with open('/tmp/test', 'a', 1, encoding='utf-8') as testfile:
testfile.write(logtext)

with Path('/tmp/test').open(mode='a', encoding='utf-8', buffering=1) as file:
file.write(logtext)
R1kaB3rN marked this conversation as resolved.
Show resolved Hide resolved

def info(self, msg: str) -> None:
"""Wrapper for printing info messages"""
Expand Down
6 changes: 4 additions & 2 deletions ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ select = [
"FLY",
# Simplify and update syntax to our target Python version
"UP",
"D"
"D",
# Enforce the use of pathlib when working with filesystem paths
"PTH",
]
ignore = [
# Requiring a type for self is deprecated
Expand Down Expand Up @@ -84,7 +86,7 @@ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"

[lint.per-file-ignores]
# Relax docstring-related lint rules for gamefixes
"gamefixes-*" = ["D103", "D205"]
"gamefixes-*" = ["D103", "D205", "PTH"]

[format]
# Like Black, use double quotes for strings.
Expand Down
23 changes: 9 additions & 14 deletions steamhelper.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
"""The Steamhelper allows the installation of Steam apps"""

import os
import re
import shutil
import subprocess
import time
from pathlib import Path


libpaths = []
Expand Down Expand Up @@ -50,8 +50,10 @@ def _is_app_installed(appid: str) -> bool:

is_installed = False
for librarypath in libraries_path:
appmanifest_path = _get_manifest_path(appid, librarypath)
if os.path.exists(appmanifest_path):
appmanifest_path = Path(
librarypath, 'steamapps', f'appmanifest_{str(appid)}.acf'
)
if appmanifest_path.exists(): # noqa: PTH110
state = _find_regex_groups(appmanifest_path, REGEX_STATE, 'state')
if len(state) > 0 and int(state[0]) == 4:
is_installed = True
Expand All @@ -63,24 +65,17 @@ def _get_steam_libraries_path() -> list:
"""Get Steam Libraries Path"""
if len(libpaths) == 0:
for steampath in STEAM_DIRS:
libfile = os.path.join(
os.path.expanduser(steampath), 'steamapps', 'libraryfolders.vdf'
)
if os.path.exists(libfile):
libfile = Path(steampath, 'steamapps', 'libraryfolders.vdf').expanduser()
if libfile.is_file():
libpaths.append(_find_regex_groups(libfile, REGEX_LIB, 'path'))
break
return libpaths


def _get_manifest_path(appid: str, librarypath: str) -> str:
"""Get appmanifest path"""
return os.path.join(librarypath, 'steamapps', f'appmanifest_{str(appid)}.acf')


def _find_regex_groups(path: str, regex: re.Pattern, groupname: str) -> list:
def _find_regex_groups(path: Path, regex: re.Pattern, groupname: str) -> list:
"""Given a file and a regex with a named group groupname, return an array of all the matches"""
matches = []
with open(path, encoding='ascii') as re_file:
with path.open(encoding='ascii') as re_file:
for line in re_file:
search = regex.search(line)
if search:
Expand Down
Loading
Loading