awpy icon indicating copy to clipboard operation
awpy copied to clipboard

CLI `awpy parse` crashes w/ `KeyError: "player_sound"` on default settings for some demos

Open bkushigian opened this issue 8 months ago • 3 comments

Describe the bug I ran awpy parse DEMO_PATH and got a KeyError saying that player_sound isn't present. This seems to be a common issue with many demos, and hard stops the parse command. Since player_sound is often not included, and is parsed by default in awpy parse, it would be good to handle missing properties with a warning.

There are 2 obvious solutions off the top of my head.

First, we could remove player_sound from the default set of events to parse. This might be correct to do anyway, but I think that even when we specify a property that isn't in the demo awpy cli should handle it gracefully (either exit with a nice error message or continue optimistically).

Second, we can wrap our cachedproperties in a try/except:



    @cached_property
    def footsteps(self) -> pl.DataFrame:
        try:
            footsteps = awpy.parsers.utils.get_event_from_parsed_events(self.events, "player_sound")
            footsteps = awpy.parsers.events.parse_footsteps(footsteps)
            return awpy.parsers.rounds.apply_round_num(df=footsteps, rounds_df=self.rounds, tick_col="tick").filter(
                pl.col("round_num").is_not_null()
            )
        except KeyError as e:
            print_pretty_user_warning("footsteps", e)
            return pl.DataFrame()

This second method is also a bit awkward because we have an empty dataframe, so maybe we can use optional types instead for the cached properties?

Include Information to Reproduce Run awpy parse DEMO for a valve/faceit demo (i seems like it works on some pro demos, I don't know if it has 100% failure rate on valve/faceit demos, but it's common enough that it should fail after 3 or 4 tries at most).

This produces the following stack trace:

awpy parse /mnt/c/Program\ Files\ \(x86\)/Steam/steamapps/common/Counter-Strike\ Global\ Offensive/game/csgo/replays/match730_003740035103774998617_0611549455_129.dem
2025-04-10 11:51:01.073 | WARNING  | awpy.data.map_data:<module>:20 - Failed to load map data from /home/benku/.awpy/maps/map-data.json.
Traceback (most recent call last):
  File "/home/benku/Code/awpy/.venv/bin/awpy", line 8, in <module>
    sys.exit(awpy_cli())
             ^^^^^^^^^^
  File "/home/benku/Code/awpy/.venv/lib/python3.11/site-packages/click/core.py", line 1161, in __call__
    return self.main(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/benku/Code/awpy/.venv/lib/python3.11/site-packages/click/core.py", line 1082, in main
    rv = self.invoke(ctx)
         ^^^^^^^^^^^^^^^^
  File "/home/benku/Code/awpy/.venv/lib/python3.11/site-packages/click/core.py", line 1697, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/benku/Code/awpy/.venv/lib/python3.11/site-packages/click/core.py", line 1443, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/benku/Code/awpy/.venv/lib/python3.11/site-packages/click/core.py", line 788, in invoke
    return __callback(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/benku/Code/awpy/awpy/cli.py", line 73, in parse_demo
    demo.compress(outpath=outpath)
  File "/home/benku/Code/awpy/awpy/demo.py", line 670, in compress
    ("footsteps", self.footsteps),
                  ^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/functools.py", line 1001, in __get__
    val = self.func(instance)
          ^^^^^^^^^^^^^^^^^^^
  File "/home/benku/Code/awpy/awpy/demo.py", line 375, in footsteps
    footsteps = awpy.parsers.utils.get_event_from_parsed_events(self.events, "player_sound")
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/benku/Code/awpy/awpy/parsers/utils.py", line 27, in get_event_from_parsed_events
    raise KeyError(missing_key_err_msg)
KeyError: "Required event 'player_sound' is missing from the events dictionary."

bkushigian avatar Apr 10 '25 19:04 bkushigian

I just saw that we already have a way to specify that we want to get an empty dataframe if the respective entry is not present:

https://github.com/pnxenopoulos/awpy/blob/main/awpy/parsers/utils.py#L24

I would probably extend this to also log a warning message and enable this by default for the player_sound event, because that is frequently missing.

JanEricNitschke avatar Apr 11 '25 06:04 JanEricNitschke

Actually i am changing it again to not special case footsteps. Instead i am just making it so that compress at the end does not throw an error on a missing property and instead skips that.

JanEricNitschke avatar Apr 11 '25 07:04 JanEricNitschke

Nice yeah, LGTM. I added a couple comments in a review, but nothing super important...just the issue of an empty dataframe and a nonexistent dataframe being combined (this should probably be documented at the very least).

bkushigian avatar Apr 11 '25 19:04 bkushigian