• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Olezhich / CueTools / 22798638048

07 Mar 2026 11:59AM UTC coverage: 90.201%. First build
22798638048

Pull #40

github

web-flow
Merge 883b8964b into e5789f659
Pull Request #40: support for REM COMPOSER, ISRC and PREGAP keywords without parsing th…

8 of 9 new or added lines in 2 files covered. (88.89%)

359 of 398 relevant lines covered (90.2%)

0.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.03
/cuetools/parser/parser.py
1
from pathlib import Path
1✔
2
from typing import Iterator
1✔
3
from cuetools.models import AlbumData, TrackData
1✔
4
from cuetools.parser.errors import CueParseError, CueValidationError
1✔
5
from cuetools.parser.handlers import title_case_handler
1✔
6
from cuetools.parser.lex import Token, lex
1✔
7

8
WAVE_EXTENSIONS = ['.flac', '.ape', '.wav']
1✔
9

10

11
def load_f_iter(cue: Iterator[str], strict_title_case: bool = False) -> AlbumData:
1✔
12
    """loading an object from an iterator"""
13
    album = AlbumData()
1✔
14
    current_file = None
1✔
15
    current_track = None
1✔
16

17
    current_line = 0
1✔
18

19
    for line in cue:
1✔
20
        current_line += 1
1✔
21
        tokens = [i for i in lex(line)]
1✔
22
        try:
1✔
23
            match tokens[0].type:
1✔
24
                case Token.PERFORMER:
1✔
25
                    performer = tokens[1]
1✔
26
                    if not current_track:
1✔
27
                        title_case_handler(
1✔
28
                            performer,
29
                            strict_title_case,
30
                            lambda x: setattr(album, 'performer', x),
31
                            album.set_performer,
32
                            current_line,
33
                            line,
34
                            'album performer name',
35
                        )
36
                    else:
37
                        title_case_handler(
×
38
                            performer,
39
                            strict_title_case,
40
                            lambda x: setattr(current_track, 'performer', x),
41
                            current_track.set_performer,
42
                            current_line,
43
                            line,
44
                            'track performer name',
45
                        )
46
                case Token.TITLE:
1✔
47
                    title = tokens[1]
1✔
48
                    if not current_track:
1✔
49
                        title_case_handler(
1✔
50
                            title,
51
                            strict_title_case,
52
                            lambda x: setattr(album, 'title', x),
53
                            album.set_title,
54
                            current_line,
55
                            line,
56
                            'album title',
57
                        )
58
                    else:
59
                        title_case_handler(
1✔
60
                            title,
61
                            strict_title_case,
62
                            lambda x: setattr(current_track, 'title', x),
63
                            current_track.set_title,
64
                            current_line,
65
                            line,
66
                            'track title',
67
                        )
68
                case Token.FILE:
1✔
69
                    filepath = tokens[1]
1✔
70
                    match filepath.type:
1✔
71
                        case Token.ARG_QUOTES:
1✔
72
                            file_type = tokens[2]
1✔
73
                            match file_type.type:
1✔
74
                                case Token.WAVE:
1✔
75
                                    if not any(
1✔
76
                                        filepath.lexeme.endswith(ext)
77
                                        for ext in WAVE_EXTENSIONS
78
                                    ):
79
                                        raise CueParseError(
1✔
80
                                            current_line,
81
                                            line,
82
                                            f"""current WAVE file type like {', '.join(f"'{i}'" for i in WAVE_EXTENSIONS)}""",
83
                                            file_type.lexeme,
84
                                            file_type.pos,
85
                                        )
86
                                case _:
×
87
                                    raise CueParseError(
×
88
                                        current_line,
89
                                        line,
90
                                        'file type tag',
91
                                        file_type.lexeme,
92
                                        file_type.pos,
93
                                    )
94

95
                            current_file = (  # noqa: F841 # type: ignore
1✔
96
                                Path(filepath.lexeme),
97
                                current_line,
98
                                line,
99
                                filepath.pos,
100
                                filepath.lexeme,
101
                            )
102
                        case _:
×
103
                            CueParseError(
×
104
                                current_line,
105
                                line,
106
                                'audiofile path',
107
                                filepath.lexeme,
108
                                filepath.pos,
109
                            )
110
                case Token.ISRC:
1✔
111
                    ...
1✔
112
                case Token.PREGAP:
1✔
NEW
113
                    ...
×
114
                case Token.REM:
1✔
115
                    rem_type = tokens[1]
1✔
116
                    value = tokens[2]
1✔
117
                    match rem_type.type:
1✔
118
                        case Token.GENRE:
1✔
119
                            title_case_handler(
1✔
120
                                value,
121
                                strict_title_case,
122
                                lambda x: setattr(album.rem, 'genre', x),
123
                                album.rem.set_genre,
124
                                current_line,
125
                                line,
126
                                'album genre',
127
                            )
128
                        case Token.DATE:
1✔
129
                            try:
1✔
130
                                album.rem.date = int(value.lexeme)
1✔
131
                            except ValueError as e:
1✔
132
                                raise CueValidationError(
1✔
133
                                    current_line, line, value.pos, value.lexeme, e
134
                                )
135
                        case Token.REPLAYGAIN_ALBUM_GAIN:
1✔
136
                            try:
1✔
137
                                album.rem.replaygain_gain = value.lexeme  # type: ignore[assignment]
1✔
138
                            except ValueError as e:
1✔
139
                                raise CueValidationError(
1✔
140
                                    current_line, line, value.pos, value.lexeme, e
141
                                )
142
                        case Token.REPLAYGAIN_ALBUM_PEAK:
1✔
143
                            try:
1✔
144
                                album.rem.replaygain_peak = value.lexeme  # type: ignore[assignment]
1✔
145
                            except ValueError as e:
×
146
                                raise CueValidationError(
×
147
                                    current_line, line, value.pos, value.lexeme, e
148
                                )
149
                        case Token.REPLAYGAIN_TRACK_GAIN:
1✔
150
                            if not current_track:
1✔
151
                                raise CueParseError(
×
152
                                    current_line,
153
                                    line,
154
                                    'any TRACK tag before',
155
                                    tokens[0].lexeme,
156
                                    tokens[1].pos,
157
                                )
158
                            try:
1✔
159
                                current_track.rem.replaygain_gain = value.lexeme  # type: ignore[assignment]
1✔
160
                            except ValueError as e:
×
161
                                raise CueValidationError(
×
162
                                    current_line, line, value.pos, value.lexeme, e
163
                                )
164
                        case Token.REPLAYGAIN_TRACK_PEAK:
1✔
165
                            if not current_track:
1✔
166
                                raise CueParseError(
×
167
                                    current_line,
168
                                    line,
169
                                    'any TRACK tag before',
170
                                    tokens[0].lexeme,
171
                                    tokens[1].pos,
172
                                )
173
                            try:
1✔
174
                                current_track.rem.replaygain_peak = value.lexeme  # type: ignore[assignment]
1✔
175
                            except ValueError as e:
×
176
                                raise CueValidationError(
×
177
                                    current_line, line, value.pos, value.lexeme, e
178
                                )
179
                        case Token.DISCID:
1✔
180
                            ...
1✔
181
                        case Token.COMMENT:
1✔
182
                            ...
1✔
183
                        case Token.COMPOSER:
1✔
184
                            ...
1✔
185
                        case _:
×
186
                            raise CueParseError(
1✔
187
                                current_line,
188
                                line,
189
                                'Correct REM parameter',
190
                                rem_type.lexeme,
191
                                rem_type.pos,
192
                            )
193
                case Token.TRACK:
1✔
194
                    if not current_file:
1✔
195
                        raise CueParseError(
×
196
                            current_line,
197
                            line,
198
                            'any FILE tag before',
199
                            tokens[1].lexeme,
200
                            tokens[0].pos,
201
                        )
202
                    if len(tokens) < 2 or tokens[1].type != Token.AUDIO:
1✔
203
                        raise CueParseError(
×
204
                            current_line,
205
                            line,
206
                            'AUDIO tag',
207
                            tokens[1].lexeme if len(tokens) > 1 else 'Nothing',
208
                            tokens[0].pos,
209
                        )
210
                    if current_track:
1✔
211
                        try:
1✔
212
                            album.add_track(current_track)
1✔
213
                        except ValueError as e:
×
214
                            raise CueValidationError(
×
215
                                current_line, line, 0, 'Invalid previous track', e
216
                            )
217
                    current_track = TrackData(
1✔
218
                        track=int(tokens[0].lexeme),
219
                        file=current_file[0],
220
                    )
221
                case Token.INDEX:
1✔
222
                    if not current_track:
1✔
223
                        raise CueParseError(
1✔
224
                            current_line,
225
                            line,
226
                            'any TRACK tag before',
227
                            tokens[0].lexeme,
228
                            tokens[1].pos,
229
                        )
230
                    index_type = int(tokens[0].lexeme)
1✔
231
                    if index_type == 0:
1✔
232
                        if current_track.index00:
1✔
233
                            raise CueParseError(
×
234
                                current_line,
235
                                line,
236
                                'one INDEX 00 value',
237
                                'dublicate INDEX 00 value',
238
                                tokens[0].pos,
239
                            )
240
                        current_track.index00 = tokens[1].lexeme  # type: ignore[assignment]
1✔
241
                    elif index_type == 1:
1✔
242
                        current_track.index01 = tokens[1].lexeme  # type: ignore[assignment]
1✔
243
                case _:
×
244
                    raise CueParseError(
×
245
                        current_line,
246
                        line,
247
                        'Correct CUE keyword',
248
                        tokens[0].lexeme,
249
                        tokens[0].pos,
250
                    )
251
        except IndexError:
1✔
252
            raise CueParseError(
1✔
253
                current_line, line, 'More tokens to parse the string correctly', line, 0
254
            )
255

256
    if current_track:
1✔
257
        album.add_track(current_track)
1✔
258

259
    return album
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc