umu-commander 1.5.3__tar.gz → 1.5.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of umu-commander might be problematic. Click here for more details.

Files changed (32) hide show
  1. umu_commander-1.5.5/.idea/shelf/Uncommitted_changes_before_rebase_[Changes]/shelved.patch +229 -0
  2. {umu_commander-1.5.3 → umu_commander-1.5.5}/.idea/workspace.xml +89 -87
  3. {umu_commander-1.5.3 → umu_commander-1.5.5}/PKG-INFO +10 -3
  4. {umu_commander-1.5.3 → umu_commander-1.5.5}/README.md +8 -1
  5. {umu_commander-1.5.3 → umu_commander-1.5.5}/pyproject.toml +7 -4
  6. umu_commander-1.5.5/src/umu_commander/configuration.py +77 -0
  7. umu_commander-1.5.5/src/umu_commander/database.py +54 -0
  8. {umu_commander-1.5.3 → umu_commander-1.5.5}/src/umu_commander/main.py +6 -6
  9. {umu_commander-1.5.3 → umu_commander-1.5.5}/src/umu_commander/proton.py +4 -4
  10. {umu_commander-1.5.3 → umu_commander-1.5.5}/src/umu_commander/tracking.py +7 -7
  11. {umu_commander-1.5.3 → umu_commander-1.5.5}/src/umu_commander/umu_config.py +8 -3
  12. {umu_commander-1.5.3 → umu_commander-1.5.5}/src/umu_commander/util.py +1 -1
  13. {umu_commander-1.5.3 → umu_commander-1.5.5}/tests/test_config.py +2 -2
  14. {umu_commander-1.5.3 → umu_commander-1.5.5}/tests/test_db.py +4 -3
  15. {umu_commander-1.5.3 → umu_commander-1.5.5}/tests/test_proton.py +1 -1
  16. {umu_commander-1.5.3 → umu_commander-1.5.5}/tests/test_tracking.py +2 -2
  17. umu_commander-1.5.3/src/umu_commander/configuration.py +0 -72
  18. umu_commander-1.5.3/src/umu_commander/database.py +0 -43
  19. {umu_commander-1.5.3 → umu_commander-1.5.5}/.gitignore +0 -0
  20. {umu_commander-1.5.3 → umu_commander-1.5.5}/.idea/.gitignore +0 -0
  21. {umu_commander-1.5.3 → umu_commander-1.5.5}/.idea/inspectionProfiles/Project_Default.xml +0 -0
  22. {umu_commander-1.5.3 → umu_commander-1.5.5}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
  23. {umu_commander-1.5.3 → umu_commander-1.5.5}/.idea/misc.xml +0 -0
  24. {umu_commander-1.5.3 → umu_commander-1.5.5}/.idea/modules.xml +0 -0
  25. {umu_commander-1.5.3 → umu_commander-1.5.5}/.idea/umu-commander.iml +0 -0
  26. {umu_commander-1.5.3 → umu_commander-1.5.5}/.idea/vcs.xml +0 -0
  27. {umu_commander-1.5.3 → umu_commander-1.5.5}/LICENSE.txt +0 -0
  28. {umu_commander-1.5.3 → umu_commander-1.5.5}/example_config.toml +0 -0
  29. {umu_commander-1.5.3 → umu_commander-1.5.5}/src/umu_commander/__init__.py +0 -0
  30. {umu_commander-1.5.3 → umu_commander-1.5.5}/src/umu_commander/classes.py +0 -0
  31. {umu_commander-1.5.3 → umu_commander-1.5.5}/tests/__init__.py +0 -0
  32. {umu_commander-1.5.3 → umu_commander-1.5.5}/tests/test_manual.py +0 -0
@@ -0,0 +1,229 @@
1
+ Index: tests/test_db.py
2
+ IDEA additional info:
3
+ Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
4
+ <+>import unittest\nfrom json import JSONDecodeError\n\nimport umu_commander.configuration as config\nfrom tests import *\nfrom umu_commander.database import Database as db\n\n\nclass Database(unittest.TestCase):\n def setUp(self):\n config.DB_DIR = TESTING_DIR\n setup()\n\n def tearDown(self):\n teardown()\n\n def test_missing_db(self):\n db.load()\n self.assertEqual(db.get(), {})\n\n def test_malformed_db(self):\n with open(os.path.join(config.DB_DIR, config.DB_NAME), \"tw\") as db_file:\n db_file.write(\"{\")\n\n with self.assertRaises(JSONDecodeError):\n db.load()\n\n def test_addition_removal(self):\n db.load()\n db.get(PROTON_DIR_1, PROTON_BIG).append(USER_DIR)\n\n self.assertIn(PROTON_BIG, db.get(PROTON_DIR_1))\n self.assertIn(USER_DIR, db.get(PROTON_DIR_1, PROTON_BIG))\n\n db.get(PROTON_DIR_1, PROTON_BIG).remove(USER_DIR)\n\n self.assertIn(PROTON_BIG, db.get(PROTON_DIR_1))\n self.assertNotIn(USER_DIR, db.get(PROTON_DIR_1, PROTON_BIG))\n\n del db.get(PROTON_DIR_1)[PROTON_BIG]\n self.assertNotIn(PROTON_BIG, db.get(PROTON_DIR_1))\n
5
+ Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
6
+ <+>UTF-8
7
+ ===================================================================
8
+ diff --git a/tests/test_db.py b/tests/test_db.py
9
+ --- a/tests/test_db.py (revision 2c990ed1cab5a20498dc2cfe9bd4e331f60cb3af)
10
+ +++ b/tests/test_db.py (date 1754467945899)
11
+ @@ -2,8 +2,8 @@
12
+ from json import JSONDecodeError
13
+
14
+ import umu_commander.configuration as config
15
+ +import umu_commander.database as db
16
+ from tests import *
17
+ -from umu_commander.database import Database as db
18
+
19
+
20
+ class Database(unittest.TestCase):
21
+ Index: tests/test_config.py
22
+ IDEA additional info:
23
+ Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
24
+ <+>import os.path\nimport unittest\n\nimport umu_commander.configuration as config\nfrom tests import *\nfrom umu_commander import configuration\n\n\nclass Config(unittest.TestCase):\n def setUp(self):\n configuration._CONFIG_DIR = TESTING_DIR\n configuration.DB_DIR = TESTING_DIR\n setup()\n\n def tearDown(self):\n teardown()\n\n def test_missing_config(self):\n config.load()\n self.assertTrue(\n os.path.exists(os.path.join(TESTING_DIR, configuration._CONFIG_NAME))\n )\n config.load()\n
25
+ Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
26
+ <+>UTF-8
27
+ ===================================================================
28
+ diff --git a/tests/test_config.py b/tests/test_config.py
29
+ --- a/tests/test_config.py (revision 2c990ed1cab5a20498dc2cfe9bd4e331f60cb3af)
30
+ +++ b/tests/test_config.py (date 1754467762383)
31
+ @@ -8,7 +8,7 @@
32
+
33
+ class Config(unittest.TestCase):
34
+ def setUp(self):
35
+ - configuration._CONFIG_DIR = TESTING_DIR
36
+ + configuration.CONFIG_DIR = TESTING_DIR
37
+ configuration.DB_DIR = TESTING_DIR
38
+ setup()
39
+
40
+ @@ -18,6 +18,6 @@
41
+ def test_missing_config(self):
42
+ config.load()
43
+ self.assertTrue(
44
+ - os.path.exists(os.path.join(TESTING_DIR, configuration._CONFIG_NAME))
45
+ + os.path.exists(os.path.join(TESTING_DIR, configuration.CONFIG_NAME))
46
+ )
47
+ config.load()
48
+ Index: src/umu_commander/database.py
49
+ IDEA additional info:
50
+ Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
51
+ <+>import json\nimport os\nfrom collections import defaultdict\n\nimport umu_commander.configuration as config\n\n\nclass Database:\n _db: defaultdict[str, defaultdict[str, list[str]]]\n\n @staticmethod\n def load():\n if not os.path.exists(config.DB_DIR):\n os.mkdir(config.DB_DIR)\n\n try:\n with open(os.path.join(config.DB_DIR, config.DB_NAME), \"rt\") as db_file:\n Database._db = defaultdict(lambda: defaultdict(list))\n Database._db.update(json.load(db_file))\n\n except FileNotFoundError:\n Database._db = defaultdict(lambda: defaultdict(list))\n\n @staticmethod\n def dump():\n with open(os.path.join(config.DB_DIR, config.DB_NAME), \"wt\") as db_file:\n # noinspection PyTypeChecker\n json.dump(Database._db, db_file, indent=\"\\t\")\n\n @staticmethod\n def get(\n proton_dir: str = None, proton_ver: str = None\n ) -> dict[str, dict[str, list[str]]] | dict[str, list[str]] | list[str]:\n if proton_dir is None and proton_ver is None:\n return Database._db\n\n if proton_ver is None:\n return Database._db[proton_dir]\n\n if proton_ver not in Database._db[proton_dir]:\n Database._db[proton_dir][proton_ver] = []\n\n return Database._db[proton_dir][proton_ver]\n
52
+ Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
53
+ <+>UTF-8
54
+ ===================================================================
55
+ diff --git a/src/umu_commander/database.py b/src/umu_commander/database.py
56
+ --- a/src/umu_commander/database.py (revision 2c990ed1cab5a20498dc2cfe9bd4e331f60cb3af)
57
+ +++ b/src/umu_commander/database.py (date 1754467762394)
58
+ @@ -4,40 +4,46 @@
59
+
60
+ import umu_commander.configuration as config
61
+
62
+ +_db: defaultdict[str, defaultdict[str, list[str]]] = defaultdict(
63
+ + lambda: defaultdict(list)
64
+ +)
65
+
66
+ -class Database:
67
+ - _db: defaultdict[str, defaultdict[str, list[str]]]
68
+
69
+ - @staticmethod
70
+ - def load():
71
+ - if not os.path.exists(config.DB_DIR):
72
+ - os.mkdir(config.DB_DIR)
73
+ +def load():
74
+ + global _db
75
+ +
76
+ + if not os.path.exists(config.DB_DIR):
77
+ + os.mkdir(config.DB_DIR)
78
+
79
+ - try:
80
+ - with open(os.path.join(config.DB_DIR, config.DB_NAME), "rt") as db_file:
81
+ - Database._db = defaultdict(lambda: defaultdict(list))
82
+ - Database._db.update(json.load(db_file))
83
+ + db_path: str = os.path.join(config.DB_DIR, config.DB_NAME)
84
+ + if not os.path.exists(db_path):
85
+ + return
86
+
87
+ - except FileNotFoundError:
88
+ - Database._db = defaultdict(lambda: defaultdict(list))
89
+ + with open(os.path.join(db_path), "rt") as db_file:
90
+ + _db.update(json.load(db_file))
91
+
92
+ - @staticmethod
93
+ - def dump():
94
+ - with open(os.path.join(config.DB_DIR, config.DB_NAME), "wt") as db_file:
95
+ - # noinspection PyTypeChecker
96
+ - json.dump(Database._db, db_file, indent="\t")
97
+ +
98
+ +def dump():
99
+ + if not os.path.exists(config.DB_DIR):
100
+ + os.mkdir(config.DB_DIR)
101
+ +
102
+ + with open(os.path.join(config.DB_DIR, config.DB_NAME), "wt") as db_file:
103
+ + # noinspection PyTypeChecker
104
+ + json.dump(_db, db_file, indent="\t")
105
+
106
+ - @staticmethod
107
+ - def get(
108
+ - proton_dir: str = None, proton_ver: str = None
109
+ - ) -> dict[str, dict[str, list[str]]] | dict[str, list[str]] | list[str]:
110
+ - if proton_dir is None and proton_ver is None:
111
+ - return Database._db
112
+ +
113
+ +def get(
114
+ + proton_dir: str = None, proton_ver: str = None
115
+ +) -> dict[str, dict[str, list[str]]] | dict[str, list[str]] | list[str]:
116
+ + global _db
117
+ +
118
+ + if proton_dir is None and proton_ver is None:
119
+ + return _db
120
+
121
+ - if proton_ver is None:
122
+ - return Database._db[proton_dir]
123
+ + if proton_ver is None:
124
+ + return _db[proton_dir]
125
+
126
+ - if proton_ver not in Database._db[proton_dir]:
127
+ - Database._db[proton_dir][proton_ver] = []
128
+ + if proton_ver not in _db[proton_dir]:
129
+ + _db[proton_dir][proton_ver] = []
130
+
131
+ - return Database._db[proton_dir][proton_ver]
132
+ + return _db[proton_dir][proton_ver]
133
+ Index: tests/test_tracking.py
134
+ IDEA additional info:
135
+ Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
136
+ <+>import unittest\n\nimport umu_commander.configuration as config\nfrom tests import *\nfrom umu_commander import tracking\nfrom umu_commander.classes import ProtonVer\nfrom umu_commander.database import Database as db\n\n\nclass Tracking(unittest.TestCase):\n def setUp(self):\n config.DB_DIR = TESTING_DIR\n setup()\n db.load()\n\n def tearDown(self):\n teardown()\n\n def test_track_untrack(self):\n os.chdir(USER_DIR)\n\n tracking.track(ProtonVer(PROTON_DIR_1, PROTON_BIG), refresh_versions=False)\n self.assertIn(PROTON_BIG, db.get(PROTON_DIR_1))\n self.assertIn(USER_DIR, db.get(PROTON_DIR_1, PROTON_BIG))\n\n tracking.untrack(quiet=True)\n self.assertIn(PROTON_BIG, db.get(PROTON_DIR_1))\n self.assertNotIn(USER_DIR, db.get(PROTON_DIR_1, PROTON_BIG))\n\n def test_track_auto_untrack(self):\n os.chdir(USER_DIR)\n\n tracking.track(ProtonVer(PROTON_DIR_1, PROTON_BIG), refresh_versions=False)\n self.assertIn(PROTON_BIG, db.get(PROTON_DIR_1))\n self.assertIn(USER_DIR, db.get(PROTON_DIR_1, PROTON_BIG))\n\n os.rmdir(USER_DIR)\n tracking.untrack_unlinked()\n self.assertIn(PROTON_BIG, db.get(PROTON_DIR_1))\n self.assertNotIn(\n USER_DIR,\n db.get(PROTON_DIR_1, PROTON_BIG),\n \"Auto untrack did not untrack removed directory.\",\n )\n
137
+ Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
138
+ <+>UTF-8
139
+ ===================================================================
140
+ diff --git a/tests/test_tracking.py b/tests/test_tracking.py
141
+ --- a/tests/test_tracking.py (revision 2c990ed1cab5a20498dc2cfe9bd4e331f60cb3af)
142
+ +++ b/tests/test_tracking.py (date 1754467945864)
143
+ @@ -1,10 +1,10 @@
144
+ import unittest
145
+
146
+ import umu_commander.configuration as config
147
+ +import umu_commander.database as db
148
+ from tests import *
149
+ from umu_commander import tracking
150
+ from umu_commander.classes import ProtonVer
151
+ -from umu_commander.database import Database as db
152
+
153
+
154
+ class Tracking(unittest.TestCase):
155
+ Index: src/umu_commander/proton.py
156
+ IDEA additional info:
157
+ Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
158
+ <+>import os\nimport re\nimport subprocess\n\nimport umu_commander.configuration as config\nfrom umu_commander.classes import ProtonDir, ProtonVer\nfrom umu_commander.database import Database as db\n\n\ndef _natural_sort_proton_ver_key(p: ProtonVer, _nsre=re.compile(r\"(\\d+)\")):\n s: str = p.version_num\n return [int(text) if text.isdigit() else text for text in _nsre.split(s)]\n\n\ndef refresh_proton_versions():\n print(\"Updating umu Proton.\")\n umu_update_process = subprocess.run(\n [\"umu-run\", '\"\"'],\n env={\"PROTONPATH\": \"UMU-Latest\", \"UMU_LOG\": \"debug\"},\n capture_output=True,\n text=True,\n )\n\n for line in umu_update_process.stderr.split(\"\\n\"):\n if \"PROTONPATH\" in line and \"/\" in line:\n try:\n left: int = line.rfind(\"/\") + 1\n print(f\"Using {line[left:len(line) - 1]}.\")\n except ValueError:\n print(\"Could not fetch latest UMU-Proton.\")\n\n break\n\n\ndef _sort_proton_versions(versions: list[ProtonVer]) -> list[ProtonVer]:\n return sorted(versions, key=_natural_sort_proton_ver_key, reverse=True)\n\n\ndef collect_proton_versions(\n sort: bool = False, user_count: bool = False\n) -> list[ProtonDir]:\n def get_user_count(proton_dir: str, proton_ver) -> str:\n return (\n \"(\" + str(len(db.get(proton_dir, proton_ver))) + \")\"\n if proton_ver in db.get(proton_dir)\n else \"(-)\"\n )\n\n proton_dirs: list[ProtonDir] = []\n for proton_dir in config.PROTON_PATHS:\n versions: list[ProtonVer] = [\n ProtonVer(\n proton_dir,\n version,\n get_user_count(proton_dir, version) if user_count else \"\",\n )\n for version in os.listdir(proton_dir)\n if os.path.isdir(os.path.join(proton_dir, version))\n ]\n\n if sort:\n versions = sorted(versions, key=_natural_sort_proton_ver_key, reverse=True)\n\n proton_dirs.append(\n ProtonDir(proton_dir, f\"Proton versions in {proton_dir}:\", versions)\n )\n\n return proton_dirs\n\n\ndef get_latest_umu_proton():\n umu_proton_versions: list[ProtonVer] = [\n ProtonVer(config.UMU_PROTON_PATH, version)\n for version in os.listdir(config.UMU_PROTON_PATH)\n if \"UMU\" in version\n and os.path.isdir(os.path.join(config.UMU_PROTON_PATH, version))\n ]\n umu_proton_versions = sorted(\n umu_proton_versions, key=_natural_sort_proton_ver_key, reverse=True\n )\n\n return umu_proton_versions[0].version_num\n
159
+ Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
160
+ <+>UTF-8
161
+ ===================================================================
162
+ diff --git a/src/umu_commander/proton.py b/src/umu_commander/proton.py
163
+ --- a/src/umu_commander/proton.py (revision 2c990ed1cab5a20498dc2cfe9bd4e331f60cb3af)
164
+ +++ b/src/umu_commander/proton.py (date 1754467945890)
165
+ @@ -3,8 +3,8 @@
166
+ import subprocess
167
+
168
+ import umu_commander.configuration as config
169
+ +import umu_commander.database as db
170
+ from umu_commander.classes import ProtonDir, ProtonVer
171
+ -from umu_commander.database import Database as db
172
+
173
+
174
+ def _natural_sort_proton_ver_key(p: ProtonVer, _nsre=re.compile(r"(\d+)")):
175
+ Index: src/umu_commander/tracking.py
176
+ IDEA additional info:
177
+ Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
178
+ <+>import os\nimport shutil\n\nfrom umu_commander.classes import ProtonDir, ProtonVer\nfrom umu_commander.database import Database as db\nfrom umu_commander.proton import (\n collect_proton_versions,\n get_latest_umu_proton,\n refresh_proton_versions,\n)\nfrom umu_commander.util import (\n get_selection,\n)\n\n\ndef untrack(quiet: bool = False):\n current_dir: str = os.getcwd()\n for proton_dir in db.get().keys():\n for proton_ver in db.get(proton_dir):\n if current_dir in db.get(proton_dir, proton_ver):\n db.get(proton_dir, proton_ver).remove(current_dir)\n\n if not quiet:\n print(\"Directory removed from all user lists.\")\n\n\ndef track(\n proton_ver: ProtonVer = None, refresh_versions: bool = True, quiet: bool = False\n):\n if refresh_versions:\n refresh_proton_versions()\n\n if proton_ver is None:\n proton_ver: ProtonVer = get_selection(\n \"Select Proton version to add directory as user:\",\n None,\n collect_proton_versions(sort=True),\n ).as_proton_ver()\n\n untrack(quiet=True)\n current_directory: str = os.getcwd()\n db.get(proton_ver.dir, proton_ver.version_num).append(current_directory)\n\n if not quiet:\n print(\n f\"Directory {current_directory} added to Proton version's {proton_ver.version_num} in {proton_ver.dir} user list.\"\n )\n\n\ndef users():\n proton_dirs: list[ProtonDir] = collect_proton_versions(sort=True, user_count=True)\n\n proton_ver: ProtonVer = get_selection(\n \"Select Proton version to view user list:\", None, proton_dirs\n ).as_proton_ver()\n\n if proton_ver.dir in db.get() and proton_ver.version_num in db.get(proton_ver.dir):\n version_users: list[str] = db.get(proton_ver.dir, proton_ver.version_num)\n if len(version_users) > 0:\n print(f\"Directories using {proton_ver.version_num} of {proton_ver.dir}:\")\n print(*version_users, sep=\"\\n\")\n else:\n print(\"No directories currently use this version.\")\n else:\n print(\"This version hasn't been used by umu before.\")\n\n\ndef delete():\n for proton_dir in db.get().keys():\n for proton_ver, version_users in db.get(proton_dir).copy().items():\n if proton_ver == get_latest_umu_proton():\n continue\n\n if len(version_users) == 0:\n selection: str = input(\n f\"{proton_ver} in {proton_dir} has no using directories, delete? (Y/N) ? \"\n )\n if selection.lower() == \"y\":\n try:\n shutil.rmtree(os.path.join(proton_dir, proton_ver))\n except FileNotFoundError:\n pass\n del db.get(proton_dir)[proton_ver]\n\n\ndef untrack_unlinked():\n for proton_dir in db.get().keys():\n for proton_ver, version_users in db.get()[proton_dir].items():\n for user in version_users:\n if not os.path.exists(user):\n db.get(proton_dir, proton_ver).remove(user)\n
179
+ Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
180
+ <+>UTF-8
181
+ ===================================================================
182
+ diff --git a/src/umu_commander/tracking.py b/src/umu_commander/tracking.py
183
+ --- a/src/umu_commander/tracking.py (revision 2c990ed1cab5a20498dc2cfe9bd4e331f60cb3af)
184
+ +++ b/src/umu_commander/tracking.py (date 1754467945881)
185
+ @@ -1,8 +1,8 @@
186
+ import os
187
+ import shutil
188
+
189
+ +import umu_commander.database as db
190
+ from umu_commander.classes import ProtonDir, ProtonVer
191
+ -from umu_commander.database import Database as db
192
+ from umu_commander.proton import (
193
+ collect_proton_versions,
194
+ get_latest_umu_proton,
195
+ Index: src/umu_commander/main.py
196
+ IDEA additional info:
197
+ Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
198
+ <+>#!/usr/bin/python3\nimport os\nimport sys\nfrom json import JSONDecodeError\n\nimport umu_commander.configuration as config\nfrom umu_commander import tracking, umu_config\nfrom umu_commander.classes import ExitCode\nfrom umu_commander.configuration import _CONFIG_DIR, _CONFIG_NAME\nfrom umu_commander.database import Database as db\n\n\ndef print_help():\n print(\n \"umu-commander is an interactive CLI tool to help you manage Proton versions used by umu, as well as create enhanced launch configs.\",\n \"\",\n \"For details, explanations, and more, see the README.md file, or visit https://github.com/Mpaxlamitsounas/umu-commander.\",\n sep=\"\\n\",\n )\n\n\ndef main() -> ExitCode:\n try:\n config.load()\n except (JSONDecodeError, KeyError):\n config_path: str = os.path.join(_CONFIG_DIR, _CONFIG_NAME)\n print(f\"Config file at {config_path} could not be read.\")\n os.rename(config_path, os.path.join(_CONFIG_DIR, _CONFIG_NAME + \".old\"))\n\n try:\n db.load()\n except JSONDecodeError:\n db_path: str = os.path.join(config.DB_DIR, config.DB_NAME)\n print(f\"Tracking file at {db_path} could not be read.\")\n os.rename(db_path, os.path.join(config.DB_DIR, config.DB_NAME + \".old\"))\n\n if len(sys.argv) == 1:\n print_help()\n return ExitCode.SUCCESS.value\n\n verb: str = sys.argv[1]\n match verb:\n case \"track\":\n tracking.track()\n case \"untrack\":\n tracking.untrack()\n case \"users\":\n tracking.users()\n case \"delete\":\n tracking.delete()\n case \"create\":\n umu_config.create()\n case \"run\":\n umu_config.run()\n case _:\n print(\"Invalid verb.\")\n print_help()\n return ExitCode.INVALID_SELECTION.value\n\n tracking.untrack_unlinked()\n db.dump()\n config.dump()\n\n return ExitCode.SUCCESS.value\n\n\nif __name__ == \"__main__\":\n exit(main())\n
199
+ Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
200
+ <+>UTF-8
201
+ ===================================================================
202
+ diff --git a/src/umu_commander/main.py b/src/umu_commander/main.py
203
+ --- a/src/umu_commander/main.py (revision 2c990ed1cab5a20498dc2cfe9bd4e331f60cb3af)
204
+ +++ b/src/umu_commander/main.py (date 1754467945908)
205
+ @@ -4,10 +4,10 @@
206
+ from json import JSONDecodeError
207
+
208
+ import umu_commander.configuration as config
209
+ +import umu_commander.database as db
210
+ from umu_commander import tracking, umu_config
211
+ from umu_commander.classes import ExitCode
212
+ -from umu_commander.configuration import _CONFIG_DIR, _CONFIG_NAME
213
+ -from umu_commander.database import Database as db
214
+ +from umu_commander.configuration import CONFIG_DIR, CONFIG_NAME
215
+
216
+
217
+ def print_help():
218
+ @@ -23,9 +23,9 @@
219
+ try:
220
+ config.load()
221
+ except (JSONDecodeError, KeyError):
222
+ - config_path: str = os.path.join(_CONFIG_DIR, _CONFIG_NAME)
223
+ + config_path: str = os.path.join(CONFIG_DIR, CONFIG_NAME)
224
+ print(f"Config file at {config_path} could not be read.")
225
+ - os.rename(config_path, os.path.join(_CONFIG_DIR, _CONFIG_NAME + ".old"))
226
+ + os.rename(config_path, os.path.join(CONFIG_DIR, CONFIG_NAME + ".old"))
227
+
228
+ try:
229
+ db.load()
@@ -4,7 +4,9 @@
4
4
  <option name="autoReloadType" value="SELECTIVE" />
5
5
  </component>
6
6
  <component name="ChangeListManager">
7
- <list default="true" id="043620f1-711f-4c27-a2e2-15fa501e8ce0" name="Changes" comment="1.5.2" />
7
+ <list default="true" id="043620f1-711f-4c27-a2e2-15fa501e8ce0" name="Changes" comment="1.5.4">
8
+ <change beforePath="$PROJECT_DIR$/pyproject.toml" beforeDir="false" afterPath="$PROJECT_DIR$/pyproject.toml" afterDir="false" />
9
+ </list>
8
10
  <option name="SHOW_DIALOG" value="false" />
9
11
  <option name="HIGHLIGHT_CONFLICTS" value="true" />
10
12
  <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
@@ -50,46 +52,46 @@
50
52
  <option name="hideEmptyMiddlePackages" value="true" />
51
53
  <option name="showLibraryContents" value="true" />
52
54
  </component>
53
- <component name="PropertiesComponent"><![CDATA[{
54
- "keyToString": {
55
- "Python tests.Python tests for test_config.Config.executor": "Run",
56
- "Python tests.Python tests for test_config.Config.test_missing_config.executor": "Run",
57
- "Python tests.Python tests for test_db.DB.executor": "Run",
58
- "Python tests.Python tests for test_proton.Tracking.executor": "Run",
59
- "Python tests.Python tests for test_proton.Tracking.test_collect_proton_versions.executor": "Run",
60
- "Python tests.Python tests for test_tracking.Tracking.executor": "Run",
61
- "Python tests.Python tests for test_tracking.Tracking.test_track.executor": "Run",
62
- "Python tests.Python tests for test_tracking.Tracking.test_track_untrack.executor": "Run",
63
- "Python tests.Python tests for test_tracking.TrackingTest.executor": "Run",
64
- "Python tests.Python tests for test_tracking.TrackingTest.test_track.executor": "Run",
65
- "Python tests.Python tests for tests.test_db.DB.executor": "Run",
66
- "Python tests.Python tests for tests.test_db.DBTest.executor": "Run",
67
- "Python tests.Python tests for tests.test_db.DBTest.test_addition_removal.executor": "Run",
68
- "Python tests.Python tests for tests.test_db.DBTest.test_malformed_db.executor": "Run",
69
- "Python tests.Python tests for tests.test_db.DBTest.test_missing_db.executor": "Run",
70
- "Python tests.Python tests for tests.test_tracking.Tracking.executor": "Run",
71
- "Python tests.Python tests for tests.test_tracking.TrackingTest.executor": "Debug",
72
- "Python tests.Python tests for tests.test_tracking.TrackingTest.test_track.executor": "Run",
73
- "Python tests.Python tests in tests.executor": "Run",
74
- "Python tests.Run tests.executor": "Run",
75
- "Python.create.executor": "Run",
76
- "Python.delete.executor": "Run",
77
- "Python.manage.executor": "Run",
78
- "Python.run.executor": "Run",
79
- "Python.track.executor": "Run",
80
- "Python.umu-commander.executor": "Run",
81
- "Python.untrack.executor": "Run",
82
- "Python.users.executor": "Run",
83
- "RunOnceActivity.ShowReadmeOnStart": "true",
84
- "RunOnceActivity.git.unshallow": "true",
85
- "Shell Script.Build.executor": "Run",
86
- "Shell Script.Upload.executor": "Run",
87
- "git-widget-placeholder": "master",
88
- "last_opened_file_path": "/home/hiroshi/Code/Python/umu-commander/tests",
89
- "run.code.analysis.last.selected.profile": "aDefault",
90
- "settings.editor.selected.configurable": "com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable"
55
+ <component name="PropertiesComponent">{
56
+ &quot;keyToString&quot;: {
57
+ &quot;Python tests.Python tests for test_config.Config.executor&quot;: &quot;Run&quot;,
58
+ &quot;Python tests.Python tests for test_config.Config.test_missing_config.executor&quot;: &quot;Run&quot;,
59
+ &quot;Python tests.Python tests for test_db.DB.executor&quot;: &quot;Run&quot;,
60
+ &quot;Python tests.Python tests for test_proton.Tracking.executor&quot;: &quot;Run&quot;,
61
+ &quot;Python tests.Python tests for test_proton.Tracking.test_collect_proton_versions.executor&quot;: &quot;Run&quot;,
62
+ &quot;Python tests.Python tests for test_tracking.Tracking.executor&quot;: &quot;Run&quot;,
63
+ &quot;Python tests.Python tests for test_tracking.Tracking.test_track.executor&quot;: &quot;Run&quot;,
64
+ &quot;Python tests.Python tests for test_tracking.Tracking.test_track_untrack.executor&quot;: &quot;Run&quot;,
65
+ &quot;Python tests.Python tests for test_tracking.TrackingTest.executor&quot;: &quot;Run&quot;,
66
+ &quot;Python tests.Python tests for test_tracking.TrackingTest.test_track.executor&quot;: &quot;Run&quot;,
67
+ &quot;Python tests.Python tests for tests.test_db.DB.executor&quot;: &quot;Run&quot;,
68
+ &quot;Python tests.Python tests for tests.test_db.DBTest.executor&quot;: &quot;Run&quot;,
69
+ &quot;Python tests.Python tests for tests.test_db.DBTest.test_addition_removal.executor&quot;: &quot;Run&quot;,
70
+ &quot;Python tests.Python tests for tests.test_db.DBTest.test_malformed_db.executor&quot;: &quot;Run&quot;,
71
+ &quot;Python tests.Python tests for tests.test_db.DBTest.test_missing_db.executor&quot;: &quot;Run&quot;,
72
+ &quot;Python tests.Python tests for tests.test_tracking.Tracking.executor&quot;: &quot;Run&quot;,
73
+ &quot;Python tests.Python tests for tests.test_tracking.TrackingTest.executor&quot;: &quot;Debug&quot;,
74
+ &quot;Python tests.Python tests for tests.test_tracking.TrackingTest.test_track.executor&quot;: &quot;Run&quot;,
75
+ &quot;Python tests.Python tests in tests.executor&quot;: &quot;Run&quot;,
76
+ &quot;Python tests.Run tests.executor&quot;: &quot;Run&quot;,
77
+ &quot;Python.create.executor&quot;: &quot;Run&quot;,
78
+ &quot;Python.delete.executor&quot;: &quot;Run&quot;,
79
+ &quot;Python.manage.executor&quot;: &quot;Run&quot;,
80
+ &quot;Python.run.executor&quot;: &quot;Run&quot;,
81
+ &quot;Python.track.executor&quot;: &quot;Run&quot;,
82
+ &quot;Python.umu-commander.executor&quot;: &quot;Run&quot;,
83
+ &quot;Python.untrack.executor&quot;: &quot;Run&quot;,
84
+ &quot;Python.users.executor&quot;: &quot;Run&quot;,
85
+ &quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
86
+ &quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
87
+ &quot;Shell Script.Build.executor&quot;: &quot;Run&quot;,
88
+ &quot;Shell Script.Upload.executor&quot;: &quot;Run&quot;,
89
+ &quot;git-widget-placeholder&quot;: &quot;master&quot;,
90
+ &quot;last_opened_file_path&quot;: &quot;/home/hiroshi/Code/Python/umu-commander/tests&quot;,
91
+ &quot;run.code.analysis.last.selected.profile&quot;: &quot;aDefault&quot;,
92
+ &quot;settings.editor.selected.configurable&quot;: &quot;com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable&quot;
91
93
  }
92
- }]]></component>
94
+ }</component>
93
95
  <component name="RecentsManager">
94
96
  <key name="CopyFile.RECENT_KEYS">
95
97
  <recent name="$PROJECT_DIR$/tests" />
@@ -379,46 +381,6 @@
379
381
  <option name="presentableId" value="Default" />
380
382
  <updated>1751043400140</updated>
381
383
  </task>
382
- <task id="LOCAL-00009" summary="Update readme and decapitalise umu">
383
- <option name="closed" value="true" />
384
- <created>1751918299613</created>
385
- <option name="number" value="00009" />
386
- <option name="presentableId" value="LOCAL-00009" />
387
- <option name="project" value="LOCAL" />
388
- <updated>1751918299613</updated>
389
- </task>
390
- <task id="LOCAL-00010" summary="Add example config">
391
- <option name="closed" value="true" />
392
- <created>1751919191540</created>
393
- <option name="number" value="00010" />
394
- <option name="presentableId" value="LOCAL-00010" />
395
- <option name="project" value="LOCAL" />
396
- <updated>1751919191540</updated>
397
- </task>
398
- <task id="LOCAL-00011" summary="Tweak &quot;latest umu&quot; functionality">
399
- <option name="closed" value="true" />
400
- <created>1751919211128</created>
401
- <option name="number" value="00011" />
402
- <option name="presentableId" value="LOCAL-00011" />
403
- <option name="project" value="LOCAL" />
404
- <updated>1751919211128</updated>
405
- </task>
406
- <task id="LOCAL-00012" summary="Update readme">
407
- <option name="closed" value="true" />
408
- <created>1751919216236</created>
409
- <option name="number" value="00012" />
410
- <option name="presentableId" value="LOCAL-00012" />
411
- <option name="project" value="LOCAL" />
412
- <updated>1751919216236</updated>
413
- </task>
414
- <task id="LOCAL-00013" summary="Automatically untrack removed directories">
415
- <option name="closed" value="true" />
416
- <created>1751919326145</created>
417
- <option name="number" value="00013" />
418
- <option name="presentableId" value="LOCAL-00013" />
419
- <option name="project" value="LOCAL" />
420
- <updated>1751919326145</updated>
421
- </task>
422
384
  <task id="LOCAL-00014" summary="Update readme">
423
385
  <option name="closed" value="true" />
424
386
  <created>1751919652836</created>
@@ -771,7 +733,47 @@
771
733
  <option name="project" value="LOCAL" />
772
734
  <updated>1754003619303</updated>
773
735
  </task>
774
- <option name="localTasksCounter" value="58" />
736
+ <task id="LOCAL-00058" summary="made typo.">
737
+ <option name="closed" value="true" />
738
+ <created>1754004029502</created>
739
+ <option name="number" value="00058" />
740
+ <option name="presentableId" value="LOCAL-00058" />
741
+ <option name="project" value="LOCAL" />
742
+ <updated>1754004029502</updated>
743
+ </task>
744
+ <task id="LOCAL-00059" summary="1.5.3">
745
+ <option name="closed" value="true" />
746
+ <created>1754004038591</created>
747
+ <option name="number" value="00059" />
748
+ <option name="presentableId" value="LOCAL-00059" />
749
+ <option name="project" value="LOCAL" />
750
+ <updated>1754004038591</updated>
751
+ </task>
752
+ <task id="LOCAL-00060" summary="Fix error code returning">
753
+ <option name="closed" value="true" />
754
+ <created>1754004871181</created>
755
+ <option name="number" value="00060" />
756
+ <option name="presentableId" value="LOCAL-00060" />
757
+ <option name="project" value="LOCAL" />
758
+ <updated>1754004871181</updated>
759
+ </task>
760
+ <task id="LOCAL-00061" summary="Update documentation.">
761
+ <option name="closed" value="true" />
762
+ <created>1754005126667</created>
763
+ <option name="number" value="00061" />
764
+ <option name="presentableId" value="LOCAL-00061" />
765
+ <option name="project" value="LOCAL" />
766
+ <updated>1754005126667</updated>
767
+ </task>
768
+ <task id="LOCAL-00062" summary="1.5.4">
769
+ <option name="closed" value="true" />
770
+ <created>1754005137926</created>
771
+ <option name="number" value="00062" />
772
+ <option name="presentableId" value="LOCAL-00062" />
773
+ <option name="project" value="LOCAL" />
774
+ <updated>1754005137926</updated>
775
+ </task>
776
+ <option name="localTasksCounter" value="63" />
775
777
  <servers />
776
778
  </component>
777
779
  <component name="Vcs.Log.Tabs.Properties">
@@ -798,11 +800,6 @@
798
800
  </option>
799
801
  </component>
800
802
  <component name="VcsManagerConfiguration">
801
- <MESSAGE value="Add UMU_PROTON_DIR config key" />
802
- <MESSAGE value="Rename DB methods&#10;Rename variables for consistency&#10;Use dataclasses to group data better&#10;Add full support for multiple proton directories" />
803
- <MESSAGE value="shorter" />
804
- <MESSAGE value="Add some comments, rename a DB method" />
805
- <MESSAGE value="Move src code to src directory" />
806
803
  <MESSAGE value="Update documentation on the change" />
807
804
  <MESSAGE value="Reorganised project&#10;Split methods from util.py into proton.py&#10;Various fixes&#10;Formatting" />
808
805
  <MESSAGE value="Add project files" />
@@ -823,6 +820,11 @@
823
820
  <MESSAGE value="Improve config handling" />
824
821
  <MESSAGE value="Update documentation" />
825
822
  <MESSAGE value="1.5.2" />
826
- <option name="LAST_COMMIT_MESSAGE" value="1.5.2" />
823
+ <MESSAGE value="made typo." />
824
+ <MESSAGE value="1.5.3" />
825
+ <MESSAGE value="Fix error code returning" />
826
+ <MESSAGE value="Update documentation." />
827
+ <MESSAGE value="1.5.4" />
828
+ <option name="LAST_COMMIT_MESSAGE" value="1.5.4" />
827
829
  </component>
828
830
  </project>
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: umu-commander
3
- Version: 1.5.3
3
+ Version: 1.5.5
4
4
  Summary: umu-commander is an interactive CLI tool to help you manage umu.
5
5
  Project-URL: Homepage, https://github.com/Mpaxlamitsounas/umu-commander
6
6
  Project-URL: Issues, https://github.com/Mpaxlamitsounas/umu-commander/issues
7
7
  Author-email: Mpaxlamitsounas <worldstudy123@gmail.com>
8
8
  License-Expression: MIT
9
9
  License-File: LICENSE.txt
10
- Keywords: umu
10
+ Keywords: umu,umu-launcher
11
11
  Classifier: Operating System :: POSIX :: Linux
12
12
  Classifier: Programming Language :: Python :: 3
13
13
  Requires-Python: >=3.12
@@ -49,4 +49,11 @@ umu-commander needs one of the following verbs specified after the executable na
49
49
  * This is NOT equivalent to `umu-run --config <config_name>`, as vanilla umu configs do not support setting environment variables as of 07/2025.
50
50
 
51
51
  ### Installation/Usage
52
- Add umu-run to your PATH and then install with pipx by running `pipx install umu-commander`. After that you can invoke umu-commander by running `umu-commander <verb>`.
52
+ Add umu-run to your PATH and then install with pipx by running `pipx install umu-commander`. After that you can invoke umu-commander by running `umu-commander <verb>`.
53
+
54
+ ### Return codes
55
+ | Number | Name | Description |
56
+ |:-------|:------------------|:----------------------------------------------------------------|
57
+ | 0 | SUCCESS | Program executed as intended. |
58
+ | 1 | READING_ERROR | Failed to parse a file and could not recover. |
59
+ | 2 | INVALID_SELECTION | User selected an invalid verb or there are no valid selections. |
@@ -33,4 +33,11 @@ umu-commander needs one of the following verbs specified after the executable na
33
33
  * This is NOT equivalent to `umu-run --config <config_name>`, as vanilla umu configs do not support setting environment variables as of 07/2025.
34
34
 
35
35
  ### Installation/Usage
36
- Add umu-run to your PATH and then install with pipx by running `pipx install umu-commander`. After that you can invoke umu-commander by running `umu-commander <verb>`.
36
+ Add umu-run to your PATH and then install with pipx by running `pipx install umu-commander`. After that you can invoke umu-commander by running `umu-commander <verb>`.
37
+
38
+ ### Return codes
39
+ | Number | Name | Description |
40
+ |:-------|:------------------|:----------------------------------------------------------------|
41
+ | 0 | SUCCESS | Program executed as intended. |
42
+ | 1 | READING_ERROR | Failed to parse a file and could not recover. |
43
+ | 2 | INVALID_SELECTION | User selected an invalid verb or there are no valid selections. |
@@ -4,9 +4,9 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "umu-commander"
7
- version = "1.5.3"
7
+ version = "1.5.5"
8
8
  authors = [
9
- { name="Mpaxlamitsounas", email="worldstudy123@gmail.com" },
9
+ { name = "Mpaxlamitsounas", email = "worldstudy123@gmail.com" },
10
10
  ]
11
11
  description = "umu-commander is an interactive CLI tool to help you manage umu."
12
12
  readme = "README.md"
@@ -15,13 +15,16 @@ classifiers = [
15
15
  "Programming Language :: Python :: 3",
16
16
  "Operating System :: POSIX :: Linux",
17
17
  ]
18
- dependencies =[
18
+ dependencies = [
19
19
  "tomli-w"
20
20
  ]
21
21
 
22
22
  license = "MIT"
23
23
  license-files = ["LICEN[CS]E*"]
24
- keywords = ["umu"]
24
+ keywords = [
25
+ "umu",
26
+ "umu-launcher"
27
+ ]
25
28
 
26
29
  [project.urls]
27
30
  Homepage = "https://github.com/Mpaxlamitsounas/umu-commander"
@@ -0,0 +1,77 @@
1
+ import importlib
2
+ import os
3
+ import tomllib
4
+ from pathlib import Path
5
+ from typing import Any
6
+
7
+ import tomli_w
8
+
9
+ from umu_commander.classes import DLLOverride
10
+
11
+ CONFIG_DIR: str = os.path.join(Path.home(), ".config")
12
+ CONFIG_NAME: str = "umu-commander.toml"
13
+
14
+
15
+ PROTON_PATHS: tuple[str, ...] = (
16
+ os.path.join(Path.home(), ".local/share/Steam/compatibilitytools.d/"),
17
+ os.path.join(Path.home(), ".local/share/umu/compatibilitytools"),
18
+ )
19
+ UMU_PROTON_PATH: str = os.path.join(
20
+ Path.home(), ".local/share/Steam/compatibilitytools.d/"
21
+ )
22
+ DB_NAME: str = "tracking.json"
23
+ DB_DIR: str = os.path.join(Path.home(), ".local/share/umu/compatibilitytools")
24
+ UMU_CONFIG_NAME: str = "umu-config.toml"
25
+ DEFAULT_PREFIX_DIR: str = os.path.join(Path.home(), ".local/share/wineprefixes/")
26
+ DLL_OVERRIDES_OPTIONS: tuple[DLLOverride, ...] = (
27
+ DLLOverride("winhttp for BepInEx", "winhttp.dll=n;"),
28
+ )
29
+
30
+
31
+ def load():
32
+ config_path: str = os.path.join(CONFIG_DIR, CONFIG_NAME)
33
+ if not os.path.exists(config_path):
34
+ return
35
+
36
+ with open(config_path, "rb") as conf_file:
37
+ toml_conf = tomllib.load(conf_file)
38
+ if "DLL_OVERRIDES_OPTIONS" in toml_conf:
39
+ toml_conf["DLL_OVERRIDES_OPTIONS"] = tuple(
40
+ [
41
+ DLLOverride(label, override_str)
42
+ for label, override_str in toml_conf[
43
+ "DLL_OVERRIDES_OPTIONS"
44
+ ].items()
45
+ ]
46
+ )
47
+
48
+ module = importlib.import_module(__name__)
49
+ for key, value in toml_conf.items():
50
+ setattr(module, key, value)
51
+
52
+
53
+ def _get_attributes() -> dict[str, Any]:
54
+ module = importlib.import_module(__name__)
55
+ attributes: dict[str, Any] = {}
56
+ for key in dir(module):
57
+ value = getattr(module, key)
58
+ if not key.startswith("_") and not callable(value) and key.upper() == key:
59
+ attributes[key] = value
60
+
61
+ return attributes
62
+
63
+
64
+ def dump():
65
+ if not os.path.exists(CONFIG_DIR):
66
+ os.mkdir(CONFIG_DIR)
67
+
68
+ with open(os.path.join(CONFIG_DIR, CONFIG_NAME), "wb") as conf_file:
69
+ toml_conf = _get_attributes()
70
+ del toml_conf["CONFIG_DIR"]
71
+ del toml_conf["CONFIG_NAME"]
72
+
73
+ toml_conf["DLL_OVERRIDES_OPTIONS"] = dict(
74
+ [(override.info, override.value) for override in DLL_OVERRIDES_OPTIONS]
75
+ )
76
+
77
+ tomli_w.dump(toml_conf, conf_file)
@@ -0,0 +1,54 @@
1
+ import json
2
+ import os
3
+ from collections import defaultdict
4
+
5
+ import umu_commander.configuration as config
6
+
7
+ _db: defaultdict[str, defaultdict[str, list[str]]] = defaultdict(
8
+ lambda: defaultdict(list)
9
+ )
10
+
11
+
12
+ def load():
13
+ global _db
14
+
15
+ if not os.path.exists(config.DB_DIR):
16
+ os.mkdir(config.DB_DIR)
17
+
18
+ db_path: str = os.path.join(config.DB_DIR, config.DB_NAME)
19
+ if not os.path.exists(db_path):
20
+ return
21
+
22
+ with open(os.path.join(db_path), "rt") as db_file:
23
+ _db.update(json.load(db_file))
24
+
25
+
26
+ def dump():
27
+ if not os.path.exists(config.DB_DIR):
28
+ os.mkdir(config.DB_DIR)
29
+
30
+ with open(os.path.join(config.DB_DIR, config.DB_NAME), "wt") as db_file:
31
+ # noinspection PyTypeChecker
32
+ json.dump(_db, db_file, indent="\t")
33
+
34
+
35
+ def get(
36
+ proton_dir: str = None, proton_ver: str = None
37
+ ) -> dict[str, dict[str, list[str]]] | dict[str, list[str]] | list[str]:
38
+ global _db
39
+
40
+ if proton_dir is None and proton_ver is None:
41
+ return _db
42
+
43
+ if proton_ver is None:
44
+ return _db[proton_dir]
45
+
46
+ if proton_ver not in _db[proton_dir]:
47
+ _db[proton_dir][proton_ver] = []
48
+
49
+ return _db[proton_dir][proton_ver]
50
+
51
+
52
+ def _reset():
53
+ global _db
54
+ _db = defaultdict(lambda: defaultdict(list))
@@ -3,11 +3,11 @@ import os
3
3
  import sys
4
4
  from json import JSONDecodeError
5
5
 
6
+ import umu_commander.configuration as config
7
+ import umu_commander.database as db
6
8
  from umu_commander import tracking, umu_config
7
9
  from umu_commander.classes import ExitCode
8
10
  from umu_commander.configuration import CONFIG_DIR, CONFIG_NAME
9
- from umu_commander.configuration import Configuration as config
10
- from umu_commander.database import Database as db
11
11
 
12
12
 
13
13
  def print_help():
@@ -36,7 +36,7 @@ def main() -> ExitCode:
36
36
 
37
37
  if len(sys.argv) == 1:
38
38
  print_help()
39
- return ExitCode.SUCCESS
39
+ return ExitCode.SUCCESS.value
40
40
 
41
41
  verb: str = sys.argv[1]
42
42
  match verb:
@@ -55,14 +55,14 @@ def main() -> ExitCode:
55
55
  case _:
56
56
  print("Invalid verb.")
57
57
  print_help()
58
- return ExitCode.INVALID_SELECTION
58
+ return ExitCode.INVALID_SELECTION.value
59
59
 
60
60
  tracking.untrack_unlinked()
61
61
  db.dump()
62
62
  config.dump()
63
63
 
64
- return ExitCode.SUCCESS
64
+ return ExitCode.SUCCESS.value
65
65
 
66
66
 
67
67
  if __name__ == "__main__":
68
- exit(main().value)
68
+ exit(main())
@@ -2,9 +2,9 @@ import os
2
2
  import re
3
3
  import subprocess
4
4
 
5
+ import umu_commander.configuration as config
6
+ import umu_commander.database as db
5
7
  from umu_commander.classes import ProtonDir, ProtonVer
6
- from umu_commander.configuration import Configuration as config
7
- from umu_commander.database import Database as db
8
8
 
9
9
 
10
10
  def _natural_sort_proton_ver_key(p: ProtonVer, _nsre=re.compile(r"(\d+)")):
@@ -25,9 +25,9 @@ def refresh_proton_versions():
25
25
  if "PROTONPATH" in line and "/" in line:
26
26
  try:
27
27
  left: int = line.rfind("/") + 1
28
- print(f"Using {line[left:len(line) - 1]}.")
28
+ print(f":Latest umu Proton: {line[left:len(line) - 1]}.")
29
29
  except ValueError:
30
- print("Could not fetch latest UMU-Proton.")
30
+ print("Could not fetch latest umu Proton.")
31
31
 
32
32
  break
33
33
 
@@ -1,8 +1,8 @@
1
1
  import os
2
2
  import shutil
3
3
 
4
+ import umu_commander.database as db
4
5
  from umu_commander.classes import ProtonDir, ProtonVer
5
- from umu_commander.database import Database as db
6
6
  from umu_commander.proton import (
7
7
  collect_proton_versions,
8
8
  get_latest_umu_proton,
@@ -21,7 +21,7 @@ def untrack(quiet: bool = False):
21
21
  db.get(proton_dir, proton_ver).remove(current_dir)
22
22
 
23
23
  if not quiet:
24
- print("Directory removed from all user lists.")
24
+ print("Directory removed from all tracking lists.")
25
25
 
26
26
 
27
27
  def track(
@@ -32,7 +32,7 @@ def track(
32
32
 
33
33
  if proton_ver is None:
34
34
  proton_ver: ProtonVer = get_selection(
35
- "Select Proton version to add directory as user:",
35
+ "Select Proton version to track directory with:",
36
36
  None,
37
37
  collect_proton_versions(sort=True),
38
38
  ).as_proton_ver()
@@ -43,7 +43,7 @@ def track(
43
43
 
44
44
  if not quiet:
45
45
  print(
46
- f"Directory {current_directory} added to Proton version's {proton_ver.version_num} in {proton_ver.dir} user list."
46
+ f"Directory {current_directory} added to Proton version's {proton_ver.version_num} in {proton_ver.dir} tracking list."
47
47
  )
48
48
 
49
49
 
@@ -57,10 +57,10 @@ def users():
57
57
  if proton_ver.dir in db.get() and proton_ver.version_num in db.get(proton_ver.dir):
58
58
  version_users: list[str] = db.get(proton_ver.dir, proton_ver.version_num)
59
59
  if len(version_users) > 0:
60
- print(f"Directories using {proton_ver.version_num} of {proton_ver.dir}:")
60
+ print(f"Directories tracked by {proton_ver.version_num} of {proton_ver.dir}:")
61
61
  print(*version_users, sep="\n")
62
62
  else:
63
- print("No directories currently use this version.")
63
+ print("This version is tracking no directories.")
64
64
  else:
65
65
  print("This version hasn't been used by umu before.")
66
66
 
@@ -73,7 +73,7 @@ def delete():
73
73
 
74
74
  if len(version_users) == 0:
75
75
  selection: str = input(
76
- f"{proton_ver} in {proton_dir} has no using directories, delete? (Y/N) ? "
76
+ f"Version {proton_ver} in {proton_dir} is tracking no directories, delete? (Y/N) ? "
77
77
  )
78
78
  if selection.lower() == "y":
79
79
  try:
@@ -5,9 +5,9 @@ from typing import Any
5
5
 
6
6
  import tomli_w
7
7
 
8
+ import umu_commander.configuration as config
8
9
  from umu_commander import tracking
9
10
  from umu_commander.classes import DLLOverride, ProtonVer
10
- from umu_commander.configuration import Configuration as config
11
11
  from umu_commander.proton import collect_proton_versions, refresh_proton_versions
12
12
  from umu_commander.util import (
13
13
  get_selection,
@@ -54,7 +54,7 @@ def create():
54
54
  ]
55
55
  selected: set[int] = set()
56
56
  while True:
57
- print("Select DLLs to override, you can select multiple:")
57
+ print("Select DLLs to override, multiple can be selected:")
58
58
  for idx, override in enumerate(possible_overrides):
59
59
  if idx in selected:
60
60
  idx = "Y"
@@ -122,10 +122,14 @@ def create():
122
122
  if not selected_umu_latest:
123
123
  tracking.track(proton_ver, False)
124
124
  except:
125
- print("Could not create configiguration file.")
125
+ print("Could not create configuration file.")
126
126
 
127
127
 
128
128
  def run():
129
+ if not os.path.exists(config.UMU_CONFIG_NAME):
130
+ print("No umu config in current directory.")
131
+ return
132
+
129
133
  with open(config.UMU_CONFIG_NAME, "rb") as toml_file:
130
134
  toml_conf = tomllib.load(toml_file)
131
135
 
@@ -137,3 +141,4 @@ def run():
137
141
  args=["umu-run", "--config", config.UMU_CONFIG_NAME],
138
142
  env=os.environ,
139
143
  )
144
+
@@ -58,7 +58,7 @@ def get_selection(
58
58
  ) -> Element:
59
59
  if not _selection_set_valid(selection_elements, selection_groups):
60
60
  print("Nothing to select from.")
61
- exit(ExitCode.INVALID_SELECTION)
61
+ exit(ExitCode.INVALID_SELECTION.value)
62
62
 
63
63
  if selection_groups is None:
64
64
  selection_groups = []
@@ -1,9 +1,9 @@
1
1
  import os.path
2
2
  import unittest
3
3
 
4
+ import umu_commander.configuration as config
4
5
  from tests import *
5
6
  from umu_commander import configuration
6
- from umu_commander.configuration import Configuration as config
7
7
 
8
8
 
9
9
  class Config(unittest.TestCase):
@@ -16,7 +16,7 @@ class Config(unittest.TestCase):
16
16
  teardown()
17
17
 
18
18
  def test_missing_config(self):
19
- config.load()
19
+ config.dump()
20
20
  self.assertTrue(
21
21
  os.path.exists(os.path.join(TESTING_DIR, configuration.CONFIG_NAME))
22
22
  )
@@ -1,22 +1,23 @@
1
1
  import unittest
2
2
  from json import JSONDecodeError
3
3
 
4
+ import umu_commander.configuration as config
5
+ import umu_commander.database as db
4
6
  from tests import *
5
- from umu_commander.configuration import Configuration as config
6
- from umu_commander.database import Database as db
7
7
 
8
8
 
9
9
  class Database(unittest.TestCase):
10
10
  def setUp(self):
11
11
  config.DB_DIR = TESTING_DIR
12
12
  setup()
13
+ db._reset()
13
14
 
14
15
  def tearDown(self):
15
16
  teardown()
16
17
 
17
18
  def test_missing_db(self):
18
19
  db.load()
19
- self.assertEqual(db.get(), {})
20
+ self.assertEqual(db.get().keys(), {}.keys())
20
21
 
21
22
  def test_malformed_db(self):
22
23
  with open(os.path.join(config.DB_DIR, config.DB_NAME), "tw") as db_file:
@@ -1,9 +1,9 @@
1
1
  import unittest
2
2
 
3
+ import umu_commander.configuration as config
3
4
  from tests import *
4
5
  from umu_commander import proton
5
6
  from umu_commander.classes import Group
6
- from umu_commander.configuration import Configuration as config
7
7
 
8
8
 
9
9
  class Tracking(unittest.TestCase):
@@ -1,10 +1,10 @@
1
1
  import unittest
2
2
 
3
+ import umu_commander.configuration as config
4
+ import umu_commander.database as db
3
5
  from tests import *
4
6
  from umu_commander import tracking
5
7
  from umu_commander.classes import ProtonVer
6
- from umu_commander.configuration import Configuration as config
7
- from umu_commander.database import Database as db
8
8
 
9
9
 
10
10
  class Tracking(unittest.TestCase):
@@ -1,72 +0,0 @@
1
- import os
2
- import tomllib
3
- from pathlib import Path
4
-
5
- import tomli_w
6
-
7
- from umu_commander.classes import DLLOverride
8
-
9
- CONFIG_DIR: str = os.path.join(Path.home(), ".config")
10
- CONFIG_NAME: str = "umu-commander.toml"
11
-
12
-
13
- class Configuration:
14
- PROTON_PATHS: tuple[str, ...] = (
15
- os.path.join(Path.home(), ".local/share/Steam/compatibilitytools.d/"),
16
- os.path.join(Path.home(), ".local/share/umu/compatibilitytools"),
17
- )
18
- UMU_PROTON_PATH: str = os.path.join(
19
- Path.home(), ".local/share/Steam/compatibilitytools.d/"
20
- )
21
- DB_NAME: str = "tracking.json"
22
- DB_DIR: str = os.path.join(Path.home(), ".local/share/umu/compatibilitytools")
23
- UMU_CONFIG_NAME: str = "umu-config.toml"
24
- DEFAULT_PREFIX_DIR: str = os.path.join(Path.home(), ".local/share/wineprefixes/")
25
- DLL_OVERRIDES_OPTIONS: tuple[DLLOverride, ...] = (
26
- DLLOverride("winhttp for BepInEx", "winhttp.dll=n;"),
27
- )
28
-
29
- @staticmethod
30
- def load():
31
- try:
32
- with open(os.path.join(CONFIG_DIR, CONFIG_NAME), "rb") as conf_file:
33
- toml_conf = tomllib.load(conf_file)
34
- toml_conf["DLL_OVERRIDES_OPTIONS"] = tuple(
35
- [
36
- DLLOverride(label, override_str)
37
- for label, override_str in toml_conf[
38
- "DLL_OVERRIDES_OPTIONS"
39
- ].items()
40
- ]
41
- )
42
-
43
- for key, value in toml_conf.items():
44
- setattr(Configuration, key, value)
45
-
46
- except FileNotFoundError:
47
- Configuration.dump()
48
-
49
- @staticmethod
50
- def dump():
51
- if not os.path.exists(CONFIG_DIR):
52
- os.mkdir(CONFIG_DIR)
53
-
54
- with open(os.path.join(CONFIG_DIR, CONFIG_NAME), "wb") as conf_file:
55
- toml_conf = Configuration._get_attributes()
56
- toml_conf["DLL_OVERRIDES_OPTIONS"] = dict(
57
- [
58
- (override.info, override.value)
59
- for override in Configuration.DLL_OVERRIDES_OPTIONS
60
- ]
61
- )
62
-
63
- tomli_w.dump(toml_conf, conf_file)
64
-
65
- @staticmethod
66
- def _get_attributes():
67
- attributes = {}
68
- for key, value in vars(Configuration).items():
69
- if not key.startswith("__") and not callable(getattr(Configuration, key)):
70
- attributes[key] = value
71
-
72
- return attributes
@@ -1,43 +0,0 @@
1
- import json
2
- import os
3
- from collections import defaultdict
4
-
5
- from umu_commander.configuration import Configuration as config
6
-
7
-
8
- class Database:
9
- _db: defaultdict[str, defaultdict[str, list[str]]]
10
-
11
- @staticmethod
12
- def load():
13
- if not os.path.exists(config.DB_DIR):
14
- os.mkdir(config.DB_DIR)
15
-
16
- try:
17
- with open(os.path.join(config.DB_DIR, config.DB_NAME), "rt") as db_file:
18
- Database._db = defaultdict(lambda: defaultdict(list))
19
- Database._db.update(json.load(db_file))
20
-
21
- except FileNotFoundError:
22
- Database._db = defaultdict(lambda: defaultdict(list))
23
-
24
- @staticmethod
25
- def dump():
26
- with open(os.path.join(config.DB_DIR, config.DB_NAME), "wt") as db_file:
27
- # noinspection PyTypeChecker
28
- json.dump(Database._db, db_file, indent="\t")
29
-
30
- @staticmethod
31
- def get(
32
- proton_dir: str = None, proton_ver: str = None
33
- ) -> dict[str, dict[str, list[str]]] | dict[str, list[str]] | list[str]:
34
- if proton_dir is None and proton_ver is None:
35
- return Database._db
36
-
37
- if proton_ver is None:
38
- return Database._db[proton_dir]
39
-
40
- if proton_ver not in Database._db[proton_dir]:
41
- Database._db[proton_dir][proton_ver] = []
42
-
43
- return Database._db[proton_dir][proton_ver]
File without changes
File without changes