Skip to content

Commit 5c1b51a

Browse files
committed
Add dedicated mod.py methods for "all"
Add dedicated *_all() methods for: - delete_mod_all - tag_all - activate_mod_all - deactivate_mod_all This covers mod.py but not bethesda.py, which should probably get the same treatment for mod and plugin functions. Also: - Resolve a few `ty` linter complaints in mod.py, particularly in regards to when we cast index to int and that the result of .stage() has Path keys, not str keys. - Refactor how de/activate handles ValueErrors
1 parent 093d771 commit 5c1b51a

1 file changed

Lines changed: 101 additions & 95 deletions

File tree

ammo/controller/mod.py

Lines changed: 101 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ def stage(self) -> dict:
379379
Returns a dict containing the final symlinks that would be installed.
380380
"""
381381
# { destination: (mod_name, source), ... }
382-
result: dict[str, tuple[str, Path]] = {}
382+
result: dict[Path, tuple[str, Path]] = {}
383383
# Iterate through enabled mods in order.
384384
for mod in self.mods:
385385
mod.conflict = False
@@ -496,32 +496,32 @@ def has_extra_folder(self, path: Path) -> bool:
496496
ui = UI(prompt_controller)
497497
return ui.repl()
498498

499+
def activate_mod_all(self) -> None:
500+
warnings = []
501+
for i, mod in enumerate(self.mods):
502+
if not mod.visible:
503+
continue
504+
try:
505+
# Activate the mod, unless it's an unconfigured fomod.
506+
self.set_mod_state(i, True)
507+
except Warning as e:
508+
warnings.append(e)
509+
self.stage()
510+
if warnings:
511+
raise Warning("\n".join(set([i.args[0] for i in warnings])))
512+
499513
def activate_mod(self, index: Union[int, str]) -> None:
500514
"""
501515
Enabled mods will be loaded by game.
502516
"""
503-
try:
504-
int(index)
505-
except ValueError as e:
506-
if index != "all":
507-
raise Warning(e)
508-
509-
warnings = []
510-
511517
if index == "all":
512-
for i in range(len(self.mods)):
513-
if self.mods[i].visible:
514-
try:
515-
# Activate the mod, unless it's an unconfigured fomod.
516-
self.set_mod_state(i, True)
517-
except Warning as e:
518-
warnings.append(e)
519-
else:
520-
self.set_mod_state(index, True)
518+
return self.activate_mod_all()
521519

522-
self.stage()
523-
if warnings:
524-
raise Warning("\n".join(set([i.args[0] for i in warnings])))
520+
try:
521+
self.set_mod_state(int(index), True)
522+
self.stage()
523+
except ValueError as e:
524+
raise Warning(e)
525525

526526
def do_activate(self, component: ComponentMove, index: Union[int, str]) -> None:
527527
"""
@@ -535,24 +535,25 @@ def do_activate(self, component: ComponentMove, index: Union[int, str]) -> None:
535535
f"Expected one of {list(ComponentMove)} but got '{component}'"
536536
)
537537

538+
def deactivate_mod_all(self) -> None:
539+
for i, mod in enumerate(self.mods):
540+
if not mod.visible:
541+
continue
542+
self.set_mod_state(i, False)
543+
self.stage()
544+
538545
def deactivate_mod(self, index: Union[int, str]) -> None:
539546
"""
540547
Diabled mods will not be loaded by the game.
541548
"""
542-
try:
543-
int(index)
544-
except ValueError as e:
545-
if index != "all":
546-
raise Warning(e)
547-
548549
if index == "all":
549-
for i in range(len(self.mods)):
550-
if self.mods[i].visible:
551-
self.set_mod_state(i, False)
552-
else:
553-
self.set_mod_state(index, False)
550+
return self.deactivate_mod_all()
554551

555-
self.stage()
552+
try:
553+
self.set_mod_state(int(index), False)
554+
self.stage()
555+
except ValueError as e:
556+
raise Warning(e)
556557

557558
def do_deactivate(self, component: ComponentMove, index: Union[int, str]) -> None:
558559
"""
@@ -633,6 +634,16 @@ def remove_tag(self, mod: Mod, tag: str) -> None:
633634
if mod.tags != original_tags:
634635
self.changes = True
635636

637+
def tag_all(self, command: TagOperation, tag_name: str) -> None:
638+
for mod in self.mods:
639+
if not mod.visible:
640+
continue
641+
match command:
642+
case TagOperation.ADD:
643+
self.add_tag(mod, tag_name)
644+
case TagOperation.REMOVE:
645+
self.remove_tag(mod, tag_name)
646+
636647
def do_tag(
637648
self, command: TagOperation, index: Union[int, str], tag_name: str
638649
) -> None:
@@ -642,29 +653,22 @@ def do_tag(
642653
self.interface_mode = InterfaceMode.TAGS
643654

644655
if index == "all":
645-
for mod in self.mods:
646-
if not mod.visible:
647-
continue
648-
match command:
649-
case TagOperation.ADD:
650-
self.add_tag(mod, tag_name)
651-
case TagOperation.REMOVE:
652-
self.remove_tag(mod, tag_name)
653-
else:
654-
try:
655-
mod = self.mods[index]
656-
except ValueError as e:
657-
raise Warning(e)
656+
return self.tag_all(command, tag_name)
658657

659-
if not mod.visible:
660-
raise Warning("You can only tag against visible mods.")
658+
try:
659+
mod = self.mods[int(index)]
660+
except (ValueError, IndexError) as e:
661+
raise Warning(e)
661662

662-
match command:
663-
case TagOperation.ADD:
664-
self.add_tag(mod, tag_name)
663+
if not mod.visible:
664+
raise Warning("You can only tag against visible mods.")
665665

666-
case TagOperation.REMOVE:
667-
self.remove_tag(mod, tag_name)
666+
match command:
667+
case TagOperation.ADD:
668+
self.add_tag(mod, tag_name)
669+
670+
case TagOperation.REMOVE:
671+
self.remove_tag(mod, tag_name)
668672

669673
def do_display(self, interface_mode: InterfaceMode) -> None:
670674
"""
@@ -956,64 +960,66 @@ def do_rename(self, component: ComponentWrite, index: int, name: str) -> None:
956960
f"Expected one of {list(ComponentWrite)}, got '{component}'"
957961
)
958962

963+
def delete_mod_all(self) -> None:
964+
deleted_mods = ""
965+
visible_mods = [i for i in self.mods if i.visible]
966+
# Don't allow deleting mods with "all" unless they're inactive.
967+
for mod in visible_mods:
968+
if mod.enabled:
969+
raise Warning(
970+
"You can only delete all visible components if they are all deactivated."
971+
)
972+
for target_mod in visible_mods:
973+
# Deactivate the mod here in case set_mod_state is overridden and
974+
# provides any cleanup to the child (like removing plugins).
975+
# Then we don't have to override this in children which use plugins.
976+
self.set_mod_state(self.mods.index(target_mod), False)
977+
self.mods.remove(target_mod)
978+
with ignored(FileNotFoundError):
979+
log.info(f"Deleting MOD: {target_mod.name}")
980+
if target_mod.location.is_symlink():
981+
target_mod.location.unlink()
982+
else:
983+
shutil.rmtree(target_mod.location)
984+
deleted_mods += f"{target_mod.name}\n"
985+
self.do_commit()
986+
959987
@requires_sync
960988
def delete_mod(self, index: Union[int, str]) -> None:
961989
"""
962990
Removes specified mod from the filesystem.
963991
"""
964992
try:
965-
index = int(index)
993+
int(index)
966994
except ValueError as e:
967995
if index != "all":
968996
raise Warning(e)
969997

970998
if index == "all":
971-
deleted_mods = ""
972-
visible_mods = [i for i in self.mods if i.visible]
973-
# Don't allow deleting mods with "all" unless they're inactive.
974-
for mod in visible_mods:
975-
if mod.enabled:
976-
raise Warning(
977-
"You can only delete all visible components if they are all deactivated."
978-
)
979-
for target_mod in visible_mods:
980-
# Deactivate the mod here in case set_mod_state is overridden and
981-
# provides any cleanup to the child (like removing plugins).
982-
# Then we don't have to override this in children which use plugins.
983-
self.set_mod_state(self.mods.index(target_mod), False)
984-
self.mods.remove(target_mod)
985-
with ignored(FileNotFoundError):
986-
log.info(f"Deleting MOD: {target_mod.name}")
987-
if target_mod.location.is_symlink():
988-
target_mod.location.unlink()
989-
else:
990-
shutil.rmtree(target_mod.location)
991-
deleted_mods += f"{target_mod.name}\n"
992-
self.do_commit()
993-
else:
994-
try:
995-
target_mod = self.mods[index]
999+
return self.delete_mod_all()
9961000

997-
except IndexError as e:
998-
raise Warning(e)
1001+
try:
1002+
target_mod = self.mods[int(index)]
1003+
except (ValueError, IndexError) as e:
1004+
raise Warning(e)
9991005

1000-
if not target_mod.visible:
1001-
raise Warning("You can only delete visible components.")
1006+
if not target_mod.visible:
1007+
raise Warning("You can only delete visible components.")
10021008

1003-
originally_active = target_mod.enabled
1009+
originally_active = target_mod.enabled
10041010

1005-
# Remove the mod from the controller then delete it.
1006-
self.set_mod_state(self.mods.index(target_mod), False)
1007-
self.mods.pop(index)
1008-
with ignored(FileNotFoundError):
1009-
log.info(f"Deleting MOD: {target_mod.name}")
1010-
if target_mod.location.is_symlink():
1011-
target_mod.location.unlink()
1012-
else:
1013-
shutil.rmtree(target_mod.location)
1011+
# Remove the mod from the controller then delete it.
1012+
self.set_mod_state(self.mods.index(target_mod), False)
1013+
self.mods.pop(int(index))
1014+
with ignored(FileNotFoundError):
1015+
log.info(f"Deleting MOD: {target_mod.name}")
1016+
if target_mod.location.is_symlink():
1017+
target_mod.location.unlink()
1018+
else:
1019+
shutil.rmtree(target_mod.location)
10141020

1015-
if originally_active:
1016-
self.do_commit()
1021+
if originally_active:
1022+
self.do_commit()
10171023

10181024
@requires_sync
10191025
def delete_download(self, index: Union[int, str]) -> None:

0 commit comments

Comments
 (0)