Skip to content

Conversation

MayberryZoom
Copy link
Contributor

Also achieves full coverage of BRSA and adds docstrings to every method (both new and old)

Copy link

codecov bot commented Mar 2, 2025

Codecov Report

Attention: Patch coverage is 97.87234% with 1 line in your changes missing coverage. Please review.

Project coverage is 79.12%. Comparing base (9c60be4) to head (e389c1a).

Files with missing lines Patch % Lines
src/mercury_engine_data_structures/formats/brsa.py 97.87% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #280      +/-   ##
==========================================
+ Coverage   78.85%   79.12%   +0.27%     
==========================================
  Files          80       80              
  Lines        4323     4365      +42     
==========================================
+ Hits         3409     3454      +45     
+ Misses        914      911       -3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

@duncathan duncathan left a comment

Choose a reason for hiding this comment

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

if you're returning a Container from an API it should probably have its own type too

@MayberryZoom
Copy link
Contributor Author

That's already used in various places, and it's something I plan to clean up once I finish generating classes for every type. We can leave it as a Container for now imo

@MayberryZoom
Copy link
Contributor Author

To elaborate:
Adding full types for these is beyond the scope of this PR. The only thing we could realistically do for now is add a type alias, but that would provide strictly less information than how it is now. What's being returned is already detailed in the docstring, so type aliasing the return type would only obfuscate what the actual data type is (a Container). For the moment, this is the best way to do this. Imo, this PR isn't worth blocking until those types finish getting generated.

That said, I just remembered I should be wrapping some of those lists in ListContainers, so I'll fix that soon.

@MayberryZoom
Copy link
Contributor Author

I fixed those ListContainers, and I also raised ValueErrors if the subarea config/setup/charclass group already exists, so this PR should be ready now.

@MayberryZoom MayberryZoom requested a review from duncathan March 3, 2025 01:59
ignore_camera_offsets: bool = False,
charclass_group: str = "No Enemies",
camera_ids: list[str] = [],
cutscene_ids: list[str] = [],
Copy link
Contributor

Choose a reason for hiding this comment

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

as a rule, you should never use a mutable value as a default argument

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wanted to show that if it's not provided, it defaults to empty, hence the ternaries below

Copy link
Contributor

Choose a reason for hiding this comment

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

in that case do cutscene_ids: list[str] | None = None and if cutscene_ids is None: cutscene_ids = [] and drop the ternaries

Copy link
Contributor Author

Choose a reason for hiding this comment

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

facepalming at myself for not doing that to begin with. thanks

Copy link
Contributor Author

Choose a reason for hiding this comment

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

actually, are the ternaries actually that bad? I feel like it only needs to default to None and then the ternary is just more compact.

Copy link
Contributor

Choose a reason for hiding this comment

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

if cutscene_ids is None:
    cutscene_ids = []
{"vsCutsceneIds": ListContainer(cutscene_ids)}

is more compact and readable imo than

{"vsCutsceneIds": ListContainer(cutscene_ids) if cutscene_ids is not None else ListContainer()}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

to be fair, you only need if cutscene_ids there. I also personally like inlining it, but maybe that's the JavaScript background talking, haha. if the former way is more pythonic, I'll just go with that

Copy link
Contributor

Choose a reason for hiding this comment

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

yes, please do. though if you're making a new wrapper class, you should also convert this function into a classmethod to create the wrapper and then have this function accept an instance of the wrapper class

"sCharclassGroupId": charclass_group,
"asItemsIds": ListContainer([""] * 9),
"vsCameraCollisionsIds": ListContainer(camera_ids) if camera_ids else ListContainer(),
"vsCutscenesIds": ListContainer(cutscene_ids) if cutscene_ids else ListContainer(),
Copy link
Contributor

Choose a reason for hiding this comment

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

what's with these ternaries?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

these are here because of the mutable default arguments

param subarea_id: the name of the subarea
param preset_name: the name of the preset
param setup_id: the name of the setup the subarea is in"""
self.get_subarea_config(subarea_id, setup_id).asItemsIds[8] = preset_name
Copy link
Contributor

Choose a reason for hiding this comment

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

ok I appreciate your concern about it being out of scope of this PR to abolish the use of Container here, and that's fine, but these functions are a problem. at the very minimum I need you to manually write a wrapper class for subarea configs cuz these are just untenable

Copy link
Contributor Author

Choose a reason for hiding this comment

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

do you mean just asItemsIds or the whole config? I can see this in particular being made a lot simpler by it and I wouldn't mind doing so as a temporary solution. something like BMSAD?

Copy link
Contributor

Choose a reason for hiding this comment

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

it would be easiest to add a wrapper that gets returned by get_subarea_config(). otherwise you would have to deal with some really messy code to couple the asItemsIds wrapper to the internal container, which is way more work than just adding properties to the config wrapper.

something like the BMSLD pr: https://github.com/randovania/mercury-engine-data-structures/pull/258/files#diff-bf70997618c433f3cb8e9e854527f73c9a3a8327c932b8bd6101897ec7c55857R145

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah, definitely seems easiest.

returns: the charclass group"""
return next(group for group in self.charclass_groups if group.sId == group_id)

def add_charclass_group(self, group_id: str, charclasses: list[str] = []) -> Container:
Copy link
Contributor

Choose a reason for hiding this comment

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

mutable default argument

raise ValueError(f"Charclass {group_id} is already present")

new_group = Container(
{"sId": group_id, "vsCharClassesIds": ListContainer(charclasses) if charclasses else ListContainer()}
Copy link
Contributor

Choose a reason for hiding this comment

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

another weird ternary

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants