diff --git a/Mopy/bash/basher/__init__.py b/Mopy/bash/basher/__init__.py index 5b0edf0388e..6732c66ab13 100644 --- a/Mopy/bash/basher/__init__.py +++ b/Mopy/bash/basher/__init__.py @@ -56,7 +56,7 @@ from collections import OrderedDict, defaultdict, namedtuple from collections.abc import Iterable from functools import partial -from itertools import chain +from itertools import chain, repeat import wx @@ -520,21 +520,19 @@ def SetFileInfo(self,fileInfo): #--Fill data and populate self._update_real_indices(fileInfo) self.is_inaccurate = fileInfo.has_inaccurate_masters - if isinstance(fileInfo, bosh.ModInfo): - can_have_sizes = bush.game.Esp.check_master_sizes - can_have_esl = False - else: - can_have_sizes = False - can_have_esl = bush.game.has_esl + # info attributes? + can_have_sizes = (is_mod := isinstance(fileInfo, bosh.ModInfo)) and \ + bush.game.Esp.check_master_sizes + can_have_esl = (not is_mod) and bush.game.has_esl all_esl_masters = (set(fileInfo.header.masters_esl) if can_have_esl - else None) + else set()) all_master_sizes = (fileInfo.header.master_sizes if can_have_sizes - else None) - for mi, ma_name in enumerate(fileInfo.masterNames): - ma_size = all_master_sizes[mi] if can_have_sizes else 0 - ma_esl = can_have_esl and ma_name in all_esl_masters + else repeat(0)) + for mi, (ma_name, ma_size) in enumerate( + zip(fileInfo.masterNames, all_master_sizes)): self.data_store[mi] = bosh.MasterInfo(parent_minf=fileInfo, - master_name=ma_name, master_size=ma_size, was_esl=ma_esl) + master_name=ma_name, master_size=ma_size, + was_esl=ma_name in all_esl_masters) self._reList() def set_item_format(self, item_key, item_format, target_ini_setts): diff --git a/Mopy/bash/basher/installer_links.py b/Mopy/bash/basher/installer_links.py index 7b473e4760c..4ec201511a3 100644 --- a/Mopy/bash/basher/installer_links.py +++ b/Mopy/bash/basher/installer_links.py @@ -409,7 +409,7 @@ def _apply_tweaks(self, installer, ret, ui_refresh): # actually installed. Since BAIN is setup to not auto # install after the wizard, we'll show a message telling # the User what tweaks to apply manually. - manuallyApply.append((outFile, iniFile)) + manuallyApply.append((outFile.stail, iniFile)) continue target_ini_file = bosh.BestIniFile(target_path) if INIList.apply_tweaks((bosh.iniInfos[outFile.stail],), @@ -423,15 +423,13 @@ def _apply_tweaks(self, installer, ret, ui_refresh): target_path.stail, reset_choices=target_updated) ini_uilist.panel.ShowPanel(focus_list=False, detail_item=lastApplied) - ui_refresh[Store.INIS] = False - if len(manuallyApply) > 0: - message = _('The following INI Tweaks were not automatically ' - 'applied. Be sure to apply them after installing the ' - 'package.') - message += u'\n\n' - message += u'\n'.join([u' * %s\n TO: %s' % (x[0].stail, x[1]) - for x in manuallyApply]) - self._showInfo(message) + ui_refresh[Store.INIS] = False # None or we refreshed in ShowPanel + if manuallyApply: + message = [_('The following INI Tweaks were not automatically ' + 'applied. Be sure to apply them after installing the ' + 'package.'), '', ''] + message.extend(f' * {x}\n TO: {y}' for x, y in manuallyApply) + self._showInfo('\n'.join(message)) class Installer_OpenReadme(_SingleInstallable): """Opens the installer's readme if BAIN can find one.""" diff --git a/Mopy/bash/basher/mod_links.py b/Mopy/bash/basher/mod_links.py index 81cf9cc0fc8..d783297ab05 100644 --- a/Mopy/bash/basher/mod_links.py +++ b/Mopy/bash/basher/mod_links.py @@ -186,9 +186,8 @@ class Mod_CreateDummyMasters(OneItemLink, _LoadLink): 'ck_name': bush.game.Ck.long_name, } - def _enable(self): - return super(Mod_CreateDummyMasters, self)._enable() and \ - self._selected_info.getStatus() == 30 # Missing masters + def _enable(self): # enable if there are missing masters + return super()._enable() and self._selected_info.getStatus() == 30 def Execute(self): """Create Dummy Masters""" @@ -207,7 +206,7 @@ def Execute(self): continue # Missing master, create a dummy plugin for it newInfo = bosh.ModInfo(self._selected_info.info_dir.join(master)) - to_refresh.append((master, newInfo, previous_master)) + to_refresh.append((master, previous_master)) previous_master = master newFile = ModFile(newInfo, self._load_fact()) ### newFile.tes4.author = u'BASHED DUMMY' @@ -221,7 +220,7 @@ def Execute(self): newFile.tes4.flags1.esl_flag = True newFile.safeSave() to_select = [] - for mod, info, previous in to_refresh: + for mod, previous in to_refresh: # add it to modInfos or lo_insert_after blows for timestamp games bosh.modInfos.new_info(mod, notify_bain=True, _in_refresh=True) bosh.modInfos.cached_lo_insert_after(previous, mod) diff --git a/Mopy/bash/basher/saves_links.py b/Mopy/bash/basher/saves_links.py index 7d6e6cd520e..2b5e5f5eec9 100644 --- a/Mopy/bash/basher/saves_links.py +++ b/Mopy/bash/basher/saves_links.py @@ -369,7 +369,7 @@ def Execute(self): else: message = u'' if missing: - message += u'=== '+_(u'Removed Masters')+u' (%s):\n* ' % oldName + message += '=== ' + _('Removed Masters') + f' ({oldName}):\n* ' message += u'\n* '.join(load_order.get_ordered(missing)) if added: message += u'\n\n' if added: @@ -698,7 +698,7 @@ def _move_saves(self, destDir, profile: str | None): if not result: continue ask = ask and result != 2 # so don't warn for rest of operation if self.copyMode: - bosh.saveInfos.copy_info(fileName, destDir) + bosh.saveInfos.copy_info(fileName, destDir, fileName) if fileName in savesTable: destTable[fileName] = savesTable[fileName] else: diff --git a/Mopy/bash/bolt.py b/Mopy/bash/bolt.py index 789869446a9..baff3799231 100644 --- a/Mopy/bash/bolt.py +++ b/Mopy/bash/bolt.py @@ -433,10 +433,10 @@ def __init__(self, wrapped_func): self._wrapped_func = wrapped_func self._wrapped_attr = None # set later - def __set_name__(self, owner, name): + def __set_name__(self, owner_, name): self._wrapped_attr = name - def __get__(self, instance, owner=None): + def __get__(self, instance, owner_=None): try: wrapped_val = instance.__dict__[self._wrapped_attr] except KeyError: @@ -450,8 +450,8 @@ class classproperty: def __init__(self, fget): self.fget = fget - def __get__(self, obj, owner): - return self.fget(owner) + def __get__(self, obj, owner_): + return self.fget(owner_) # JSON parsing ---------------------------------------------------------------- class JsonParsable: diff --git a/Mopy/bash/bosh/__init__.py b/Mopy/bash/bosh/__init__.py index 7b983f17ba4..d296da0f779 100644 --- a/Mopy/bash/bosh/__init__.py +++ b/Mopy/bash/bosh/__init__.py @@ -126,11 +126,12 @@ class MasterInfo: __slots__ = ('is_ghost', 'curr_name', 'mod_info', 'old_name', 'stored_size', '_was_esl', 'parent_mod_info') - def __init__(self, *, parent_minf, master_name, master_size, was_esl): + def __init__(self, *, parent_minf, master_name: FName, master_size, + was_esl): self.parent_mod_info = parent_minf self.stored_size = master_size self._was_esl = was_esl - self.old_name = FName(master_name) + self.old_name = master_name self.mod_info = self.rename_if_present(master_name) if self.mod_info is None: self.curr_name = FName(master_name) @@ -213,7 +214,6 @@ def _stat_tuple(self): return self.abs_path.size_mtime_ctime() def __init__(self, fullpath, load_cache=False, **kwargs): self.header = None self.masterNames: tuple[FName, ...] = () - self.masterOrder: tuple[FName, ...] = () self.madeBackup = False # True if the masters for this file are not reliable self.has_inaccurate_masters = False @@ -224,7 +224,6 @@ def __init__(self, fullpath, load_cache=False, **kwargs): def _reset_masters(self): #--Master Names/Order self.masterNames = tuple(self._get_masters()) - self.masterOrder = tuple() #--Reset to empty for now def _file_changed(self, stat_tuple): return (self.fsize, self.ftime, self.ctime) != stat_tuple @@ -295,25 +294,22 @@ def makeBackup(self, forceBackup=False): if self.madeBackup and not forceBackup: return #--Backup cop = self.get_store().copy_info - cop(self.fn_key, self.backup_dir) + cop(self.fn_key, self.backup_dir, self.fn_key) #--First backup firstBackup = self.backup_dir.join(self.fn_key) + 'f' if not firstBackup.exists(): cop(self.fn_key, self.backup_dir, firstBackup.tail) self.madeBackup = True - def backup_restore_paths(self, first=False, fname=None): + def backup_restore_paths(self, first, fname=None) -> list[tuple[Path, Path]]: """Return a list of tuples, mapping backup paths to their restore destinations. If fname is not given returns the (first) backup filename corresponding to self.abs_path, else the backup filename - for fname mapped to its restore location in data_store.store_dir - :rtype: list[tuple] - """ + for fname mapped to its restore location in data_store.store_dir.""" restore_path = (fname and self.get_store().store_dir.join( fname)) or self.abs_path fname = fname or self.fn_key - return [(self.backup_dir.join(fname) + (u'f' if first else u''), - restore_path)] + return [(self.backup_dir.join(fname + 'f' * first), restore_path)] def all_backup_paths(self, fname=None): """Return the list of all possible paths a backup operation may create. @@ -1041,15 +1037,14 @@ def get_rename_paths(self, newName): return old_new_paths def _masters_order_status(self, status): - self.masterOrder = tuple(load_order.get_ordered(self.masterNames)) - loads_before_its_masters = self.masterOrder and \ - load_order.cached_lo_index( - self.masterOrder[-1]) > load_order.cached_lo_index(self.fn_key) - if self.masterOrder != self.masterNames and loads_before_its_masters: + mo = tuple(load_order.get_ordered(self.masterNames)) # masterOrder + loads_before_its_masters = mo and load_order.cached_lo_index( + mo[-1]) > load_order.cached_lo_index(self.fn_key) + if mo != self.masterNames and loads_before_its_masters: return 21 elif loads_before_its_masters: return 20 - elif self.masterOrder != self.masterNames: + elif mo != self.masterNames: return 10 else: return status @@ -1271,17 +1266,17 @@ def __init__(self, fullpath, load_cache=False, **kwargs): def get_store(cls): return saveInfos def _masters_order_status(self, status): - self.masterOrder = tuple(load_order.get_ordered(self.masterNames)) - if self.masterOrder != self.masterNames: + mo = tuple(load_order.get_ordered(self.masterNames)) + if mo != self.masterNames: return 20 # Reordered masters are far more important in saves elif status > 0: # Missing or reordered masters -> orange or red return status active_tuple = load_order.cached_active_tuple() - if self.masterOrder == active_tuple: + if mo == active_tuple: # Exact match with LO -> purple return -20 - if self.masterOrder == active_tuple[:len(self.masterOrder)]: + if mo == active_tuple[:len(mo)]: # Matches LO except for new plugins at the end -> blue return -10 else: @@ -1355,7 +1350,7 @@ def get_cosave_tags(self): abs(inst.abs_path.mtime - self.ftime) < 10] return u'\n'.join(co_ui_strings) - def backup_restore_paths(self, first=False, fname=None): + def backup_restore_paths(self, first, fname=None): """Return as parent and in addition back up paths for the cosaves.""" back_to_dest = super(SaveInfo, self).backup_restore_paths(first, fname) # see if we have cosave backups - we must delete cosaves when restoring @@ -1410,7 +1405,7 @@ def _get_masters(self): return [*map(FName, xse_cosave.get_master_list())] # Fall back on the regular masters - either the cosave is unnecessary, # doesn't exist or isn't accurate - return self.header.masters + return [*map(FName, self.header.masters)] def has_circular_masters(self, *, fake_masters: list[FName] | None = None): return False # Saves can't have circular masters @@ -1608,7 +1603,8 @@ def refresh(self, refresh_infos=True, booting=False): self.corrupted[new] = cor = Corrupted(cor_path, er, **kws) deprint(f'Failed to load {new} from {cor.abs_path}: {er}', traceback=True) - self.pop(new, None) + if new := self.pop(new, None): # effectively deleted + del_infos.add(new) rdata.to_del = {d.fn_key for d in del_infos} self.delete_refresh(del_infos, check_existence=False) if rdata.redraw: @@ -1712,7 +1708,7 @@ def rename_operation(self, member_info, newName): return res #--Copy - def copy_info(self, fileName, destDir, destName=empty_path, set_mtime=None, + def copy_info(self, fileName, destDir, destName, set_mtime=None, save_lo_cache=False): """Copies member file to destDir. Will overwrite! Will update internal self._data for the file if copied inside self.store_dir but the @@ -1723,7 +1719,6 @@ def copy_info(self, fileName, destDir, destName=empty_path, set_mtime=None, :param set_mtime: if None self[fileName].ftime is copied to destination """ destDir.makedirs() - if not destName: destName = fileName src_info = self[fileName] if destDir == self.store_dir and destName in self: destPath = self[destName].abs_path @@ -1731,10 +1726,9 @@ def copy_info(self, fileName, destDir, destName=empty_path, set_mtime=None, destPath = destDir.join(destName) self._do_copy(src_info, destPath) if destDir == self.store_dir: - self._add_info(destDir, destName, set_mtime, fileName, - save_lo_cache) + self._add_info(destName, set_mtime, fileName, save_lo_cache) - def _add_info(self, destDir, destName, set_mtime, fileNam, save_lo_cache): + def _add_info(self, destName, set_mtime, fileNam, save_lo_cache): # TODO(ut) : in duplicate pass the info in and load_cache=False return self.new_info(destName, notify_bain=True) @@ -3172,9 +3166,8 @@ def move_infos(self, sources, destinations, window, bash_frame): bash_frame.warn_corrupted(warn_mods=True, warn_strings=True) return moved - def _add_info(self, destDir, destName, set_mtime, fileName, - save_lo_cache=None): - inf = super()._add_info(destDir, destName, set_mtime, fileName, save_lo_cache) + def _add_info(self, destName, set_mtime, fileName, save_lo_cache): + inf = super()._add_info(destName, set_mtime, fileName, save_lo_cache) if set_mtime is not None: inf.setmtime(set_mtime) # correctly update table self.cached_lo_insert_after(fileName, destName) @@ -3414,12 +3407,12 @@ def rename_operation(self, member_info, newName): co_file.abs_path = co_type.get_cosave_path(self[newName].abs_path) return old_key - def copy_info(self, fileName, destDir, destName=empty_path, set_mtime=None, + def copy_info(self, fileName, destDir, destName, set_mtime=None, save_lo_cache=False): """Copies savefile and associated cosaves file(s).""" super().copy_info(fileName, destDir, destName, set_mtime) self.co_copy_or_move(self[fileName]._co_saves, - destDir.join(destName or fileName)) + destDir.join(destName)) @staticmethod def co_copy_or_move(co_instances, dest_path: Path, move_cosave=False): @@ -3439,7 +3432,6 @@ def move_infos(self, sources, destinations, window, bash_frame): if FName(d.stail) in moved: co_instances = SaveInfo.get_cosaves_for_path(s) self.co_copy_or_move(co_instances, d, move_cosave=True) - break for m in moved: try: self.new_info(m, notify_bain=True) ##: why True?? diff --git a/Mopy/bash/bosh/bain.py b/Mopy/bash/bosh/bain.py index 561039d6630..0f665cdaeac 100644 --- a/Mopy/bash/bosh/bain.py +++ b/Mopy/bash/bosh/bain.py @@ -64,19 +64,17 @@ def _remove_empty_dirs(root_dir): root_dir, folders, files = next(os.walk(root_dir)) if not files and not folders: return True # empty - possible_empty = [] + empty_dirs = [] if folders: # root_dir should be an absolute path root_gpath = GPath_no_norm(root_dir) for fol in folders: if _remove_empty_dirs(fol_abs := root_gpath.join(fol)): - possible_empty.append(fol_abs) + empty_dirs.append(fol_abs) else: files = True - if files: - for empty in possible_empty: - empty.removedirs() - return False - return True + for empty in empty_dirs: + empty.removedirs() + return not files def _scandir_walk(apath, *, __root_len=None, __folders_times=None): """Recursively walk the project dir - only used in InstallerProject.""" @@ -2922,7 +2920,7 @@ def clean_data_dir(self, ci_removes, refresh_ui): try: full_path.moveTo(destDir.join(ci_rel_path)) # will drop .ghost if store is not None: - store_del[store].add(store_inf.fn_key) + store_del[store].add(store_inf) self.data_sizeCrcDate.pop(ci_rel_path, None) emptyDirs.add(full_path.head) except (StateError, OSError): diff --git a/Mopy/bash/bosh/save_headers.py b/Mopy/bash/bosh/save_headers.py index d5ebed0a020..a26f32f4167 100644 --- a/Mopy/bash/bosh/save_headers.py +++ b/Mopy/bash/bosh/save_headers.py @@ -36,6 +36,7 @@ import zlib from enum import Enum from functools import partial +from itertools import repeat import lz4.block @@ -461,22 +462,19 @@ def _load_masters_16(self, ins, sse_offset=0): masters_expected = unpack_int(ins) # Store separate lists of regular and ESLs masters for the Indices # column on the Saves tab - self.masters_regular = [] - self.masters_esl = [] num_regular_masters = unpack_byte(ins) - append_regular = self.masters_regular.append - for count in range(num_regular_masters): - append_regular(unpack_str16(ins)) + self.masters_regular = [ + *map(unpack_str16, repeat(ins, num_regular_masters))] # SSE / FO4 save format with esl block if self._esl_block(): num_esl_masters = unpack_short(ins) - # Remember if we had ESL masters for the inacurracy warning + # Remember if we had ESL masters for the inaccuracy warning self.has_esl_masters = num_esl_masters > 0 - append_esl = self.masters_esl.append - for count in range(num_esl_masters): - append_esl(unpack_str16(ins)) + self.masters_esl = [ + *map(unpack_str16, repeat(ins, num_esl_masters))] else: self.has_esl_masters = False + self.masters_esl = [] # Check for master's table size (-4 for the stored size at the start of # the master table) masters_actual = ins.tell() + sse_offset - self._mastersStart - 4 diff --git a/Mopy/bash/game/fallout4/records.py b/Mopy/bash/game/fallout4/records.py index 8d8058ee036..67677f272cb 100644 --- a/Mopy/bash/game/fallout4/records.py +++ b/Mopy/bash/game/fallout4/records.py @@ -3171,16 +3171,17 @@ class MreSpgd(MelRecord): MelEdid(), # What on earth did you do to this struct, Bethesda? It was so nice and # normal in Skyrim... - MelExtra(MelStruct(b'DATA', - ['f', '4s', 'f', '4s', '3f', '4s', 'f', '4s', 'f', '4s', 'f', '4s', - 'I', '4s', 'I', '4s', 'I', '4s', 'I', '4s', 'f'], - 'gravity_velocity', 'unknown1', 'rotation_velocity', 'unknown2', - 'particle_size_x', 'center_offset_min1', 'particle_size_y', - 'unknown3', 'center_offset_min2', 'unknown4', 'center_offset_max', - 'unknown5', 'initial_rotation', 'unknown6', 'num_subtextures_x', - 'unknown7', 'num_subtextures_y', 'unknown8', 'spgd_type', - 'unknown9', 'spgd_box_size', 'unknown10', 'particle_density'), - extra_attr='unknown11'), + ##: Might need to be a MelTruncatedStruct with the last 4s chopped off, + # keep an eye out in the future + MelStruct(b'DATA', + ['f', '4s', 'f', '4s', 'f', '4s', 'f', '4s', 'f', '4s', 'f', '4s', + 'f', '4s', 'I', '4s', 'I', '4s', 'I', '4s', 'I', '4s', 'f', '4s'], + 'gravity_velocity', 'unused1', 'rotation_velocity', 'unused2', + 'particle_size_x', 'unused3', 'particle_size_y', 'unused4', + 'center_offset_min2', 'unused5', 'center_offset_max', 'unused6', + 'initial_rotation', 'unused7', 'num_subtextures_x', 'unused8', + 'num_subtextures_y', 'unused9', 'spgd_type', 'unused10', + 'spgd_box_size', 'unused11', 'particle_density', 'unused12'), MelString(b'MNAM', 'spgd_particle_texture'), ) diff --git a/Mopy/bash/game/skyrim/records.py b/Mopy/bash/game/skyrim/records.py index 1c4a18c3375..8cc5682a9a1 100644 --- a/Mopy/bash/game/skyrim/records.py +++ b/Mopy/bash/game/skyrim/records.py @@ -1150,6 +1150,7 @@ class HeaderFlags(MelRecord.HeaderFlags): has_distant_lod: bool = flag(15) random_anim_start: bool = flag(16) is_marker: bool = flag(23) + obstacle: bool = flag(25) melSet = MelSet( MelEdid(), @@ -1404,6 +1405,7 @@ class HeaderFlags(MelRecord.HeaderFlags): has_distant_lod: bool = flag(15) random_anim_start: bool = flag(16) is_marker: bool = flag(23) + obstacle: bool = flag(25) must_exit_to_talk: bool = flag(28) child_can_use: bool = flag(29) @@ -3415,6 +3417,7 @@ class MreTree(MelRecord): class HeaderFlags(MelRecord.HeaderFlags): has_distant_lod: bool = flag(15) + obstacle: bool = flag(25) melSet = MelSet( MelEdid(), diff --git a/Mopy/bash/patcher/patchers/multitweak_settings.py b/Mopy/bash/patcher/patchers/multitweak_settings.py index 4bd62865425..66446482a79 100644 --- a/Mopy/bash/patcher/patchers/multitweak_settings.py +++ b/Mopy/bash/patcher/patchers/multitweak_settings.py @@ -1995,7 +1995,7 @@ class GmstTweak_Combat_BlockTimeAverage(_AGmstCCTweak): tweak_tip = _('The average time for which NPCs will keep their shield ' 'raised or block with their weapon during combat.') tweak_key = ('fCombatBlockTimeMid',) - tweak_choices = [(_('2.5 Seconds'), 1.5), + tweak_choices = [(_('2.5 Seconds'), 2.5), (_('4 Seconds'), 4.0), (_('8 Seconds'), 8.0), (_('12 Seconds'), 12.0), diff --git a/Mopy/bash/tests/test_bosh/test_bain.py b/Mopy/bash/tests/test_bosh/test_bain.py new file mode 100644 index 00000000000..3f8c294b0e4 --- /dev/null +++ b/Mopy/bash/tests/test_bosh/test_bain.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# +# GPL License and Copyright Notice ============================================ +# This file is part of Wrye Bash. +# +# Wrye Bash is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# Wrye Bash is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Wrye Bash; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# Wrye Bash copyright (C) 2005-2009 Wrye, 2010-2024 Wrye Bash Team +# https://github.com/wrye-bash +# +# ============================================================================= +import os + +from ...bosh.bain import _remove_empty_dirs +from ...wbtemp import TempDir + +def test__remove_empty_dirs(): + with TempDir() as tempdir: + os.mkdir(tex := os.path.join(tempdir, 'textures')) + os.mkdir(cl := os.path.join(tex, 'clothes')) + os.mkdir(os.path.join(cl, 'farmclothes02')) + _remove_empty_dirs(tex) + assert not os.path.exists(cl) diff --git a/Mopy/bash/update_checker.py b/Mopy/bash/update_checker.py index 22293565580..c2d1bc6a89d 100644 --- a/Mopy/bash/update_checker.py +++ b/Mopy/bash/update_checker.py @@ -43,7 +43,7 @@ except ImportError as e: deprint(f'requests not installed, update checking functionality will not ' f'be available (error: {e})') - ARestHandler = None + ARestHandler = object can_check_updates = False # This won't work if packaging isn't installed - in that case, we simply have @@ -128,10 +128,11 @@ class _GitHub(ARestHandler): core_used: int | None def __init__(self): - super().__init__(extra_headers={ - 'accept': 'application/vnd.github+json', - 'x-github-api-version': _GITHUB_API_VERSION, - }) + if can_check_updates: + super().__init__(extra_headers={ + 'accept': 'application/vnd.github+json', + 'x-github-api-version': _GITHUB_API_VERSION, + }) # Rate limiting information - set to None (= unknown) by default self.core_limit = None self.core_remaining = None diff --git a/Mopy/bash/web.py b/Mopy/bash/web.py index 791d6a07b5d..d09da84871e 100644 --- a/Mopy/bash/web.py +++ b/Mopy/bash/web.py @@ -77,8 +77,8 @@ class _RestOp(Enum): # Public API ------------------------------------------------------------------ class ARestHandler: - """An abstract base class for APIs that need to use .""" - # The base URL for the REST API you're using. Set in + """An abstract base class for web APIs that use REST-like semantics.""" + # The base URL for the REST API you're using. Set statically in subclasses _base_url: str def __init__(self, extra_headers: dict[str, str] | None = None): diff --git a/requirements-scripts.txt b/requirements-scripts.txt index 76044f2cc60..af8970e4a61 100644 --- a/requirements-scripts.txt +++ b/requirements-scripts.txt @@ -4,6 +4,6 @@ isort==5.13.2 pyfiglet==1.0.2 pygit2==1.14.1 -PyGithub==2.2.0 -pyinstaller==6.4.0 -pytest==8.0.2 +PyGithub==2.3.0 +pyinstaller==6.6.0 +pytest==8.2.0 diff --git a/requirements.txt b/requirements.txt index aae74e98c23..26156fad5f3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,20 +9,20 @@ wxPython==4.2.1; sys_platform != 'win32' https://raw.githubusercontent.com/wrye-bash/dev-tools/master/wheels/wxPython-4.2.2a1-cp312-cp312-win_amd64.whl; sys_platform == 'win32' # Runtime, recommended -------------------------------------------------------- # For FOMOD schema validation -lxml==5.1.0 +lxml==5.2.1 # For parsing download conditions when checking for updates -packaging~=24.0 +packaging==24.0 # For custom launcher icons on non-Windows platforms pefile~=2023.2.7; sys_platform != 'win32' # For PDF support in the doc browser -PyMuPDF==1.23.26 +PyMuPDF==1.24.2 # For reflink copies (does not support Windows/ReFS yet) reflink==0.2.2; sys_platform != 'win32' # For various Internet-based functionality requests[use_chardet_on_py3]==2.31.0 # For sending files to the recycle bin on non-Windows platforms -send2trash==1.8.2; sys_platform != 'win32' +send2trash==1.8.3; sys_platform != 'win32' # For Nexus Mods integration -websocket-client==1.7.0 +websocket-client==1.8.0 # Compile/Build-time ---------------------------------------------------------- -r requirements-scripts.txt diff --git a/scripts/build.py b/scripts/build.py index 9ee8bce2f3b..7f0fe3b85e6 100755 --- a/scripts/build.py +++ b/scripts/build.py @@ -64,7 +64,7 @@ # distributable right now _NOT_WINDOWS = os.name != 'nt' -_NSIS_VERSION = '3.09' +_NSIS_VERSION = '3.10' if _NOT_WINDOWS: _EXE_7Z = '7z' else: