chadwickpy 0.1.0__py3-none-any.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.
- chadwickpy/__init__.py +8 -0
- chadwickpy/__main__.py +8 -0
- chadwickpy/book.py +128 -0
- chadwickpy/box.py +1145 -0
- chadwickpy/file.py +156 -0
- chadwickpy/game.py +458 -0
- chadwickpy/gameiter.py +661 -0
- chadwickpy/guard.py +50 -0
- chadwickpy/lint.py +112 -0
- chadwickpy/parse.py +1170 -0
- chadwickpy/py.typed +0 -0
- chadwickpy/roster.py +160 -0
- chadwickpy/tools/cli.py +614 -0
- chadwickpy/tools/comment.py +156 -0
- chadwickpy/tools/cwbox.py +553 -0
- chadwickpy/tools/cwboxsml.py +1111 -0
- chadwickpy/tools/cwboxxml.py +339 -0
- chadwickpy/tools/cwgame.py +749 -0
- chadwickpy/tools/daily.py +464 -0
- chadwickpy/tools/events.py +1046 -0
- chadwickpy/tools/sub.py +220 -0
- chadwickpy/tools/tools.py +80 -0
- chadwickpy/write.py +123 -0
- chadwickpy/xmlwrite.py +128 -0
- chadwickpy-0.1.0.dist-info/METADATA +103 -0
- chadwickpy-0.1.0.dist-info/RECORD +31 -0
- chadwickpy-0.1.0.dist-info/WHEEL +4 -0
- chadwickpy-0.1.0.dist-info/entry_points.txt +8 -0
- chadwickpy-0.1.0.dist-info/licenses/COPYING-chadwick +340 -0
- chadwickpy-0.1.0.dist-info/licenses/LICENSE +674 -0
- chadwickpy-0.1.0.dist-info/licenses/NOTICE +18 -0
chadwickpy/__init__.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Pure-Python port of the Chadwick baseball tools (GPL-2.0-or-later source; see module headers)."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
__version__ = version("chadwickpy")
|
|
7
|
+
except PackageNotFoundError: # running from a source tree that is not installed
|
|
8
|
+
__version__ = "0+unknown"
|
chadwickpy/__main__.py
ADDED
chadwickpy/book.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""Port of Chadwick's ``src/cwlib/book.c`` (``cw_scorebook_read``) and ``cw_file_find_first_game``.
|
|
2
|
+
|
|
3
|
+
Chadwick is Copyright (c) 2002-2023 Dr T L Turocy and the Chadwick Baseball
|
|
4
|
+
Bureau, licensed GPL-2.0-or-later; this module is a derivative of it and keeps
|
|
5
|
+
that notice.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from collections.abc import Callable, Iterator
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
|
|
11
|
+
from chadwickpy.file import CFile, StrTok
|
|
12
|
+
from chadwickpy.game import Game, read_game
|
|
13
|
+
|
|
14
|
+
COMMENT_BUFSIZE = 256 # ``char buf[256]`` in ``cw_scorebook_read_comments`` and the finder
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _read_comments(file: CFile) -> tuple[bool, list[str]]:
|
|
18
|
+
"""``cw_scorebook_read_comments``: leading ``com`` lines. False if the file is empty."""
|
|
19
|
+
tok = StrTok()
|
|
20
|
+
comments: list[str] = []
|
|
21
|
+
while True:
|
|
22
|
+
buf = file.fgets(COMMENT_BUFSIZE)
|
|
23
|
+
if buf is None:
|
|
24
|
+
return False, comments
|
|
25
|
+
kind = tok(buf)
|
|
26
|
+
com = tok(None)
|
|
27
|
+
if kind is not None and kind == "com" and com is not None:
|
|
28
|
+
comments.append(com)
|
|
29
|
+
else:
|
|
30
|
+
return True, comments
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def find_first_game(file: CFile) -> bool:
|
|
34
|
+
"""``cw_file_find_first_game``: rewind, then stop at the first line that begins ``id``"""
|
|
35
|
+
tok = StrTok()
|
|
36
|
+
file.setpos(0)
|
|
37
|
+
while not file.eof:
|
|
38
|
+
filepos = file.getpos()
|
|
39
|
+
buf = file.fgets(COMMENT_BUFSIZE)
|
|
40
|
+
if buf is None:
|
|
41
|
+
return False
|
|
42
|
+
kind = tok(buf)
|
|
43
|
+
if kind is not None and kind == "id":
|
|
44
|
+
file.setpos(filepos)
|
|
45
|
+
return True
|
|
46
|
+
return False
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _strcmp(a: str | None, b: str | None) -> int:
|
|
50
|
+
"""``strcmp``, which crashes in C on a NULL argument"""
|
|
51
|
+
if a is None or b is None:
|
|
52
|
+
raise ValueError("NULL string passed to strcmp (Chadwick would crash)")
|
|
53
|
+
return (a > b) - (a < b)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class Scorebook:
|
|
58
|
+
"""``CWScorebook``: the leading comments of an event file and its games"""
|
|
59
|
+
|
|
60
|
+
comments: list[str] = field(default_factory=list)
|
|
61
|
+
games: list[Game] = field(default_factory=list)
|
|
62
|
+
|
|
63
|
+
def append_game(self, game: Game | None) -> bool:
|
|
64
|
+
"""``cw_scorebook_append_game``: False (nothing done) for NULL"""
|
|
65
|
+
if game is None:
|
|
66
|
+
return False
|
|
67
|
+
self.games.append(game)
|
|
68
|
+
return True
|
|
69
|
+
|
|
70
|
+
def insert_game(self, game: Game | None) -> bool:
|
|
71
|
+
"""``cw_scorebook_insert_game``: before the first game not earlier by date, then number"""
|
|
72
|
+
if game is None:
|
|
73
|
+
return False
|
|
74
|
+
if not self.games:
|
|
75
|
+
self.games.append(game)
|
|
76
|
+
return True
|
|
77
|
+
i = 0
|
|
78
|
+
while i < len(self.games):
|
|
79
|
+
g = self.games[i]
|
|
80
|
+
if _strcmp(g.info_lookup("date"), game.info_lookup("date")) < 0 or (
|
|
81
|
+
_strcmp(g.info_lookup("date"), game.info_lookup("date")) == 0
|
|
82
|
+
and _strcmp(g.info_lookup("number"), game.info_lookup("number")) < 0
|
|
83
|
+
):
|
|
84
|
+
i += 1
|
|
85
|
+
else:
|
|
86
|
+
break
|
|
87
|
+
self.games.insert(i, game)
|
|
88
|
+
return True
|
|
89
|
+
|
|
90
|
+
def remove_game(self, game_id: str) -> Game | None:
|
|
91
|
+
"""``cw_scorebook_remove_game``: the first game with this id, or ``None``"""
|
|
92
|
+
for i, game in enumerate(self.games):
|
|
93
|
+
if game.game_id == game_id:
|
|
94
|
+
return self.games.pop(i)
|
|
95
|
+
return None
|
|
96
|
+
|
|
97
|
+
def iterate(self, f: Callable[[Game], bool] | None = None) -> Iterator[Game]:
|
|
98
|
+
"""``cw_scorebook_iterate`` / ``_iterator_next``: games for which ``f`` is true"""
|
|
99
|
+
for game in self.games:
|
|
100
|
+
if f is None or f(game):
|
|
101
|
+
yield game
|
|
102
|
+
|
|
103
|
+
def read(self, data: bytes) -> int:
|
|
104
|
+
"""``cw_scorebook_read``: the number of games read, or -1 for an empty file"""
|
|
105
|
+
file = CFile(data)
|
|
106
|
+
ok, comments = _read_comments(file)
|
|
107
|
+
self.comments.extend(comments)
|
|
108
|
+
if not ok:
|
|
109
|
+
return -1
|
|
110
|
+
find_first_game(file)
|
|
111
|
+
count = 0
|
|
112
|
+
while not file.eof:
|
|
113
|
+
if not self.append_game(read_game(file)):
|
|
114
|
+
break
|
|
115
|
+
count += 1
|
|
116
|
+
return count
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def scorebook_read(data: bytes) -> list[Game] | None:
|
|
120
|
+
"""``cw_scorebook_read``: every game of an event file, or ``None`` where C returns -1.
|
|
121
|
+
|
|
122
|
+
``None`` is the "could not open file" case of ``cwtools_process_scorebook``: an
|
|
123
|
+
empty file.
|
|
124
|
+
"""
|
|
125
|
+
book = Scorebook()
|
|
126
|
+
if book.read(data) < 0:
|
|
127
|
+
return None
|
|
128
|
+
return book.games
|