131131
132132import io
133133import zipfile
134- from typing import Optional
135134from xml .etree import ElementTree as ET
136135
137136from . import models as gp
138137
139138
140139# ── XML helpers ───────────────────────────────────────────────────────
141140
142- def _text (elem : Optional [ ET .Element ] , default : str = "" ) -> str :
141+ def _text (elem : ET .Element | None , default : str = "" ) -> str :
143142 if elem is None or elem .text is None :
144143 return default
145144 return elem .text .strip ()
146145
147146
148- def _int (elem : Optional [ ET .Element ] , default : int = 0 ) -> int :
147+ def _int (elem : ET .Element | None , default : int = 0 ) -> int :
149148 try :
150149 return int (_text (elem , str (default )))
151150 except ValueError :
152151 return default
153152
154153
155- def _float (elem : Optional [ ET .Element ] , default : float = 0.0 ) -> float :
154+ def _float (elem : ET .Element | None , default : float = 0.0 ) -> float :
156155 if elem is None or elem .text is None :
157156 return default
158157 try :
@@ -214,7 +213,7 @@ def _split_tokens(text: str) -> list[str]:
214213}
215214
216215
217- def _music_font_symbol (token : str ) -> " gp.MusicFontSymbol" :
216+ def _music_font_symbol (token : str ) -> gp .MusicFontSymbol :
218217 """Map a GPIF music-font token (``noteheadHalf``, ``guitarGolpe``,
219218 …) onto PyGuitarPro's ``MusicFontSymbol`` enum.
220219
@@ -229,7 +228,7 @@ def _music_font_symbol(token: str) -> "gp.MusicFontSymbol":
229228 return gp .MusicFontSymbol .none
230229
231230
232- def _technique_symbol_placement (token : str ) -> " gp.TechniqueSymbolPlacement" :
231+ def _technique_symbol_placement (token : str ) -> gp .TechniqueSymbolPlacement :
233232 """GPIF ``<TechniquePlacement>`` token → enum. Unknown tokens fall
234233 back to ``outside`` (alphaTab's default)."""
235234 try :
@@ -239,53 +238,53 @@ def _technique_symbol_placement(token: str) -> "gp.TechniqueSymbolPlacement":
239238
240239
241240_DURATION_MAP = {
242- "Long" : - 4 , # Longa / QuadrupleWhole (4 whole notes)
241+ "Long" : - 4 , # Longa / QuadrupleWhole (4 whole notes)
243242 "DoubleWhole" : - 2 , # Breve; represented with AT's negative sentinel
244- "Whole" : 1 ,
245- "Half" : 2 ,
243+ "Whole" : 1 ,
244+ "Half" : 2 ,
246245 "Quarter" : 4 ,
247- "Eighth" : 8 ,
248- "16th" : 16 ,
249- "32nd" : 32 ,
250- "64th" : 64 ,
251- "128th" : 128 ,
252- "256th" : 256 ,
246+ "Eighth" : 8 ,
247+ "16th" : 16 ,
248+ "32nd" : 32 ,
249+ "64th" : 64 ,
250+ "128th" : 128 ,
251+ "256th" : 256 ,
253252}
254253
255254_DYNAMIC_VELOCITY = {
256255 "PPP" : 15 ,
257- "PP" : 31 ,
258- "P" : 47 ,
259- "MP" : 63 ,
260- "MF" : 79 ,
261- "F" : 95 ,
262- "FF" : 111 ,
256+ "PP" : 31 ,
257+ "P" : 47 ,
258+ "MP" : 63 ,
259+ "MF" : 79 ,
260+ "F" : 95 ,
261+ "FF" : 111 ,
263262 "FFF" : 127 ,
264263}
265264
266265_CLEF_MAP = {
267- "G2" : 0 , # treble
268- "F4" : 1 , # bass
269- "C4" : 2 , # tenor
270- "C3" : 3 , # alto
266+ "G2" : 0 , # treble
267+ "F4" : 1 , # bass
268+ "C4" : 2 , # tenor
269+ "C3" : 3 , # alto
271270 "Neutral" : 0 , # neutral — use treble as default
272271}
273272
274273# Accent flag bits (<Accent>N</Accent> inside <Note>)
275- _ACCENT_STACCATO = 0x01
276- _ACCENT_HEAVY = 0x04
277- _ACCENT_NORMAL = 0x08
278- _ACCENT_TENUTO = 0x10
274+ _ACCENT_STACCATO = 0x01
275+ _ACCENT_HEAVY = 0x04
276+ _ACCENT_NORMAL = 0x08
277+ _ACCENT_TENUTO = 0x10
279278
280279# Slide flag bits (<Property name="Slide">/<Flags>)
281- _SLIDE_SHIFT = 0x01
282- _SLIDE_LEGATO = 0x02
283- _SLIDE_OUT_DOWN = 0x04
284- _SLIDE_OUT_UP = 0x08
285- _SLIDE_IN_FROM_BELOW = 0x10
286- _SLIDE_IN_FROM_ABOVE = 0x20
287- _SLIDE_PICK_DOWN = 0x40
288- _SLIDE_PICK_UP = 0x80
280+ _SLIDE_SHIFT = 0x01
281+ _SLIDE_LEGATO = 0x02
282+ _SLIDE_OUT_DOWN = 0x04
283+ _SLIDE_OUT_UP = 0x08
284+ _SLIDE_IN_FROM_BELOW = 0x10
285+ _SLIDE_IN_FROM_ABOVE = 0x20
286+ _SLIDE_PICK_DOWN = 0x40
287+ _SLIDE_PICK_UP = 0x80
289288# 0x40/0x80 = pick slides (no direct PyGuitarPro mapping)
290289
291290# GP6 percussion mapping: (element, variation) → MIDI articulation number.
@@ -326,6 +325,7 @@ def _gp6_percussion_articulation(element: int, variation: int) -> int:
326325 return row [variation ]
327326 return 38 # default: Snare (hit)
328327
328+
329329# GPIF Target → PyGuitarPro direction-sign names (Coda/Segno/Fine "destinations")
330330_DIRECTION_TARGETS = {
331331 "Coda" : "Coda" ,
@@ -1233,7 +1233,8 @@ def _fill_chord_degrees(chord: gp.Chord, chord_info: ET.Element) -> None:
12331233 from <Chord>/<KeyNote>/<BassNote>/<Degree> GPIF structure."""
12341234 # Root and bass note
12351235 step_map = {"C" : 0 , "D" : 2 , "E" : 4 , "F" : 5 , "G" : 7 , "A" : 9 , "B" : 11 }
1236- def make_pitch (node : Optional [ET .Element ]) -> Optional [gp .PitchClass ]:
1236+
1237+ def make_pitch (node : ET .Element | None ) -> gp .PitchClass | None :
12371238 if node is None :
12381239 return None
12391240 step = node .get ("step" , "" )
@@ -1493,8 +1494,6 @@ def _build_lookup_tables(self, root: ET.Element) -> None:
14931494
14941495 def _read_master_bars (self , song : gp .Song ) -> None :
14951496 """Build `song.measureHeaders` from <MasterBars>/<MasterBar>."""
1496- # Cache nodes for later per-track walk.
1497- root_doc = None
14981497 for mb in self ._iter_master_bars (song ):
14991498 pass # no-op; iteration finalises the list
15001499 # alphaTab's _buildModel clears hasDoubleBar on the last master
@@ -1521,7 +1520,7 @@ def _iter_master_bars(self, song: gp.Song):
15211520 previous_header = header
15221521 yield mb
15231522
1524- def _root_from_song (self , song : gp .Song ) -> Optional [ ET .Element ] :
1523+ def _root_from_song (self , song : gp .Song ) -> ET .Element | None :
15251524 """The reader kept the root element alive via closure — we need it
15261525 for master-bar/bar walks. Stash it on the instance instead."""
15271526 return getattr (self , "_root" , None )
@@ -1779,7 +1778,7 @@ def _assemble_tracks(self, song: gp.Song) -> None:
17791778 measure = self ._build_measure (track , header , bar_id )
17801779 track .measures .append (measure )
17811780
1782- def _build_measure (self , track : gp .Track , header : gp .MeasureHeader , bar_id : Optional [ str ] ) -> gp .Measure :
1781+ def _build_measure (self , track : gp .Track , header : gp .MeasureHeader , bar_id : str | None ) -> gp .Measure :
17831782 measure = gp .Measure (track = track , header = header )
17841783 measure .voices = []
17851784
@@ -1934,7 +1933,7 @@ def _build_beat(self, voice: gp.Voice, beat_id: str) -> gp.Beat:
19341933
19351934 return beat
19361935
1937- def _build_note (self , beat : gp .Beat , note_id : str , velocity : int ) -> Optional [ gp .Note ] :
1936+ def _build_note (self , beat : gp .Beat , note_id : str , velocity : int ) -> gp .Note | None :
19381937 raw = self ._notes_raw .get (note_id )
19391938 if raw is None :
19401939 return None
0 commit comments