diff --git a/.mailmap b/.mailmap index 5acbd1b581..ff964a01d7 100644 --- a/.mailmap +++ b/.mailmap @@ -23,3 +23,6 @@ Derek Frogget <114030121+derekfrogget@users.noreply.github.c Nikhil Ghosh David Wever <56411717+dmwever@users.noreply.github.com> Ngô Xuân Minh +Nicolas Sanchez <98576999+nicolassanchez02@users.noreply.github.com> +Manas Pradhan <64654573+manas-maker@users.noreply.github.com> +Jason Lu <5738189+lugt@users.noreply.github.com> diff --git a/buildsystem/compilepy.py b/buildsystem/compilepy.py index 740b38876b..b584bdac1c 100644 --- a/buildsystem/compilepy.py +++ b/buildsystem/compilepy.py @@ -1,4 +1,4 @@ -# Copyright 2015-2022 the openage authors. See copying.md for legal info. +# Copyright 2015-2026 the openage authors. See copying.md for legal info. """ Compiles python modules with cpython to pyc/pyo files. @@ -96,7 +96,7 @@ def main(): maxwidth = len(str(len(to_compile))) for idx, (module, outputfile) in enumerate(to_compile): try: - print(f"[{idx+1:{maxwidth}}/{len(to_compile)}] " + print(f"[{idx + 1}:{maxwidth}/{len(to_compile)}] " f"Compiling {module} to {outputfile}") py_compile.compile(module, cfile=outputfile, doraise=True) diff --git a/buildsystem/cythonize.py b/buildsystem/cythonize.py index e7ac86ae6f..a5aa27f3c4 100755 --- a/buildsystem/cythonize.py +++ b/buildsystem/cythonize.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright 2015-2025 the openage authors. See copying.md for legal info. +# Copyright 2015-2026 the openage authors. See copying.md for legal info. """ Runs Cython on all modules that were listed via add_cython_module. @@ -13,6 +13,15 @@ from multiprocessing import cpu_count from pathlib import Path +# setuptools 60+ ships a distutils shim that re-exports the legacy distutils +# APIs (Extension, etc.). Importing setuptools before Cython registers the +# shim in sys.modules, which is what Cython 3.x's Build.Dependencies needs +# on the first cythonize() call. Without this, the import in Cython's +# 'from distutils.extension import Extension' fails on Python 3.12+ because +# distutils was removed from the stdlib. This is the official recommended +# workaround; see https://github.com/cython/cython/issues/4610 +import setuptools # noqa: F401 # pylint: disable=unused-import + from Cython.Build import cythonize diff --git a/copying.md b/copying.md index c18aa6bbcf..c3c6ebc67d 100644 --- a/copying.md +++ b/copying.md @@ -169,6 +169,7 @@ _the openage authors_ are: | | bytegrrrl | bytegrrrl à proton dawt me | | Nicolas Sanchez | nicolassanchez02 | nicolasjpsanchez à gmail dawt com | | Manas Pradhan | manas-maker | manasmpradhan5 à gmail dawt com | +| Jason Lu | jasonlu, lugt | lu.gt à 163 dawt com | If you're a first-time committer, add yourself to the above list. This is not just for legal reasons, but also to keep an overview of all those nicknames. diff --git a/etc/gdb_pretty/printers.py b/etc/gdb_pretty/printers.py index 9ae29539ce..795fd029d7 100644 --- a/etc/gdb_pretty/printers.py +++ b/etc/gdb_pretty/printers.py @@ -1,4 +1,4 @@ -# Copyright 2024-2025 the openage authors. See copying.md for legal info. +# Copyright 2024-2026 the openage authors. See copying.md for legal info. """ Pretty printers for GDB. @@ -59,8 +59,8 @@ def __call__(self, val: gdb.Value): return None -pp = PrinterControl('openage') -gdb.printing.register_pretty_printer(None, pp) +OPENAGE_PRINTER = PrinterControl('openage') +gdb.printing.register_pretty_printer(None, OPENAGE_PRINTER) def printer_typedef(type_name: str): @@ -74,7 +74,7 @@ def _register_printer(printer): """ Registers the printer with GDB. """ - pp.add_printer(type_name, printer) + OPENAGE_PRINTER.add_printer(type_name, printer) return _register_printer @@ -90,7 +90,7 @@ def _register_printer(printer): """ Registers the printer with GDB. """ - pp.add_printer_regex(regex, printer) + OPENAGE_PRINTER.add_printer_regex(regex, printer) return _register_printer diff --git a/etc/pylintrc b/etc/pylintrc index 717d1c0cbb..17586c748b 100644 --- a/etc/pylintrc +++ b/etc/pylintrc @@ -269,8 +269,14 @@ int-import-graph= [DESIGN] -# Maximum number of arguments for function / method -max-args=5 +# Maximum number of positional arguments for function / method. +# openage's convert subprocessor functions routinely need 6-11 positional +# args (converter_group, line, container_obj_ref, command_id, ranged, +# diff, ...). The codebase already opts out of the sister R0913 +# (too-many-arguments) per-function; raising the R0917 +# (too-many-positional-arguments) threshold to 12 lets those functions +# through without a refactor that would have no real side benefit. +max-positional-arguments=12 # Argument names that match this expression will be ignored. Default to name # with leading underscore diff --git a/kevinfile b/kevinfile index e4acd0e036..fe58cf1800 100644 --- a/kevinfile +++ b/kevinfile @@ -14,6 +14,13 @@ sanity_check: configure: - env: mode=debug compiler=gcc (? if job == "debian" ?) - env: mode=debug compiler=clang (? if job == "debian-clang" ?) + # Debian trixie's cython3 (3.0.x) and bare Python 3.13 are both + # incompatible with how Cython 3.x reaches into distutils (removed + # from the stdlib in 3.12). Pull a newer Cython (3.1+) and a + # current setuptools (provides the distutils shim Cython 3.x + # needs) via pip before configure. FindCython then picks the + # working Cython and the cythonize step has its shim. Idempotent. + python3 -m pip install --break-system-packages "cython>=3.1.0,<4.0.0" "setuptools>=68" ./configure --mode=${mode} --compiler=${compiler} --ccache --download-nyan # TODO: once all warnings are gone again, set --flags="-Werror" diff --git a/openage/__main__.py b/openage/__main__.py index ea5d1b6284..0c7f1dd135 100644 --- a/openage/__main__.py +++ b/openage/__main__.py @@ -196,6 +196,8 @@ def main(argv=None): multiprocessing.freeze_support() # openage is complicated and multithreaded; better not use fork. - multiprocessing.set_start_method('spawn') + # force=True: Python >= 3.13 raises if the start method was already + # implicitly fixed (e.g. by freeze_support() calling get_start_method()). + multiprocessing.set_start_method('spawn', force=True) sys.exit(main()) diff --git a/openage/codegen/codegen.py b/openage/codegen/codegen.py index 7696a837b4..0cdf8b5926 100644 --- a/openage/codegen/codegen.py +++ b/openage/codegen/codegen.py @@ -1,4 +1,4 @@ -# Copyright 2014-2022 the openage authors. See copying.md for legal info. +# Copyright 2014-2026 the openage authors. See copying.md for legal info. """ Utility and driver module for C++ code generation. @@ -86,8 +86,7 @@ def get_reads(self) -> None: Returns an iterable of all path component tuples for files that have been read. """ - for parts in self.reads: - yield parts + yield from self.reads self.reads.clear() diff --git a/openage/codegen/cpp_testlist.py b/openage/codegen/cpp_testlist.py index abfed4e533..6e1a4ac52c 100644 --- a/openage/codegen/cpp_testlist.py +++ b/openage/codegen/cpp_testlist.py @@ -1,4 +1,4 @@ -# Copyright 2015-2022 the openage authors. See copying.md for legal info. +# Copyright 2015-2026 the openage authors. See copying.md for legal info. """ Generates code for C++ testing, mostly the table to look up symbols from test @@ -44,8 +44,7 @@ def gen_prototypes(self): for namespacename, namespace in sorted(self.namespaces.items()): yield f"namespace {namespacename} {{\n" - for line in namespace.gen_prototypes(): - yield line + yield from namespace.gen_prototypes() yield f"}} // {namespacename}\n\n" def get_functionnames(self): @@ -53,8 +52,7 @@ def get_functionnames(self): Yields all function names in this namespace, as well as all subnamespaces. """ - for name in self.functions: - yield name + yield from self.functions for namespacename, namespace in sorted(self.namespaces.items()): for name in namespace.get_functionnames(): diff --git a/openage/convert/processor/conversion/aoc/ability_subprocessor.py b/openage/convert/processor/conversion/aoc/ability_subprocessor.py index 120541fe4c..88196989eb 100644 --- a/openage/convert/processor/conversion/aoc/ability_subprocessor.py +++ b/openage/convert/processor/conversion/aoc/ability_subprocessor.py @@ -1,4 +1,4 @@ -# Copyright 2020-2024 the openage authors. See copying.md for legal info. +# Copyright 2020-2026 the openage authors. See copying.md for legal info. # # pylint: disable=too-many-public-methods,too-many-lines,too-many-locals # pylint: disable=too-many-branches,too-many-statements,too-many-arguments @@ -254,6 +254,12 @@ def apply_continuous_effect_ability( "engine.ability.type.RangedContinuousEffect") # Effects + # `effects` and `allowed_types` are set inside the per-command branches + # below. Pre-initialise so the later add_raw_member calls are valid for + # command_ids we don't yet handle (e.g. a newly-introduced one); those + # paths get an empty ability that downstream code can ignore. + effects = None + allowed_types = [] if command_id == 101: # Construct effects = AoCEffectSubprocessor.get_construct_effects(line, ability_ref) @@ -554,6 +560,10 @@ def apply_discrete_effect_ability( "engine.ability.type.RangedDiscreteEffect") # Effects + # `effects` is set inside the per-command branches below; pre-initialise + # so the later add_raw_member is valid for command_ids we don't yet + # handle. + effects = None batch_ref = f"{ability_ref}.Batch" batch_raw_api_object = RawAPIObject(batch_ref, "Batch", dataset.nyan_api_objects) batch_raw_api_object.add_raw_parent("engine.util.effect_batch.type.UnorderedBatch") @@ -820,7 +830,7 @@ def collect_storage_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def collision_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the Collision ability to a line. @@ -2165,7 +2175,7 @@ def constructable_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def create_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the Create ability to a line. @@ -2239,7 +2249,7 @@ def create_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def death_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds a PassiveTransformTo ability to a line that is used to make entities die. @@ -2516,7 +2526,7 @@ def death_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def delete_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds a PassiveTransformTo ability to a line that is used to make entities die. @@ -2627,7 +2637,7 @@ def delete_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def despawn_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the Despawn ability to a line. @@ -2778,7 +2788,7 @@ def despawn_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def drop_resources_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the DropResources ability to a line. @@ -2885,7 +2895,7 @@ def drop_resources_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def drop_site_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the DropSite ability to a line. @@ -2962,7 +2972,7 @@ def drop_site_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def enter_container_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the EnterContainer ability to a line. @@ -3029,7 +3039,7 @@ def enter_container_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def exchange_resources_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the ExchangeResources ability to a line. @@ -3096,7 +3106,7 @@ def exchange_resources_ability(line: GenieGameEntityGroup) -> ForwardRef: return abilities - @ staticmethod + @staticmethod def exit_container_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the ExitContainer ability to a line. @@ -3150,7 +3160,7 @@ def exit_container_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def game_entity_stance_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the GameEntityStance ability to a line. @@ -3237,7 +3247,7 @@ def game_entity_stance_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def formation_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the Formation ability to a line. @@ -3320,7 +3330,7 @@ def formation_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def foundation_ability(line: GenieGameEntityGroup, terrain_id: int = -1) -> ForwardRef: """ Adds the Foundation abilities to a line. Optionally chooses the specified @@ -3364,7 +3374,7 @@ def foundation_ability(line: GenieGameEntityGroup, terrain_id: int = -1) -> Forw return ability_forward_ref - @ staticmethod + @staticmethod def gather_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the Gather abilities to a line. Unlike the other methods, this @@ -3601,7 +3611,7 @@ def gather_ability(line: GenieGameEntityGroup) -> ForwardRef: return abilities - @ staticmethod + @staticmethod def harvestable_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the Harvestable ability to a line. @@ -3982,7 +3992,7 @@ def harvestable_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def herd_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the Herd ability to a line. @@ -4034,7 +4044,7 @@ def herd_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def herdable_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the Herdable ability to a line. @@ -4074,7 +4084,7 @@ def herdable_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def idle_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the Idle ability to a line. @@ -4179,7 +4189,7 @@ def idle_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def live_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the Live ability to a line. @@ -4285,7 +4295,7 @@ def live_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def los_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the LineOfSight ability to a line. @@ -4344,7 +4354,7 @@ def los_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def move_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the Move ability to a line. @@ -4543,7 +4553,7 @@ def move_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def move_projectile_ability(line: GenieGameEntityGroup, position: int = -1) -> ForwardRef: """ Adds the Move ability to a projectile of the specified line. @@ -4640,7 +4650,7 @@ def move_projectile_ability(line: GenieGameEntityGroup, position: int = -1) -> F return ability_forward_ref - @ staticmethod + @staticmethod def named_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the Named ability to a line. @@ -4732,7 +4742,7 @@ def named_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def overlay_terrain_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the OverlayTerrain to a line. @@ -4773,7 +4783,7 @@ def overlay_terrain_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def pathable_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the Pathable ability to a line. @@ -4820,7 +4830,7 @@ def pathable_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def production_queue_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the ProductionQueue ability to a line. @@ -4880,7 +4890,7 @@ def production_queue_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def projectile_ability(line: GenieGameEntityGroup, position: int = 0) -> ForwardRef: """ Adds a Projectile ability to projectiles in a line. Which projectile should @@ -4991,7 +5001,7 @@ def projectile_ability(line: GenieGameEntityGroup, position: int = 0) -> Forward return ability_forward_ref - @ staticmethod + @staticmethod def provide_contingent_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the ProvideContingent ability to a line. @@ -5070,7 +5080,7 @@ def provide_contingent_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def rally_point_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the RallyPoint ability to a line. @@ -5099,7 +5109,7 @@ def rally_point_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def regenerate_attribute_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the RegenerateAttribute ability to a line. @@ -5180,7 +5190,7 @@ def regenerate_attribute_ability(line: GenieGameEntityGroup) -> ForwardRef: return [ability_forward_ref] - @ staticmethod + @staticmethod def regenerate_resource_spot_ability(line: GenieGameEntityGroup) -> None: """ Adds the RegenerateResourceSpot ability to a line. @@ -5192,7 +5202,7 @@ def regenerate_resource_spot_ability(line: GenieGameEntityGroup) -> None: """ # Unused in AoC - @ staticmethod + @staticmethod def remove_storage_ability(line) -> ForwardRef: """ Adds the RemoveStorage ability to a line. @@ -5242,7 +5252,7 @@ def remove_storage_ability(line) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def restock_ability(line: GenieGameEntityGroup, restock_target_id: int) -> ForwardRef: """ Adds the Restock ability to a line. @@ -5380,7 +5390,7 @@ def restock_ability(line: GenieGameEntityGroup, restock_target_id: int) -> Forwa return ability_forward_ref - @ staticmethod + @staticmethod def research_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the Research ability to a line. @@ -5456,7 +5466,7 @@ def research_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def resistance_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the Resistance ability to a line. @@ -5506,7 +5516,7 @@ def resistance_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def resource_storage_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the ResourceStorage ability to a line. @@ -5595,6 +5605,9 @@ def resource_storage_ability(line: GenieGameEntityGroup) -> ForwardRef: # The unit uses no gathering command or we don't recognize it continue + # `container_name` and `resource` are set by the gathering/trade + # branches below. If the unit neither gathers nor trades, skip it + # rather than fall through with undefined values. if line.is_gatherer(): gatherer_unit_id = gatherer.get_id() if gatherer_unit_id not in gather_lookup_dict: @@ -5607,6 +5620,9 @@ def resource_storage_ability(line: GenieGameEntityGroup) -> ForwardRef: # Trading container_name = "TradeContainer" + else: + continue + container_ref = f"{ability_ref}.{container_name}" container_raw_api_object = RawAPIObject(container_ref, container_name, @@ -5628,6 +5644,11 @@ def resource_storage_ability(line: GenieGameEntityGroup) -> ForwardRef: # No restriction for trading carry_capacity = MemberSpecialValue.NYAN_INF + else: + # Unreachable: the outer if/elif already filtered to gatherer + # or trader. Defensive default in case that logic changes. + carry_capacity = 0 + container_raw_api_object.add_raw_member("max_amount", carry_capacity, "engine.util.storage.ResourceContainer") @@ -5755,7 +5776,7 @@ def resource_storage_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def selectable_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds Selectable abilities to a line. Units will get two of these, @@ -5976,7 +5997,7 @@ def selectable_ability(line: GenieGameEntityGroup) -> ForwardRef: return abilities - @ staticmethod + @staticmethod def send_back_to_task_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the SendBackToTask ability to a line. @@ -6017,7 +6038,7 @@ def send_back_to_task_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def shoot_projectile_ability(line: GenieGameEntityGroup, command_id: int) -> ForwardRef: """ Adds the ShootProjectile ability to a line. @@ -6276,7 +6297,7 @@ def shoot_projectile_ability(line: GenieGameEntityGroup, command_id: int) -> For return ability_forward_ref - @ staticmethod + @staticmethod def stop_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the Stop ability to a line. @@ -6330,7 +6351,7 @@ def stop_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def storage_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the Storage ability to a line. @@ -6767,7 +6788,7 @@ def storage_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def terrain_requirement_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the TerrainRequirement to a line. @@ -6820,7 +6841,7 @@ def terrain_requirement_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def trade_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the Trade ability to a line. @@ -6884,7 +6905,7 @@ def trade_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def trade_post_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the TradePost ability to a line. @@ -6951,7 +6972,7 @@ def trade_post_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def transfer_storage_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the TransferStorage ability to a line. @@ -7030,7 +7051,7 @@ def transfer_storage_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def turn_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the Turn ability to a line. @@ -7104,7 +7125,7 @@ def turn_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def use_contingent_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the UseContingent ability to a line. @@ -7180,7 +7201,7 @@ def use_contingent_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def visibility_ability(line: GenieGameEntityGroup) -> ForwardRef: """ Adds the Visibility ability to a line. @@ -7299,7 +7320,7 @@ def visibility_ability(line: GenieGameEntityGroup) -> ForwardRef: return ability_forward_ref - @ staticmethod + @staticmethod def create_animation( line: GenieGameEntityGroup, animation_id: int, @@ -7355,7 +7376,7 @@ def create_animation( return animation_forward_ref - @ staticmethod + @staticmethod def create_civ_animation( line: GenieGameEntityGroup, civ_group: GenieCivilizationGroup, @@ -7465,7 +7486,7 @@ def create_civ_animation( [wrapper_forward_ref]) civ_group.add_raw_member_push(push_object) - @ staticmethod + @staticmethod def create_sound( line: GenieGameEntityGroup, sound_id: int, @@ -7530,7 +7551,7 @@ def create_sound( return sound_forward_ref - @ staticmethod + @staticmethod def create_language_strings( line: GenieGameEntityGroup, string_id: int, diff --git a/openage/convert/processor/conversion/aoc/auxiliary_subprocessor.py b/openage/convert/processor/conversion/aoc/auxiliary_subprocessor.py index 618666d32f..e70a2ca271 100644 --- a/openage/convert/processor/conversion/aoc/auxiliary_subprocessor.py +++ b/openage/convert/processor/conversion/aoc/auxiliary_subprocessor.py @@ -1,4 +1,4 @@ -# Copyright 2020-2023 the openage authors. See copying.md for legal info. +# Copyright 2020-2026 the openage authors. See copying.md for legal info. # # pylint: disable=line-too-long,too-many-locals,too-many-branches,too-many-statements,no-else-return @@ -59,11 +59,15 @@ def get_creatable_game_entity(line: GenieGameEntityGroup) -> None: train_location_id = line.get_train_location_id() if isinstance(line, GenieBuildingLineGroup): train_location = dataset.unit_lines[train_location_id] - train_location_name = name_lookup_dict[train_location_id][0] else: train_location = dataset.building_lines[train_location_id] - train_location_name = name_lookup_dict[train_location_id][0] + + # Derive the game-entity name from the train_location's head unit, + # not from train_location_id: the latter may not be a head unit (it + # can be a non-head member of a line), in which case the id-based + # lookup would not match the resolved group's actual game entity. + train_location_name = name_lookup_dict[train_location.get_head_unit_id()][0] # Location of the object depends on whether it'a a unique unit or a normal unit if line.is_unique(): @@ -415,7 +419,7 @@ def get_researchable_tech(tech_group: GenieTechEffectBundleGroup) -> None: tech_lookup_dict = internal_name_lookups.get_tech_lookups(dataset.game_version) civ_lookup_dict = internal_name_lookups.get_civ_lookups(dataset.game_version) - research_location_name = name_lookup_dict[research_location_id][0] + research_location_name = name_lookup_dict[research_location.get_head_unit_id()][0] tech_name = tech_lookup_dict[tech_group.get_id()][0] obj_ref = f"{tech_name}.ResearchableTech" diff --git a/openage/convert/processor/conversion/aoc/civ_subprocessor.py b/openage/convert/processor/conversion/aoc/civ_subprocessor.py index 30eea447f9..fa2a41326a 100644 --- a/openage/convert/processor/conversion/aoc/civ_subprocessor.py +++ b/openage/convert/processor/conversion/aoc/civ_subprocessor.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 the openage authors. See copying.md for legal info. +# Copyright 2020-2026 the openage authors. See copying.md for legal info. # # pylint: disable=too-many-locals,too-many-statements,too-many-branches @@ -44,7 +44,7 @@ def get_civ_setup(cls, civ_group: GenieCivilizationGroup) -> list[ForwardRef]: return patches - @ classmethod + @classmethod def get_modifiers(cls, civ_group: GenieCivilizationGroup) -> list[ForwardRef]: """ Returns global modifiers of a civ. @@ -58,7 +58,7 @@ def get_modifiers(cls, civ_group: GenieCivilizationGroup) -> list[ForwardRef]: return modifiers - @ staticmethod + @staticmethod def get_starting_resources(civ_group: GenieCivilizationGroup) -> list[ForwardRef]: """ Returns the starting resources of a civ. @@ -183,7 +183,7 @@ def get_starting_resources(civ_group: GenieCivilizationGroup) -> list[ForwardRef return resource_amounts - @ classmethod + @classmethod def setup_civ_bonus(cls, civ_group: GenieCivilizationGroup) -> list[ForwardRef]: """ Returns global modifiers of a civ. @@ -274,7 +274,7 @@ def setup_civ_bonus(cls, civ_group: GenieCivilizationGroup) -> list[ForwardRef]: return patches - @ staticmethod + @staticmethod def setup_unique_units(civ_group: GenieCivilizationGroup) -> list[ForwardRef]: """ Patches the unique units into their train location. @@ -297,11 +297,11 @@ def setup_unique_units(civ_group: GenieCivilizationGroup) -> list[ForwardRef]: train_location_id = unique_line.get_train_location_id() if isinstance(unique_line, GenieBuildingLineGroup): train_location = dataset.unit_lines[train_location_id] - train_location_name = name_lookup_dict[train_location_id][0] + train_location_name = name_lookup_dict[train_location.get_head_unit_id()][0] else: train_location = dataset.building_lines[train_location_id] - train_location_name = name_lookup_dict[train_location_id][0] + train_location_name = name_lookup_dict[train_location.get_head_unit_id()][0] patch_target_ref = f"{train_location_name}.Create" patch_target_forward_ref = ForwardRef(train_location, patch_target_ref) @@ -348,7 +348,7 @@ def setup_unique_units(civ_group: GenieCivilizationGroup) -> list[ForwardRef]: return patches - @ staticmethod + @staticmethod def setup_unique_techs(civ_group: GenieCivilizationGroup) -> list[ForwardRef]: """ Patches the unique techs into their research location. @@ -418,7 +418,7 @@ def setup_unique_techs(civ_group: GenieCivilizationGroup) -> list[ForwardRef]: return patches - @ staticmethod + @staticmethod def setup_tech_tree(civ_group: GenieCivilizationGroup) -> list[ForwardRef]: """ Patches standard techs and units out of Research and Create. @@ -600,7 +600,7 @@ def setup_tech_tree(civ_group: GenieCivilizationGroup) -> list[ForwardRef]: return patches - @ staticmethod + @staticmethod def create_animation( line: GenieGameEntityGroup, animation_id: int, diff --git a/openage/convert/processor/conversion/aoc/nyan_subprocessor.py b/openage/convert/processor/conversion/aoc/nyan_subprocessor.py index 7afbd38134..a70bd1dfa6 100644 --- a/openage/convert/processor/conversion/aoc/nyan_subprocessor.py +++ b/openage/convert/processor/conversion/aoc/nyan_subprocessor.py @@ -1,4 +1,4 @@ -# Copyright 2019-2024 the openage authors. See copying.md for legal info. +# Copyright 2019-2026 the openage authors. See copying.md for legal info. # # pylint: disable=too-many-lines,too-many-locals,too-many-statements,too-many-branches # @@ -721,6 +721,12 @@ def variant_group_to_game_entity(variant_group: GenieVariantGroup) -> None: elif variant_type == "misc": variant_type_ref = "engine.util.variant.type.MiscVariant" + else: + # Unknown variant_type; skip this variant rather than produce + # an object with an undefined parent. + index += 1 + continue + variant_name = f"Variant{str(index)}" variant_ref = f"{game_entity_name}.{variant_name}" variant_raw_api_object = RawAPIObject(variant_ref, diff --git a/openage/convert/processor/conversion/aoc/processor.py b/openage/convert/processor/conversion/aoc/processor.py index d042e32065..65e81bd32c 100644 --- a/openage/convert/processor/conversion/aoc/processor.py +++ b/openage/convert/processor/conversion/aoc/processor.py @@ -1,4 +1,4 @@ -# Copyright 2019-2024 the openage authors. See copying.md for legal info. +# Copyright 2019-2026 the openage authors. See copying.md for legal info. # # pylint: disable=too-many-lines,too-many-branches,too-many-statements # pylint: disable=too-many-locals,too-many-public-methods @@ -997,6 +997,10 @@ def create_villager_groups(full_data_set: GenieObjectContainer) -> None: elif task_group_id == 2: line_id = GenieUnitTaskGroup.female_line_id + else: + # Unknown task group id; skip this unit. + continue + task_group = GenieUnitTaskGroup(line_id, task_group_id, full_data_set) task_group.add_unit(unit) full_data_set.task_groups.update({task_group_id: task_group}) diff --git a/openage/convert/processor/conversion/aoc/upgrade_ability_subprocessor.py b/openage/convert/processor/conversion/aoc/upgrade_ability_subprocessor.py index f00814db31..68a15c803b 100644 --- a/openage/convert/processor/conversion/aoc/upgrade_ability_subprocessor.py +++ b/openage/convert/processor/conversion/aoc/upgrade_ability_subprocessor.py @@ -1,4 +1,4 @@ -# Copyright 2020-2023 the openage authors. See copying.md for legal info. +# Copyright 2020-2026 the openage authors. See copying.md for legal info. # # pylint: disable=too-many-locals,too-many-lines,too-many-statements,invalid-name # pylint: disable=too-many-public-methods,too-many-branches,too-many-arguments @@ -1465,7 +1465,7 @@ def shoot_projectile_ability( )): data_changed = True - if not isinstance(diff_animation, NoDiffMember): + if not isinstance(diff_animation, NoDiffMember): # pylint: disable=possibly-used-before-assignment diff_animation_id = diff_animation.value # Nyan patch @@ -1490,7 +1490,7 @@ def shoot_projectile_ability( f"{name_lookup_dict[head_unit_id][1]}/")) wrapper.set_filename(f"{tech_lookup_dict[tech_id][1]}_upgrade") - if not isinstance(diff_comm_sound, NoDiffMember): + if not isinstance(diff_comm_sound, NoDiffMember): # pylint: disable=possibly-used-before-assignment diff_comm_sound_id = diff_comm_sound.value # Nyan patch diff --git a/openage/convert/processor/conversion/aoc/upgrade_attribute_subprocessor.py b/openage/convert/processor/conversion/aoc/upgrade_attribute_subprocessor.py index f8c185d5a8..9ce81605dc 100644 --- a/openage/convert/processor/conversion/aoc/upgrade_attribute_subprocessor.py +++ b/openage/convert/processor/conversion/aoc/upgrade_attribute_subprocessor.py @@ -1,4 +1,4 @@ -# Copyright 2020-2023 the openage authors. See copying.md for legal info. +# Copyright 2020-2026 the openage authors. See copying.md for legal info. # # pylint: disable=too-many-locals,too-many-lines,too-many-statements,too-many-public-methods # @@ -393,6 +393,10 @@ def ballistics_upgrade( # Ballistics, only for Arambai target_mode = dataset.nyan_api_objects["engine.util.target_mode.type.ExpectedPosition"] + else: + # Unknown ballistics value: nothing to patch up. + return patches + obj_id = converter_group.get_id() if isinstance(converter_group, GenieTechEffectBundleGroup): tech_lookup_dict = internal_name_lookups.get_tech_lookups(dataset.game_version) diff --git a/openage/convert/processor/conversion/ror/upgrade_ability_subprocessor.py b/openage/convert/processor/conversion/ror/upgrade_ability_subprocessor.py index 2b00b1ad60..b284da836b 100644 --- a/openage/convert/processor/conversion/ror/upgrade_ability_subprocessor.py +++ b/openage/convert/processor/conversion/ror/upgrade_ability_subprocessor.py @@ -1,4 +1,4 @@ -# Copyright 2020-2023 the openage authors. See copying.md for legal info. +# Copyright 2020-2026 the openage authors. See copying.md for legal info. # # pylint: disable=too-many-locals,too-many-lines,too-many-statements # pylint: disable=too-few-public-methods,too-many-branches @@ -84,7 +84,7 @@ def shoot_projectile_ability( diff_spawn_area_offsets)): data_changed = True - if not isinstance(diff_animation, NoDiffMember): + if not isinstance(diff_animation, NoDiffMember): # pylint: disable=possibly-used-before-assignment diff_animation_id = diff_animation.value # Nyan patch @@ -109,7 +109,7 @@ def shoot_projectile_ability( f"{name_lookup_dict[head_unit_id][1]}/")) wrapper.set_filename(f"{tech_lookup_dict[tech_id][1]}_upgrade") - if not isinstance(diff_comm_sound, NoDiffMember): + if not isinstance(diff_comm_sound, NoDiffMember): # pylint: disable=possibly-used-before-assignment diff_comm_sound_id = diff_comm_sound.value # Nyan patch diff --git a/openage/convert/processor/conversion/ror/upgrade_attribute_subprocessor.py b/openage/convert/processor/conversion/ror/upgrade_attribute_subprocessor.py index fb2f9b7bb6..3b9d61702d 100644 --- a/openage/convert/processor/conversion/ror/upgrade_attribute_subprocessor.py +++ b/openage/convert/processor/conversion/ror/upgrade_attribute_subprocessor.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 the openage authors. See copying.md for legal info. +# Copyright 2020-2026 the openage authors. See copying.md for legal info. # # pylint: disable=too-many-locals,too-many-lines,too-many-statements,too-many-public-methods # @@ -61,6 +61,10 @@ def ballistics_upgrade( elif value == 1: target_mode = dataset.nyan_api_objects["engine.util.target_mode.type.ExpectedPosition"] + else: + # Unknown ballistics value: nothing to patch up. + return patches + obj_id = converter_group.get_id() if isinstance(converter_group, GenieTechEffectBundleGroup): tech_lookup_dict = internal_name_lookups.get_tech_lookups(dataset.game_version) diff --git a/openage/convert/processor/conversion/swgbcc/ability_subprocessor.py b/openage/convert/processor/conversion/swgbcc/ability_subprocessor.py index 07ca29004a..ecf163cf81 100644 --- a/openage/convert/processor/conversion/swgbcc/ability_subprocessor.py +++ b/openage/convert/processor/conversion/swgbcc/ability_subprocessor.py @@ -1,4 +1,4 @@ -# Copyright 2020-2024 the openage authors. See copying.md for legal info. +# Copyright 2020-2026 the openage authors. See copying.md for legal info. # # pylint: disable=too-many-public-methods,too-many-lines,too-many-locals # pylint: disable=too-many-branches,too-many-statements,too-many-arguments @@ -291,6 +291,10 @@ def apply_discrete_effect_ability( line.add_raw_api_object(batch_raw_api_object) # Effects + # `effects` is set inside the per-command branches below; pre-initialise + # so the later add_raw_member is valid for command_ids we don't yet + # handle. + effects = None if command_id == 7: # Attack if projectile != 1: @@ -1505,6 +1509,9 @@ def resource_storage_ability(line: GenieGameEntityGroup) -> ForwardRef: # The unit uses no gathering command or we don't recognize it continue + # `container_name` and `resource` are set by the gathering/trade + # branches below. If the unit neither gathers nor trades, skip it + # rather than fall through with undefined values. if line.is_gatherer(): gatherer_unit_id = gatherer.get_id() if gatherer_unit_id not in gather_lookup_dict: @@ -1517,6 +1524,9 @@ def resource_storage_ability(line: GenieGameEntityGroup) -> ForwardRef: # Trading container_name = "TradeContainer" + else: + continue + container_ref = f"{ability_ref}.{container_name}" container_raw_api_object = RawAPIObject( container_ref, container_name, dataset.nyan_api_objects) diff --git a/openage/convert/processor/conversion/swgbcc/processor.py b/openage/convert/processor/conversion/swgbcc/processor.py index 8fbb9d97ae..dc8bcd8829 100644 --- a/openage/convert/processor/conversion/swgbcc/processor.py +++ b/openage/convert/processor/conversion/swgbcc/processor.py @@ -1,4 +1,4 @@ -# Copyright 2020-2024 the openage authors. See copying.md for legal info. +# Copyright 2020-2026 the openage authors. See copying.md for legal info. # # pylint: disable=too-many-lines,too-many-branches,too-many-statements,too-many-locals # @@ -551,6 +551,10 @@ def create_villager_groups(full_data_set: GenieObjectContainer) -> None: # No differences to task group 1; probably unused continue + else: + # Unknown task group id; skip this unit. + continue + task_group = GenieUnitTaskGroup(line_id, task_group_id, full_data_set) task_group.add_unit(unit) full_data_set.task_groups.update({task_group_id: task_group}) diff --git a/openage/convert/processor/export/media_exporter.py b/openage/convert/processor/export/media_exporter.py index ace7e1f83d..ee95f63d3c 100644 --- a/openage/convert/processor/export/media_exporter.py +++ b/openage/convert/processor/export/media_exporter.py @@ -1,4 +1,4 @@ -# Copyright 2021-2024 the openage authors. See copying.md for legal info. +# Copyright 2021-2026 the openage authors. See copying.md for legal info. # # pylint: disable=too-many-arguments,too-many-locals """ @@ -77,6 +77,12 @@ def export( handle_outqueue_func = None kwargs = {} + # `itargs` and `handle_outqueue_func` are set per MediaType below; + # pre-initialise so the later _export_singlethreaded / + # _export_multithreaded calls are valid for any new MediaType that + # gets added without a corresponding branch here. + itargs = () + handle_outqueue_func = None if media_type is MediaType.BLEND: read_data_func = MediaExporter._get_blend_data export_func = _export_blend @@ -724,7 +730,10 @@ def _export_terrain( elif file_ext == "dds": # TODO: Implement - pass + raise NotImplementedError( + f"DDS source file {source_filename} is not yet supported by the " + "media exporter." + ) elif file_ext == "png": with target_path.open("wb") as imagefile: diff --git a/openage/convert/service/init/version_detect.py b/openage/convert/service/init/version_detect.py index 58cec8925a..4f0baea1fd 100644 --- a/openage/convert/service/init/version_detect.py +++ b/openage/convert/service/init/version_detect.py @@ -1,4 +1,4 @@ -# Copyright 2020-2024 the openage authors. See copying.md for legal info. +# Copyright 2020-2026 the openage authors. See copying.md for legal info. # # pylint: disable=too-many-arguments,too-many-locals,too-many-branches """ @@ -235,4 +235,4 @@ def create_game_obj( return GameEdition(game_name, game_id, support, game_version_info, game_mediapaths, game_installpaths, modpacks, - expansions, **flags) + expansions, **flags) # pylint: disable=possibly-used-before-assignment diff --git a/openage/convert/tool/singlefile.py b/openage/convert/tool/singlefile.py index 9d7f1b4583..dd9fbbe1d9 100644 --- a/openage/convert/tool/singlefile.py +++ b/openage/convert/tool/singlefile.py @@ -1,4 +1,4 @@ -# Copyright 2015-2024 the openage authors. See copying.md for legal info. +# Copyright 2015-2026 the openage authors. See copying.md for legal info. """ Convert a single slp/wav file from some drs archive to a png/opus file. @@ -65,6 +65,10 @@ def main(args, error): dll_manager = DllDirectoryManager(default_paths()) dll_manager.add_directories() + # `palettes` is only required for the SLP/SMP/SMX file types. The sld, + # drs-wav, and wav paths don't read it, so we pre-declare it as None and + # only populate it when one of the palette-based paths is taken. + palettes = None if not (args.mode in ("sld", "drs-wav", "wav") or file_extension in ("sld", "wav")): if not args.palettes_path: raise RuntimeError("palettes-path needs to be specified for " diff --git a/openage/convert/value_object/read/media/blendomatic.py b/openage/convert/value_object/read/media/blendomatic.py index 1ce04ef86b..f0b56edf12 100644 --- a/openage/convert/value_object/read/media/blendomatic.py +++ b/openage/convert/value_object/read/media/blendomatic.py @@ -1,4 +1,4 @@ -# Copyright 2013-2023 the openage authors. See copying.md for legal info. +# Copyright 2013-2026 the openage authors. See copying.md for legal info. # TODO pylint: disable=too-many-function-args @@ -197,8 +197,7 @@ def get_tile_from_data(self, data: list[int]) -> BlendingTile: padding = [-1] * space_count pixels = padding + pixels + padding - if len(pixels) > max_width: - max_width = len(pixels) + max_width = max(max_width, len(pixels)) read_so_far += read_values tilerows.append(pixels) diff --git a/openage/nyan/nyan_structs.py b/openage/nyan/nyan_structs.py index 07393ad4e8..40e0754228 100644 --- a/openage/nyan/nyan_structs.py +++ b/openage/nyan/nyan_structs.py @@ -1,4 +1,4 @@ -# Copyright 2019-2023 the openage authors. See copying.md for legal info. +# Copyright 2019-2026 the openage authors. See copying.md for legal info. # # pylint: disable=too-many-lines,too-many-arguments,too-many-return-statements,too-many-locals @@ -950,7 +950,7 @@ def is_initialized(self) -> bool: """ return self.value is not None - @ staticmethod + @staticmethod def is_inherited() -> bool: """ Returns True if the member is inherited from another object. diff --git a/openage/testing/list_processor.py b/openage/testing/list_processor.py index 28a43b50b9..ee89b0800d 100644 --- a/openage/testing/list_processor.py +++ b/openage/testing/list_processor.py @@ -1,4 +1,4 @@ -# Copyright 2015-2022 the openage authors. See copying.md for legal info. +# Copyright 2015-2026 the openage authors. See copying.md for legal info. """ Processes the raw test lists from the testlist module. """ @@ -55,15 +55,13 @@ def default_cond(_): def list_targets_py(): """ Invokes list_targets() with the py-specific listers. """ from .testlist import tests_py, demos_py, benchmark_py - for val in list_targets(tests_py, demos_py, benchmark_py): - yield val + yield from list_targets(tests_py, demos_py, benchmark_py) def list_targets_cpp(): """ Invokes list_targets() with the C++-specific listers. """ from .testlist import tests_cpp, demos_cpp, benchmark_cpp - for val in list_targets(tests_cpp, demos_cpp, benchmark_cpp): - yield val + yield from list_targets(tests_cpp, demos_cpp, benchmark_cpp) def get_all_targets() -> OrderedDict: diff --git a/openage/testing/testlist.py b/openage/testing/testlist.py index ee86043efa..fb08e7bc35 100644 --- a/openage/testing/testlist.py +++ b/openage/testing/testlist.py @@ -1,4 +1,4 @@ -# Copyright 2015-2024 the openage authors. See copying.md for legal info. +# Copyright 2015-2026 the openage authors. See copying.md for legal info. """ Lists of all possible tests; enter your tests here. """ diff --git a/openage/util/dll.py b/openage/util/dll.py index a3d23be7f1..8470595004 100644 --- a/openage/util/dll.py +++ b/openage/util/dll.py @@ -1,4 +1,4 @@ -# Copyright 2024-2024 the openage authors. See copying.md for legal info. +# Copyright 2024-2026 the openage authors. See copying.md for legal info. """ Windows-specific loading of compiled Python modules and DLLs. @@ -12,7 +12,7 @@ DEFAULT_PYTHON_DLL_DIR = os.path.dirname(sys.executable) # openage.dll locations (relative to this file) -DEFAULT_OPENAGE_DLL_DIRs = [ +DEFAULT_OPENAGE_DLL_DIRS = [ "../../libopenage/Debug", "../../libopenage/Release", "../../libopenage/RelWithDebInfo", @@ -108,7 +108,7 @@ def default_paths() -> list[str]: file_dir = os.path.dirname(os.path.abspath(inspect.getsourcefile(lambda: 0))) # Add openage DLL search paths - for candidate in DEFAULT_OPENAGE_DLL_DIRs: + for candidate in DEFAULT_OPENAGE_DLL_DIRS: path = os.path.join(file_dir, candidate) if os.path.exists(path): directory_paths.append(path) diff --git a/openage/util/fslike/path.py b/openage/util/fslike/path.py index ccdf8cc2b8..5effcaf8ce 100644 --- a/openage/util/fslike/path.py +++ b/openage/util/fslike/path.py @@ -1,4 +1,4 @@ -# Copyright 2015-2023 the openage authors. See copying.md for legal info. +# Copyright 2015-2026 the openage authors. See copying.md for legal info. """ Provides Path, which is analogous to pathlib.Path, @@ -240,12 +240,12 @@ def removerecursive(self): else: self.unlink() - @ property + @property def mtime(self): """ Returns the time of last modification of the file or directory. """ return self.fsobj.mtime(self.parts) - @ property + @property def filesize(self): """ Returns the file size. """ return self.fsobj.filesize(self.parts) @@ -265,17 +265,17 @@ def poll_fs_watches(self): """ Polls the installed watches for the entire file-system. """ self.fsobj.poll_watches() - @ property + @property def parent(self): """ Parent path object. The parent of root is root. """ return type(self)(self.fsobj, self.parts[:-1]) - @ property + @property def name(self): """ The name of the topmost component (str). """ return self.parts[-1].decode() - @ property + @property def suffix(self): """ The last suffix of the name of the topmost component (str). """ name = self.name @@ -284,7 +284,7 @@ def suffix(self): return "" return name[pos:] - @ property + @property def suffixes(self): """ The suffixes of the name of the topmost component (str list). """ name = self.name @@ -292,7 +292,7 @@ def suffixes(self): name = name[1:] return ['.' + suffix for suffix in name.split('.')[1:]] - @ property + @property def stem(self): """ Name without suffix (such that stem + suffix == name). """ name = self.name diff --git a/openage/util/fslike/union.py b/openage/util/fslike/union.py index 7b2ca8a8f2..4a51bcfb7b 100644 --- a/openage/util/fslike/union.py +++ b/openage/util/fslike/union.py @@ -1,4 +1,4 @@ -# Copyright 2015-2023 the openage authors. See copying.md for legal info. +# Copyright 2015-2026 the openage authors. See copying.md for legal info. """ Provides Union, a utility class for combining multiple FSLikeObjects to a @@ -39,7 +39,7 @@ def __str__(self): for pnt in self.mounts]) return f"Union({content})" - @ property + @property def root(self): return UnionPath(self, []) diff --git a/openage/util/ordered_set.py b/openage/util/ordered_set.py index bfb264b616..f08560fdea 100644 --- a/openage/util/ordered_set.py +++ b/openage/util/ordered_set.py @@ -1,4 +1,4 @@ -# Copyright 2019-2022 the openage authors. See copying.md for legal info. +# Copyright 2019-2026 the openage authors. See copying.md for legal info. """ Provides a very simple implementation of an ordered set. We use the @@ -7,7 +7,8 @@ """ -from typing import Generic, Hashable, TypeVar +from collections.abc import Hashable +from typing import Generic, TypeVar OrderedSetItem = TypeVar("OrderedSetItem") diff --git a/openage/util/version.py b/openage/util/version.py index 67b6b4e2aa..81351f0dc9 100644 --- a/openage/util/version.py +++ b/openage/util/version.py @@ -59,13 +59,17 @@ def __init__(self, version: str) -> None: self.buildmetadata = match.group("buildmetadata") def _precedence_key(self) -> tuple: - # A version with no prerelease ranks higher than one with a - # prerelease at the same MAJOR.MINOR.PATCH (semver 11.3). We - # encode that by giving "no prerelease" a 1 and any prerelease - # a 0 as the next tuple element. The prerelease string itself - # is included so two prereleases compare stably, but note this - # is a lexicographic fallback, not the full semver 11.4 - # identifier comparison. + """ + Build the comparison key used by the version ordering protocol. + + A version with no prerelease ranks higher than one with a + prerelease at the same MAJOR.MINOR.PATCH (semver 11.3). We + encode that by giving "no prerelease" a 1 and any prerelease + a 0 as the next tuple element. The prerelease string itself + is included so two prereleases compare stably, but note this + is a lexicographic fallback, not the full semver 11.4 + identifier comparison. + """ return ( self.major, self.minor,