peppi-py-vladfi 0.9.0__cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- peppi_py/__init__.py +18 -0
- peppi_py/_peppi.abi3.so +0 -0
- peppi_py/frame.py +135 -0
- peppi_py/game.py +132 -0
- peppi_py/parse.py +196 -0
- peppi_py/util.py +17 -0
- peppi_py_vladfi-0.9.0.dist-info/METADATA +6 -0
- peppi_py_vladfi-0.9.0.dist-info/RECORD +9 -0
- peppi_py_vladfi-0.9.0.dist-info/WHEEL +5 -0
peppi_py/__init__.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from ._peppi import read_peppi as _read_peppi, read_slippi as _read_slippi
|
|
4
|
+
from .game import End, Game, Start
|
|
5
|
+
from .parse import dc_from_json, frames_from_sa, RollbackMode
|
|
6
|
+
|
|
7
|
+
def read_peppi(path: str, skip_frames: bool=False) -> Game:
|
|
8
|
+
g = _read_peppi(path, skip_frames)
|
|
9
|
+
return Game(dc_from_json(Start, g.start), g.end and dc_from_json(End, g.end), g.metadata, frames_from_sa(g.frames))
|
|
10
|
+
|
|
11
|
+
def read_slippi(path: str, skip_frames: bool=False, rollback_mode: RollbackMode=RollbackMode.ALL) -> Game:
|
|
12
|
+
g = _read_slippi(path, skip_frames)
|
|
13
|
+
return Game(
|
|
14
|
+
dc_from_json(Start, g.start),
|
|
15
|
+
g.end and dc_from_json(End, g.end),
|
|
16
|
+
g.metadata,
|
|
17
|
+
frames_from_sa(g.frames, rollback_mode=rollback_mode)
|
|
18
|
+
)
|
peppi_py/_peppi.abi3.so
ADDED
|
Binary file
|
peppi_py/frame.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from enum import IntEnum
|
|
3
|
+
|
|
4
|
+
from pyarrow.lib import (
|
|
5
|
+
Int8Array, Int16Array, Int32Array, Int64Array,
|
|
6
|
+
UInt8Array, UInt16Array, UInt32Array, UInt64Array,
|
|
7
|
+
FloatArray, DoubleArray,
|
|
8
|
+
ListArray,
|
|
9
|
+
)
|
|
10
|
+
from .util import _repr
|
|
11
|
+
|
|
12
|
+
@dataclass(slots=True)
|
|
13
|
+
class End:
|
|
14
|
+
__repr__ = _repr
|
|
15
|
+
latest_finalized_frame: Int32Array | None = None
|
|
16
|
+
|
|
17
|
+
@dataclass(slots=True)
|
|
18
|
+
class Position:
|
|
19
|
+
__repr__ = _repr
|
|
20
|
+
x: FloatArray
|
|
21
|
+
y: FloatArray
|
|
22
|
+
|
|
23
|
+
@dataclass(slots=True)
|
|
24
|
+
class Start:
|
|
25
|
+
__repr__ = _repr
|
|
26
|
+
random_seed: UInt32Array
|
|
27
|
+
scene_frame_counter: UInt32Array | None = None
|
|
28
|
+
|
|
29
|
+
@dataclass(slots=True)
|
|
30
|
+
class TriggersPhysical:
|
|
31
|
+
__repr__ = _repr
|
|
32
|
+
l: FloatArray
|
|
33
|
+
r: FloatArray
|
|
34
|
+
|
|
35
|
+
@dataclass(slots=True)
|
|
36
|
+
class Velocities:
|
|
37
|
+
__repr__ = _repr
|
|
38
|
+
self_x_air: FloatArray
|
|
39
|
+
self_y: FloatArray
|
|
40
|
+
knockback_x: FloatArray
|
|
41
|
+
knockback_y: FloatArray
|
|
42
|
+
self_x_ground: FloatArray
|
|
43
|
+
|
|
44
|
+
@dataclass(slots=True)
|
|
45
|
+
class Velocity:
|
|
46
|
+
__repr__ = _repr
|
|
47
|
+
x: FloatArray
|
|
48
|
+
y: FloatArray
|
|
49
|
+
|
|
50
|
+
@dataclass(slots=True)
|
|
51
|
+
class Item:
|
|
52
|
+
__repr__ = _repr
|
|
53
|
+
type: UInt16Array
|
|
54
|
+
state: UInt8Array
|
|
55
|
+
direction: FloatArray
|
|
56
|
+
velocity: Velocity
|
|
57
|
+
position: Position
|
|
58
|
+
damage: UInt16Array
|
|
59
|
+
timer: FloatArray
|
|
60
|
+
id: UInt32Array
|
|
61
|
+
misc: tuple[UInt8Array, UInt8Array, UInt8Array, UInt8Array] | None = None
|
|
62
|
+
owner: Int8Array | None = None
|
|
63
|
+
instance_id: UInt16Array | None = None
|
|
64
|
+
|
|
65
|
+
@dataclass(slots=True)
|
|
66
|
+
class Post:
|
|
67
|
+
__repr__ = _repr
|
|
68
|
+
character: UInt8Array
|
|
69
|
+
state: UInt16Array
|
|
70
|
+
position: Position
|
|
71
|
+
direction: FloatArray
|
|
72
|
+
percent: FloatArray
|
|
73
|
+
shield: FloatArray
|
|
74
|
+
last_attack_landed: UInt8Array
|
|
75
|
+
combo_count: UInt8Array
|
|
76
|
+
last_hit_by: UInt8Array
|
|
77
|
+
stocks: UInt8Array
|
|
78
|
+
state_age: FloatArray | None = None
|
|
79
|
+
state_flags: tuple[UInt8Array, UInt8Array, UInt8Array, UInt8Array, UInt8Array] | None = None
|
|
80
|
+
misc_as: FloatArray | None = None
|
|
81
|
+
airborne: UInt8Array | None = None
|
|
82
|
+
ground: UInt16Array | None = None
|
|
83
|
+
jumps: UInt8Array | None = None
|
|
84
|
+
l_cancel: UInt8Array | None = None
|
|
85
|
+
hurtbox_state: UInt8Array | None = None
|
|
86
|
+
velocities: Velocities | None = None
|
|
87
|
+
hitlag: FloatArray | None = None
|
|
88
|
+
animation_index: UInt32Array | None = None
|
|
89
|
+
|
|
90
|
+
@dataclass(slots=True)
|
|
91
|
+
class Pre:
|
|
92
|
+
__repr__ = _repr
|
|
93
|
+
random_seed: UInt32Array
|
|
94
|
+
state: UInt16Array
|
|
95
|
+
position: Position
|
|
96
|
+
direction: FloatArray
|
|
97
|
+
joystick: Position
|
|
98
|
+
cstick: Position
|
|
99
|
+
triggers: FloatArray
|
|
100
|
+
buttons: UInt32Array
|
|
101
|
+
buttons_physical: UInt16Array
|
|
102
|
+
triggers_physical: TriggersPhysical
|
|
103
|
+
raw_analog_x: Int8Array | None = None
|
|
104
|
+
percent: FloatArray | None = None
|
|
105
|
+
raw_analog_y: Int8Array | None = None
|
|
106
|
+
|
|
107
|
+
@dataclass(slots=True)
|
|
108
|
+
class Data:
|
|
109
|
+
__repr__ = _repr
|
|
110
|
+
pre: Pre
|
|
111
|
+
post: Post
|
|
112
|
+
|
|
113
|
+
@dataclass(slots=True)
|
|
114
|
+
class PortData:
|
|
115
|
+
__repr__ = _repr
|
|
116
|
+
leader: Data
|
|
117
|
+
follower: Data | None = None
|
|
118
|
+
|
|
119
|
+
class FodPlatform(IntEnum):
|
|
120
|
+
RIGHT = 0
|
|
121
|
+
LEFT = 1
|
|
122
|
+
|
|
123
|
+
@dataclass(slots=True)
|
|
124
|
+
class FodPlatformMove:
|
|
125
|
+
__repr__ = _repr
|
|
126
|
+
platform: ListArray
|
|
127
|
+
height: ListArray
|
|
128
|
+
|
|
129
|
+
@dataclass(slots=True)
|
|
130
|
+
class Frame:
|
|
131
|
+
__repr__ = _repr
|
|
132
|
+
id: object
|
|
133
|
+
ports: tuple[PortData]
|
|
134
|
+
items: Item | None = None
|
|
135
|
+
fod_platforms: FodPlatformMove | None = None
|
peppi_py/game.py
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from enum import IntEnum
|
|
3
|
+
from .frame import Frame
|
|
4
|
+
from .util import _repr
|
|
5
|
+
|
|
6
|
+
class Port(IntEnum):
|
|
7
|
+
P1 = 0
|
|
8
|
+
P2 = 1
|
|
9
|
+
P3 = 2
|
|
10
|
+
P4 = 3
|
|
11
|
+
|
|
12
|
+
class PlayerType(IntEnum):
|
|
13
|
+
HUMAN = 0
|
|
14
|
+
CPU = 1
|
|
15
|
+
DEMO = 2
|
|
16
|
+
|
|
17
|
+
class Language(IntEnum):
|
|
18
|
+
JAPANESE = 0
|
|
19
|
+
ENGLISH = 1
|
|
20
|
+
|
|
21
|
+
class DashBack(IntEnum):
|
|
22
|
+
UCF = 1
|
|
23
|
+
ARDUINO = 2
|
|
24
|
+
|
|
25
|
+
class ShieldDrop(IntEnum):
|
|
26
|
+
UCF = 1
|
|
27
|
+
ARDUINO = 2
|
|
28
|
+
|
|
29
|
+
class EndMethod(IntEnum):
|
|
30
|
+
UNRESOLVED = 0
|
|
31
|
+
TIME = 1
|
|
32
|
+
GAME = 2
|
|
33
|
+
RESOLVED = 3
|
|
34
|
+
NO_CONTEST = 7
|
|
35
|
+
|
|
36
|
+
@dataclass(slots=True)
|
|
37
|
+
class Scene:
|
|
38
|
+
__repr__ = _repr
|
|
39
|
+
major: int
|
|
40
|
+
minor: int
|
|
41
|
+
|
|
42
|
+
@dataclass(slots=True)
|
|
43
|
+
class Match:
|
|
44
|
+
__repr__ = _repr
|
|
45
|
+
id: str
|
|
46
|
+
game: int
|
|
47
|
+
tiebreaker: int
|
|
48
|
+
|
|
49
|
+
@dataclass(slots=True)
|
|
50
|
+
class Slippi:
|
|
51
|
+
__repr__ = _repr
|
|
52
|
+
version: tuple[int, int, int]
|
|
53
|
+
|
|
54
|
+
@dataclass(slots=True)
|
|
55
|
+
class Netplay:
|
|
56
|
+
__repr__ = _repr
|
|
57
|
+
name: str
|
|
58
|
+
code: str
|
|
59
|
+
suid: str | None = None
|
|
60
|
+
|
|
61
|
+
@dataclass(slots=True)
|
|
62
|
+
class Team:
|
|
63
|
+
__repr__ = _repr
|
|
64
|
+
color: int
|
|
65
|
+
shade: int
|
|
66
|
+
|
|
67
|
+
@dataclass(slots=True)
|
|
68
|
+
class Ucf:
|
|
69
|
+
__repr__ = _repr
|
|
70
|
+
dash_back: DashBack | None
|
|
71
|
+
shield_drop: ShieldDrop | None
|
|
72
|
+
|
|
73
|
+
@dataclass(slots=True)
|
|
74
|
+
class Player:
|
|
75
|
+
__repr__ = _repr
|
|
76
|
+
port: Port
|
|
77
|
+
character: int
|
|
78
|
+
type: PlayerType
|
|
79
|
+
stocks: int
|
|
80
|
+
costume: int
|
|
81
|
+
team: Team | None
|
|
82
|
+
handicap: int
|
|
83
|
+
bitfield: int
|
|
84
|
+
cpu_level: int | None
|
|
85
|
+
offense_ratio: float
|
|
86
|
+
defense_ratio: float
|
|
87
|
+
model_scale: float
|
|
88
|
+
ucf: Ucf | None = None
|
|
89
|
+
name_tag: str | None = None
|
|
90
|
+
netplay: Netplay | None = None
|
|
91
|
+
|
|
92
|
+
@dataclass(slots=True)
|
|
93
|
+
class Start:
|
|
94
|
+
__repr__ = _repr
|
|
95
|
+
slippi: Slippi
|
|
96
|
+
bitfield: tuple[int, int, int, int]
|
|
97
|
+
is_raining_bombs: bool
|
|
98
|
+
is_teams: bool
|
|
99
|
+
item_spawn_frequency: int
|
|
100
|
+
self_destruct_score: int
|
|
101
|
+
stage: int
|
|
102
|
+
timer: int
|
|
103
|
+
item_spawn_bitfield: tuple[int, int, int, int, int]
|
|
104
|
+
damage_ratio: float
|
|
105
|
+
players: tuple[Player, ...]
|
|
106
|
+
random_seed: int
|
|
107
|
+
is_pal: bool | None = None
|
|
108
|
+
is_frozen_ps: bool | None = None
|
|
109
|
+
scene: Scene | None = None
|
|
110
|
+
language: Language | None = None
|
|
111
|
+
match: Match | None = None
|
|
112
|
+
|
|
113
|
+
@dataclass(slots=True)
|
|
114
|
+
class PlayerEnd:
|
|
115
|
+
__repr__ = _repr
|
|
116
|
+
port: Port
|
|
117
|
+
placement: int
|
|
118
|
+
|
|
119
|
+
@dataclass(slots=True)
|
|
120
|
+
class End:
|
|
121
|
+
__repr__ = _repr
|
|
122
|
+
method: EndMethod
|
|
123
|
+
lras_initiator: Port | None = None
|
|
124
|
+
players: tuple[PlayerEnd, ...] | None = None
|
|
125
|
+
|
|
126
|
+
@dataclass(slots=True)
|
|
127
|
+
class Game:
|
|
128
|
+
__repr__ = _repr
|
|
129
|
+
start: Start
|
|
130
|
+
end: End
|
|
131
|
+
metadata: dict
|
|
132
|
+
frames: Frame | None
|
peppi_py/parse.py
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import types, typing
|
|
2
|
+
import pyarrow
|
|
3
|
+
import dataclasses as dc
|
|
4
|
+
import functools
|
|
5
|
+
from inflection import underscore
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from .frame import Data, Frame, PortData, Item
|
|
8
|
+
from .frame import FodPlatform, FodPlatformMove
|
|
9
|
+
|
|
10
|
+
T = typing.TypeVar('T')
|
|
11
|
+
|
|
12
|
+
def _repr(x):
|
|
13
|
+
if isinstance(x, pyarrow.Array):
|
|
14
|
+
s = ', '.join(repr(v.as_py()) for v in x[:3])
|
|
15
|
+
if len(x) > 3:
|
|
16
|
+
s += ', ...'
|
|
17
|
+
return f'[{s}]'
|
|
18
|
+
elif isinstance(x, tuple):
|
|
19
|
+
s = ', '.join(_repr(v) for v in x)
|
|
20
|
+
return f'({s})'
|
|
21
|
+
elif dc.is_dataclass(x):
|
|
22
|
+
s = ', '.join(f'{f.name}={_repr(getattr(x, f.name))}' for f in dc.fields(type(x)))
|
|
23
|
+
return f'{type(x).__name__}({s})'
|
|
24
|
+
else:
|
|
25
|
+
return repr(x)
|
|
26
|
+
|
|
27
|
+
get_origin = functools.cache(typing.get_origin)
|
|
28
|
+
is_dataclass = functools.cache(dc.is_dataclass)
|
|
29
|
+
dc_fields = functools.cache(dc.fields)
|
|
30
|
+
get_args = functools.cache(typing.get_args)
|
|
31
|
+
|
|
32
|
+
@functools.cache
|
|
33
|
+
def unwrap_union(cls):
|
|
34
|
+
if get_origin(cls) is types.UnionType:
|
|
35
|
+
return get_args(cls)[0]
|
|
36
|
+
else:
|
|
37
|
+
return cls
|
|
38
|
+
|
|
39
|
+
def field_from_sa(cls: type[T], arr: pyarrow.Array | None) -> T | pyarrow.Array | None:
|
|
40
|
+
if arr is None:
|
|
41
|
+
return None
|
|
42
|
+
cls = unwrap_union(cls)
|
|
43
|
+
if is_dataclass(cls):
|
|
44
|
+
return dc_from_sa(cls, arr)
|
|
45
|
+
elif get_origin(cls) is tuple:
|
|
46
|
+
return tuple_from_sa(cls, arr)
|
|
47
|
+
else:
|
|
48
|
+
return arr
|
|
49
|
+
|
|
50
|
+
def arr_field(arr, dc_field: dc.Field):
|
|
51
|
+
try:
|
|
52
|
+
return arr.field(dc_field.name)
|
|
53
|
+
except KeyError:
|
|
54
|
+
if dc_field.default is dc.MISSING:
|
|
55
|
+
raise
|
|
56
|
+
else:
|
|
57
|
+
return dc_field.default
|
|
58
|
+
|
|
59
|
+
def dc_from_sa(cls: type[T], arr: pyarrow.StructArray) -> T:
|
|
60
|
+
return cls(*(field_from_sa(f.type, arr_field(arr, f)) for f in dc_fields(cls)))
|
|
61
|
+
|
|
62
|
+
def tuple_from_sa(cls: type[tuple], arr: pyarrow.Array) -> tuple:
|
|
63
|
+
return cls((field_from_sa(t, arr.field(str(idx))) for (idx, t) in enumerate(get_args(cls))))
|
|
64
|
+
|
|
65
|
+
@functools.cache
|
|
66
|
+
def unwrap_optional(cls: type) -> type | None:
|
|
67
|
+
if get_origin(cls) is not types.UnionType:
|
|
68
|
+
return cls
|
|
69
|
+
|
|
70
|
+
args = get_args(cls)
|
|
71
|
+
assert len(args) == 2
|
|
72
|
+
assert args[1] is types.NoneType
|
|
73
|
+
return args[0]
|
|
74
|
+
|
|
75
|
+
# Generic recursion on dataclasses
|
|
76
|
+
def map_dc(cls: type[T], fn: typing.Callable, *xs: T) -> T:
|
|
77
|
+
unwrapped_cls = unwrap_optional(cls)
|
|
78
|
+
if unwrapped_cls is not None:
|
|
79
|
+
# Optional fields might be missing; if so, don't recurse.
|
|
80
|
+
for x in xs:
|
|
81
|
+
if x is None:
|
|
82
|
+
return None
|
|
83
|
+
cls = unwrapped_cls
|
|
84
|
+
|
|
85
|
+
if is_dataclass(cls):
|
|
86
|
+
return cls(*(
|
|
87
|
+
map_dc(f.type, fn, *(getattr(x, f.name) for x in xs))
|
|
88
|
+
for f in dc_fields(cls)
|
|
89
|
+
))
|
|
90
|
+
elif get_origin(cls) is tuple:
|
|
91
|
+
return cls(
|
|
92
|
+
map_dc(t, fn, *(x[idx] for x in xs))
|
|
93
|
+
for (idx, t) in enumerate(get_args(cls))
|
|
94
|
+
)
|
|
95
|
+
else:
|
|
96
|
+
return fn(*xs)
|
|
97
|
+
|
|
98
|
+
def dc_from_la(cls: type[T], la: pyarrow.ListArray) -> T:
|
|
99
|
+
"""Converts ListArray of Structs into dataclass of ListArrays."""
|
|
100
|
+
dc_sa = dc_from_sa(cls, la.values)
|
|
101
|
+
return map_dc(cls, lambda arr: pyarrow.ListArray.from_arrays(la.offsets, arr), dc_sa)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class RollbackMode(Enum):
|
|
105
|
+
ALL = 'all' # All frames in the replay.
|
|
106
|
+
FIRST = 'first' # Only the first frame, as seen by the player
|
|
107
|
+
LAST = 'last' # Only the finalized frames; the "true" frame sequence.
|
|
108
|
+
|
|
109
|
+
def frames_from_sa(
|
|
110
|
+
arrow_frames,
|
|
111
|
+
rollback_mode: RollbackMode = RollbackMode.ALL,
|
|
112
|
+
) -> typing.Optional[Frame]:
|
|
113
|
+
if arrow_frames is None:
|
|
114
|
+
return None
|
|
115
|
+
|
|
116
|
+
# Handle rollback mode
|
|
117
|
+
if rollback_mode is RollbackMode.FIRST:
|
|
118
|
+
index = arrow_frames.field('id').tolist()
|
|
119
|
+
first_indices = []
|
|
120
|
+
next_idx = index[0]
|
|
121
|
+
assert next_idx == -123
|
|
122
|
+
for i, idx in enumerate(index):
|
|
123
|
+
if idx == next_idx:
|
|
124
|
+
first_indices.append(i)
|
|
125
|
+
next_idx += 1
|
|
126
|
+
arrow_frames = arrow_frames.take(first_indices)
|
|
127
|
+
elif rollback_mode is RollbackMode.LAST:
|
|
128
|
+
index = arrow_frames.field('id').tolist()
|
|
129
|
+
next_idx = index[-1]
|
|
130
|
+
last_indices = []
|
|
131
|
+
for i, idx in reversed(list(enumerate(index))):
|
|
132
|
+
if idx == next_idx:
|
|
133
|
+
last_indices.append(i)
|
|
134
|
+
next_idx -= 1
|
|
135
|
+
assert next_idx == -124
|
|
136
|
+
last_indices.reverse()
|
|
137
|
+
arrow_frames = arrow_frames.take(last_indices)
|
|
138
|
+
|
|
139
|
+
ports = []
|
|
140
|
+
port_arrays = arrow_frames.field('ports')
|
|
141
|
+
for p in ('P1', 'P2', 'P3', 'P4'):
|
|
142
|
+
try: port = port_arrays.field(p)
|
|
143
|
+
except KeyError: continue
|
|
144
|
+
leader = dc_from_sa(Data, port.field('leader'))
|
|
145
|
+
try: follower = dc_from_sa(Data, port.field('follower'))
|
|
146
|
+
except KeyError: follower = None
|
|
147
|
+
ports.append(PortData(leader, follower))
|
|
148
|
+
|
|
149
|
+
# Extract items if available
|
|
150
|
+
items = None
|
|
151
|
+
try:
|
|
152
|
+
items_array = arrow_frames.field('item')
|
|
153
|
+
items = dc_from_la(Item, items_array)
|
|
154
|
+
except KeyError:
|
|
155
|
+
pass
|
|
156
|
+
|
|
157
|
+
# Extract FoD platforms if available
|
|
158
|
+
fod_platforms: FodPlatformMove | None = None
|
|
159
|
+
try:
|
|
160
|
+
fod_platform_array = arrow_frames.field('fod_platform')
|
|
161
|
+
fod_platforms = dc_from_la(FodPlatformMove, fod_platform_array)
|
|
162
|
+
except KeyError:
|
|
163
|
+
pass
|
|
164
|
+
|
|
165
|
+
return Frame(
|
|
166
|
+
id=arrow_frames.field('id'),
|
|
167
|
+
ports=tuple(ports),
|
|
168
|
+
items=items,
|
|
169
|
+
fod_platforms=fod_platforms
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
def field_from_json(cls, json):
|
|
173
|
+
if json is None:
|
|
174
|
+
return None
|
|
175
|
+
cls = unwrap_union(cls)
|
|
176
|
+
if dc.is_dataclass(cls):
|
|
177
|
+
return dc_from_json(cls, json)
|
|
178
|
+
elif typing.get_origin(cls) is tuple:
|
|
179
|
+
return tuple_from_json(cls, json)
|
|
180
|
+
elif issubclass(cls, Enum):
|
|
181
|
+
return cls[underscore(json).upper()]
|
|
182
|
+
else:
|
|
183
|
+
return json
|
|
184
|
+
|
|
185
|
+
def dc_from_json(cls, json):
|
|
186
|
+
return cls(*(field_from_json(f.type, json.get(f.name)) for f in dc.fields(cls)))
|
|
187
|
+
|
|
188
|
+
def tuple_from_json(cls, json):
|
|
189
|
+
child_cls = typing.get_args(cls)[0]
|
|
190
|
+
|
|
191
|
+
if type(json) is dict:
|
|
192
|
+
items = json.items()
|
|
193
|
+
else:
|
|
194
|
+
items = enumerate(json)
|
|
195
|
+
|
|
196
|
+
return tuple((field_from_json(child_cls, val) for (idx, val) in items))
|
peppi_py/util.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import pyarrow
|
|
2
|
+
import dataclasses as dc
|
|
3
|
+
|
|
4
|
+
def _repr(self):
|
|
5
|
+
if isinstance(self, pyarrow.Array):
|
|
6
|
+
s = ', '.join(repr(v.as_py()) for v in self[:3])
|
|
7
|
+
if len(self) > 3:
|
|
8
|
+
s += ', ...'
|
|
9
|
+
return f'[{s}]'
|
|
10
|
+
elif isinstance(self, tuple):
|
|
11
|
+
s = ', '.join(_repr(v) for v in self)
|
|
12
|
+
return f'({s})'
|
|
13
|
+
elif dc.is_dataclass(self):
|
|
14
|
+
s = ', '.join(f'{f.name}={_repr(getattr(self, f.name))}' for f in dc.fields(type(self)))
|
|
15
|
+
return f'{type(self).__name__}({s})'
|
|
16
|
+
else:
|
|
17
|
+
return repr(self)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
peppi_py/__init__.py,sha256=4to3yVoNHnsV1W6kQzCnXn1njIUJdbLL9oYZstq52G8,748
|
|
2
|
+
peppi_py/_peppi.abi3.so,sha256=x6VMDJA_xWqpD3vtuuDI2Obrz8fWJiOo-L5vEWjp_1o,2440648
|
|
3
|
+
peppi_py/frame.py,sha256=l1f0nyvWPTCo5ZDmFg5gYiNTum8nE8J5fI9xMnRto4k,2942
|
|
4
|
+
peppi_py/game.py,sha256=3oWv15Ik96mJ0glTEa0z2PxRzjvJtcWhWu8hJshjFmk,2222
|
|
5
|
+
peppi_py/parse.py,sha256=veTt3er79VeVmT4qedX_zM_Mq7p_CC1OMGhiG_E1k2w,5332
|
|
6
|
+
peppi_py/util.py,sha256=z6Q_NUCOwlpmpvQggkbQv1RNXX52Q7Nis0apXKb4_XU,475
|
|
7
|
+
peppi_py_vladfi-0.9.0.dist-info/METADATA,sha256=-PtlNUXkZC4uPC1h8bsBAK1BazmD6JwdPbxJE5vYSmQ,143
|
|
8
|
+
peppi_py_vladfi-0.9.0.dist-info/WHEEL,sha256=GOcqNI272zbCryxmLtJmciY1wvncsOuTwTM3MFIoyvQ,145
|
|
9
|
+
peppi_py_vladfi-0.9.0.dist-info/RECORD,,
|