From 99a7c48d8c331f5a0a61884905c140ec654b3b8f Mon Sep 17 00:00:00 2001 From: Jonas Hahnfeld Date: Wed, 18 Sep 2019 21:56:02 +0200 Subject: [PATCH 3/5] Fix musicxml2ly with Python 3 This passes the targets doc and check / test. --- python/book_snippets.py | 24 +++++++++-------- python/musicexp.py | 29 ++++++++++---------- python/musicxml.py | 20 +++++++------- python/rational.py | 13 ++++++--- python/utilities.py | 2 +- scripts/musicxml2ly.py | 60 ++++++++++++++++++++--------------------- 6 files changed, 76 insertions(+), 72 deletions(-) diff --git a/python/book_snippets.py b/python/book_snippets.py index 0a2f3fbb31..0f7888bcb9 100644 --- a/python/book_snippets.py +++ b/python/book_snippets.py @@ -605,7 +605,8 @@ class LilypondSnippet (Snippet): if self.relevant_contents (existing) != self.relevant_contents (self.full_ly ()): warning ("%s: duplicate filename but different contents of original file,\n\ printing diff against existing file." % filename) - ly.stderr_write (self.filter_pipe (self.full_ly (), 'diff -u %s -' % filename)) + diff_against_existing = self.filter_pipe (self.full_ly ().encode ('utf-8'), 'diff -u %s -' % filename) + ly.stderr_write (diff_against_existing.decode ('utf-8')) else: out = open (filename, 'w') out.write (self.full_ly ()) @@ -757,7 +758,7 @@ printing diff against existing file." % filename) status = 0 output = stdout.read () status = stdout.close () - err = stderr.read () + err = stderr.read ().decode ('utf-8') if not status: status = 0 @@ -767,7 +768,7 @@ printing diff against existing file." % filename) ly.error (_ ("`%s' failed (%d)") % (cmd, exit_status)) ly.error (_ ("The error log is as follows:")) ly.stderr_write (err) - ly.stderr_write (stderr.read ()) + ly.stderr_write (stderr.read ().decode ('utf-8')) exit (status) debug ('\n') @@ -820,13 +821,13 @@ class LilypondFileSnippet (LilypondSnippet): LilypondSnippet.__init__ (self, type, match, formatter, line_number, global_options) self.filename = self.substring ('filename') self.contents = open (BookBase.find_file (self.filename, - global_options.include_path, global_options.original_dir)).read () + global_options.include_path, global_options.original_dir), 'rb').read () def get_snippet_code (self): - return self.contents; + return self.contents.decode ('utf-8') def verb_ly (self): - s = self.contents + s = self.get_snippet_code () s = re_begin_verbatim.split (s)[-1] s = re_end_verbatim.split (s)[0] if not NOGETTEXT in self.option_dict: @@ -838,7 +839,7 @@ class LilypondFileSnippet (LilypondSnippet): def ly (self): name = self.filename return ('\\sourcefilename \"%s\"\n\\sourcefileline 0\n%s' - % (name, self.contents)) + % (name, self.get_snippet_code ())) def final_basename (self): if self.global_options.use_source_file_names: @@ -888,6 +889,7 @@ class MusicXMLFileSnippet (LilypondFileSnippet): progress (_ ("Converting MusicXML file `%s'...\n") % self.filename) ly_code = self.filter_pipe (self.contents, 'musicxml2ly %s --out=- - ' % opts) + ly_code = ly_code.decode ('utf-8') return ly_code def ly (self): @@ -914,20 +916,20 @@ class MusicXMLFileSnippet (LilypondFileSnippet): if diff_against_existing: warning (_ ("%s: duplicate filename but different contents of original file,\n\ printing diff against existing file.") % xmlfilename) - ly.stderr_write (diff_against_existing) + ly.stderr_write (diff_against_existing.decode ('utf-8')) else: - out = open (xmlfilename, 'w') + out = open (xmlfilename, 'wb') out.write (self.contents) out.close () # also write the converted lilypond filename = path + '.ly' if os.path.exists (filename): - diff_against_existing = self.filter_pipe (self.full_ly (), 'diff -u %s -' % filename) + diff_against_existing = self.filter_pipe (self.full_ly ().encode ('utf-8'), 'diff -u %s -' % filename) if diff_against_existing: warning (_ ("%s: duplicate filename but different contents of converted lilypond file,\n\ printing diff against existing file.") % filename) - ly.stderr_write (diff_against_existing) + ly.stderr_write (diff_against_existing.decode ('utf-8')) else: out = open (filename, 'w') out.write (self.full_ly ()) diff --git a/python/musicexp.py b/python/musicexp.py index 29161432f1..5805827243 100644 --- a/python/musicexp.py +++ b/python/musicexp.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- import inspect import sys -import string import re import math import lilylib as ly @@ -19,7 +18,7 @@ whatOrnament = "" ly_dur = None # stores lilypond durations def escape_instrument_string (input_string): - retstring = string.replace (input_string, "\"", "\\\"") + retstring = input_string.replace("\"", "\\\"") if re.match ('.*[\r\n]+.*', retstring): rx = re.compile (r'[\n\r]+') strings = rx.split (retstring) @@ -397,7 +396,7 @@ class Pitch: while c.step < 0: c.step += 7 c.octave -= 1 - c.octave += c.step / 7 + c.octave += c.step // 7 c.step = c.step % 7 def lisp_expression (self): @@ -462,9 +461,9 @@ class Pitch: pitch_diff = (this_pitch_steps - previous_pitch_steps) previous_pitch = self if pitch_diff > 3: - return "'" * ((pitch_diff + 3) / 7) + return "'" * ((pitch_diff + 3) // 7) elif pitch_diff < -3: - return "," * ((-pitch_diff + 3) / 7) + return "," * ((-pitch_diff + 3) // 7) else: return "" @@ -529,7 +528,7 @@ class Music: printer.newline () return - lines = string.split (text, '\n') + lines = text.split('\n') for l in lines: if l: printer.unformatted_output ('% ' + l) @@ -692,11 +691,11 @@ class NestedMusic(Music): def get_properties (self): return ("'elements (list %s)" - % string.join ([x.lisp_expression() for x in self.elements])) + % ' '.join ([x.lisp_expression() for x in self.elements])) def get_subset_properties (self, predicate): return ("'elements (list %s)" - % string.join ([x.lisp_expression() for x in list(filter (predicate, self.elements))])) + % ' '.join ([x.lisp_expression() for x in list(filter (predicate, self.elements))])) def get_neighbor (self, music, dir): assert music.parent == self idx = self.elements.index (music) @@ -814,7 +813,7 @@ class Lyrics: for l in self.lyrics_syllables: lstr += l #lstr += "\n}" - return lstr.encode('utf-8') + return lstr class Header: @@ -1036,7 +1035,7 @@ class ChordEvent (NestedMusic): basepitch = previous_pitch if stem: printer (stem.ly_expression ()) - printer ('<%s>' % string.join (pitches)) + printer ('<%s>' % ' '.join (pitches)) previous_pitch = basepitch duration = self.get_duration () if duration: @@ -1475,7 +1474,7 @@ class FretBoardEvent (NestedMusic): notes = [] for n in fretboard_notes: notes.append (n.ly_expression ()) - contents = string.join (notes) + contents = ' '.join (notes) printer ('<%s>%s' % (contents,self.duration)) class FunctionWrapperEvent (Event): @@ -1665,7 +1664,7 @@ class RhythmicEvent(Event): return [ev.pre_note_ly (is_chord_element) for ev in self.associated_events] def ly_expression_pre_note (self, is_chord_element): - res = string.join (self.pre_note_ly (is_chord_element), ' ') + res = ' '.join (self.pre_note_ly (is_chord_element)) if res != '': res = res + ' ' return res @@ -1813,7 +1812,7 @@ class KeySignatureChange (Music): elif self.non_standard_alterations: alterations = [self.format_non_standard_alteration (a) for a in self.non_standard_alterations] - return "\\set Staff.keyAlterations = #`(%s)" % string.join (alterations, " ") + return "\\set Staff.keyAlterations = #`(%s)" % " ".join (alterations) else: return '' @@ -1857,7 +1856,7 @@ class TimeSignatureChange (Music): def format_fraction (self, frac): if isinstance (frac, list): l = [self.format_fraction (f) for f in frac] - return "(" + string.join (l, " ") + ")" + return "(" + " ".join (l) + ")" else: return "%s" % frac @@ -2074,7 +2073,7 @@ class FiguredBassEvent (NestedMusic): notes = [] for x in figured_bass_events: notes.append (x.ly_expression ()) - contents = string.join (notes) + contents = ' '.join (notes) if self.parentheses: contents = '[%s]' % contents printer ('<%s>' % contents) diff --git a/python/musicxml.py b/python/musicxml.py index e1ddeb20e4..fdf6e24c0e 100644 --- a/python/musicxml.py +++ b/python/musicxml.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -#import new -import string from rational import * import re import sys @@ -43,7 +41,7 @@ class Xml_node: if not self._children: return '' - return ''.join([c.get_text() for c in self._children]).encode('utf-8') + return ''.join([c.get_text() for c in self._children]) def message(self, msg): ly.warning(msg) @@ -198,7 +196,7 @@ class Identification(Xml_node): ret.append(result) else: ret.append(text) - return string.join(ret, "\n") + return "\n".join(ret) # get contents of the source-element(usually used for publishing information).(These contents are saved in a custom variable named "source" in the header of the .ly file.) def get_source(self): @@ -206,7 +204,7 @@ class Identification(Xml_node): ret = [] for r in source: ret.append(r.get_text()) - return string.join(ret, "\n") + return "\n".join(ret) def get_creator(self, type): creators = self.get_named_children('creator') @@ -386,7 +384,7 @@ class Duration(Music_xml_node): class Hash_text(Music_xml_node): def dump(self, indent=''): - ly.debug_output('%s' % string.strip(self._data)) + ly.debug_output('%s' % self._data.strip()) class Pitch(Music_xml_node): @@ -519,7 +517,7 @@ class Attributes(Measure_element): current_sig = [] for i in mxl.get_all_children(): if isinstance(i, Beats): - beats = string.split(i.get_text().strip(), "+") + beats = i.get_text().strip().split("+") current_sig = [int(j) for j in beats] elif isinstance(i, BeatType): current_sig.append(int(i.get_text())) @@ -959,7 +957,7 @@ class Syllabic(Music_xml_node): class Lyric(Music_xml_node): - def number(self): + def get_number(self): """ Return the number attribute(if it exists) of the lyric element. @@ -967,7 +965,7 @@ class Lyric(Music_xml_node): @return: The value of the number attribute """ if hasattr(self, 'number'): - return self.number + return int(self.number) else: return -1 @@ -1241,7 +1239,7 @@ class Musicxml_voice: self.has_lyrics = len(lyrics) > 0 for l in lyrics: - nr = l.number + nr = l.get_number() if(nr > 0) and not(nr in self._lyrics): self._lyrics.append(nr) @@ -1794,7 +1792,7 @@ def get_class(name): return classname else: class_name = name2class_name(name) - klass = new.classobj(class_name,(Music_xml_node,) , {}) + klass = type(class_name,(Music_xml_node,) , {}) class_dict[name] = klass return klass diff --git a/python/rational.py b/python/rational.py index 4a6eaa5823..c43f0a5576 100644 --- a/python/rational.py +++ b/python/rational.py @@ -3,6 +3,7 @@ import math as _math +from functools import total_ordering def _gcf(a, b): """Returns the greatest common factor of a and b.""" @@ -12,6 +13,7 @@ def _gcf(a, b): a, b = b, a % b return a +@total_ordering class Rational(object): """ This class provides an exact representation of rational numbers. @@ -161,11 +163,16 @@ class Rational(object): return other - other // self * self def __divmod__(self, other): return self // other, self % other - def __cmp__(self, other): + def __eq__(self, other): if other == 0: - return cmp(self._n, 0) + return self._n == 0 else: - return cmp(self - other, 0) + return (self - other)._n == 0 + def __lt__(self, other): + if other == 0: + return self._n < 0 + else: + return (self - other) < 0 def __pow__(self, other): if isinstance(other, int): if other < 0: diff --git a/python/utilities.py b/python/utilities.py index 3d339b0a9f..264e8e8b31 100644 --- a/python/utilities.py +++ b/python/utilities.py @@ -19,7 +19,7 @@ def escape_ly_output_string (input_string): return_string = input_string needs_quotes = not re.match ("^[a-zA-ZäöüÜÄÖßñ]*$", return_string); if needs_quotes: - return_string = "\"" + string.replace (return_string, "\"", "\\\"") + "\"" + return_string = "\"" + return_string.replace("\"", "\\\"") + "\"" return return_string def interpret_alter_element (alter_elm): diff --git a/scripts/musicxml2ly.py b/scripts/musicxml2ly.py index 49bb8b4fff..4be0f617c5 100755 --- a/scripts/musicxml2ly.py +++ b/scripts/musicxml2ly.py @@ -4,7 +4,6 @@ import optparse import sys import re import os -import string import codecs import zipfile import tempfile @@ -74,7 +73,7 @@ additional_definitions = { } def round_to_two_digits(val): - return round(val * 100) / 100 + return round(val * 100) // 100 def extract_paper_information(score_partwise): defaults = score_partwise.get_maybe_exist_named_child('defaults') @@ -311,14 +310,14 @@ def staff_attributes_to_string_tunings(mxl_attr): lines = 6 staff_lines = details.get_maybe_exist_named_child('staff-lines') if staff_lines: - lines = string.atoi(staff_lines.get_text()) + lines = int(staff_lines.get_text()) tunings = [musicexp.Pitch()] * lines staff_tunings = details.get_named_children('staff-tuning') for i in staff_tunings: p = musicexp.Pitch() line = 0 try: - line = string.atoi(i.line) - 1 + line = int(i.line) - 1 except ValueError: pass tunings[line] = p @@ -358,7 +357,7 @@ def staff_attributes_to_lily_staff(mxl_attr): for d in details: staff_lines = d.get_maybe_exist_named_child('staff-lines') if staff_lines: - lines = string.atoi(staff_lines.get_text()) + lines = int(staff_lines.get_text()) # TODO: Handle other staff attributes like staff-space, etc. @@ -849,19 +848,19 @@ def musicxml_transpose_to_lily(attributes): shift = musicexp.Pitch() octave_change = transpose.get_maybe_exist_named_child('octave-change') if octave_change: - shift.octave = string.atoi(octave_change.get_text()) - chromatic_shift = string.atoi(transpose.get_named_child('chromatic').get_text()) + shift.octave = int(octave_change.get_text()) + chromatic_shift = int(transpose.get_named_child('chromatic').get_text()) chromatic_shift_normalized = chromatic_shift % 12; (shift.step, shift.alteration) = [ (0, 0), (0, 1), (1, 0), (2, -1), (2, 0), (3, 0), (3, 1), (4, 0), (5, -1), (5, 0), (6, -1), (6, 0)][chromatic_shift_normalized]; - shift.octave += (chromatic_shift - chromatic_shift_normalized) / 12 + shift.octave += (chromatic_shift - chromatic_shift_normalized) // 12 diatonic = transpose.get_maybe_exist_named_child('diatonic') if diatonic: - diatonic_step = string.atoi(diatonic.get_text()) % 7 + diatonic_step = int(diatonic.get_text()) % 7 if diatonic_step != shift.step: # We got the alter incorrect! old_semitones = shift.semitones() @@ -883,7 +882,7 @@ def musicxml_staff_details_to_lily(attributes): stafflines = details.get_maybe_exist_named_child('staff-lines') if stafflines: - lines = string.atoi(stafflines.get_text()); + lines = int(stafflines.get_text()); lines_event = musicexp.StaffLinesEvent(lines); ret.append(lines_event); @@ -1243,12 +1242,12 @@ def musicxml_dynamics_to_lily_event(dynentry): if not dynamicsname in dynamics_available: # Get rid of - in tag names (illegal in ly tags!) dynamicstext = dynamicsname - dynamicsname = string.replace(dynamicsname, "-", "") + dynamicsname = dynamicsname.replace("-", "") additional_definitions[dynamicsname] = dynamicsname + \ " = #(make-dynamic-script \"" + dynamicstext + "\")" needed_additional_definitions.append(dynamicsname) event = musicexp.DynamicsEvent() - event.type = dynamicsname.encode('utf-8') + event.type = dynamicsname return event # Convert single-color two-byte strings to numbers 0.0 - 1.0 @@ -1257,7 +1256,7 @@ def hexcolorval_to_nr(hex_val): v = int(hex_val, 16) if v == 255: v = 256 - return v / 256. + return v // 256. except ValueError: return 0. @@ -1313,7 +1312,7 @@ def musicxml_words_to_lily_event(words): if hasattr(words, 'default-y') and hasattr(options, 'convert_directions') and options.convert_directions: offset = getattr(words, 'default-y') try: - off = string.atoi(offset) + off = int(offset) if off > 0: event.force_direction = 1 else: @@ -1373,7 +1372,7 @@ def musicxml_accordion_to_markup(mxl_event): # MusicXML spec is quiet about this case... txt = 1 try: - txt = string.atoi(middle.get_text()) + txt = int(middle.get_text()) except ValueError: pass if txt == 3: @@ -1685,8 +1684,8 @@ def musicxml_get_string_tunings(lines): string_tunings = [musicexp.Pitch()] * lines for i in range(0, lines): p = musicexp.Pitch() - p.step = musicxml2ly_conversion.musicxml_step_to_lily(((("E","A","D","G","B")*(lines/5+1))[0:lines])[i]) - p.octave = (([-2+int(x%5>1)+2*(x/5) for x in range(0,lines)][0:lines])[i]) + p.step = musicxml2ly_conversion.musicxml_step_to_lily(((("E","A","D","G","B")*(lines//5+1))[0:lines])[i]) + p.octave = (([-2+int(x%5>1)+2*(x//5) for x in range(0,lines)][0:lines])[i]) p.alteration = 0 p._force_absolute_pitch = True string_tunings[i] = p @@ -1783,7 +1782,7 @@ def musicxml_harmony_to_lily_chordname(n): # require you to know the chord and calculate either the fifth # pitch (for the first inversion) or the third pitch (for the # second inversion) so they may not be helpful for musicxml2ly. - inversion_count = string.atoi(inversion.get_text()) + inversion_count = int(inversion.get_text()) if inversion_count == 1: # TODO: Calculate the bass note for the inversion... pass @@ -1875,7 +1874,7 @@ def musicxml_lyrics_to_text(lyrics, ignoremelismata): elif isinstance(e, musicxml.Text): # We need to convert soft hyphens to -, otherwise the ascii codec as well # as lilypond will barf on that character - text += string.replace(e.get_text(), '\xad', '-') + text += e.get_text().replace('\xad', '-') elif isinstance(e, musicxml.Elision): if text: text += " " @@ -1934,7 +1933,7 @@ class LilyPondVoiceBuilder: r = musicexp.MultiMeasureRest() lenfrac = self.measure_length r.duration = musicxml2ly_conversion.rational_to_lily_duration(lenfrac) - r.duration.factor *= self.pending_multibar / lenfrac + r.duration.factor *= self.pending_multibar // lenfrac self.elements.append(r) self.begin_moment = self.end_moment self.end_moment = self.begin_moment + self.pending_multibar @@ -2131,7 +2130,7 @@ def get_all_lyric_parts_in_voice(voice): lyrics = elem.get_typed_children(musicxml.Lyric) if lyrics: for lyric in lyrics: - index = lyric.number + index = lyric.get_number() if not index in all_lyric_parts: all_lyric_parts.append(index) return all_lyric_parts @@ -2158,17 +2157,17 @@ def extract_lyrics(voice, lyric_key, lyrics_dict): def has_lyric_belonging_to_lyric_part(note, lyric_part_id): lyric_elements = get_lyric_elements(note) - lyric_numbers = [lyric.number for lyric in lyric_elements] + lyric_numbers = [lyric.get_number() for lyric in lyric_elements] return any([lyric_number == lyric_part_id for lyric_number in lyric_numbers]) for idx, elem in enumerate(voice._elements): lyrics = get_lyric_elements(elem) - lyric_keys = [lyric.number for lyric in lyrics] + lyric_keys = [lyric.get_number() for lyric in lyrics] note_has_lyric_belonging_to_lyric_part = lyric_key in lyric_keys # Current note has lyric with 'number' matching 'lyric_key'. if note_has_lyric_belonging_to_lyric_part: for lyric in lyrics: - if lyric.number == lyric_key: + if lyric.get_number() == lyric_key: text = musicxml_lyrics_to_text(lyric, None) result.append(text) # Note has any lyric. @@ -3101,14 +3100,13 @@ def read_musicxml(filename, compressed, use_lxml): sys.stdin = os.fdopen(sys.stdin.fileno(), 'rb', 0) # Make sys.stdin binary bytes_read = sys.stdin.read(8192) while bytes_read: - for b in bytes_read: - tmp.write(b) + tmp.write(bytes_read) bytes_read = sys.stdin.read(8192) z = zipfile.ZipFile(tmp, "r") else: ly.progress(_("Input file %s is compressed, extracting raw MusicXML data") % filename, True) z = zipfile.ZipFile(filename, "r") - container_xml = z.read("META-INF/container.xml") + container_xml = z.read("META-INF/container.xml").decode('utf-8') if not container_xml: return None container = read_xml(io.StringIO(container_xml), use_lxml) @@ -3122,7 +3120,7 @@ def read_musicxml(filename, compressed, use_lxml): if len(rootfile_list) > 0: mxml_file = getattr(rootfile_list[0], 'full-path', None) if mxml_file: - raw_string = z.read(mxml_file) + raw_string = z.read(mxml_file).decode('utf-8') if raw_string: io_object = io.StringIO(raw_string) @@ -3174,9 +3172,9 @@ def convert(filename, options): printer = musicexp.Output_printer() #ly.progress(_("Output to `%s'") % defs_ly_name, True) if (options.output_name == "-"): - printer.set_file(codecs.getwriter("utf-8")(sys.stdout)) + printer.set_file(sys.stdout) else: - printer.set_file(codecs.open(output_ly_name, 'wb', encoding='utf-8')) + printer.set_file(open(output_ly_name, 'w')) print_ly_preamble(printer, filename) print_ly_additional_definitions(printer, filename) if score_information: @@ -3256,7 +3254,7 @@ def main(): conversion_settings.convert_stem_directions = options.convert_stem_directions # Allow the user to leave out the .xml or xml on the filename - basefilename = args[0].decode('utf-8') + basefilename = args[0] if basefilename == "-": # Read from stdin filename = "-" else: -- 2.23.0