Am Samstag, den 21.09.2019, 12:09 +1000 schrieb Andrew Bernard:
> So let me get this straight.
> 
> It's not the case that GUB is completely broken. We can still build 
> releases.
> 
> DK is working steadily to cherry pick items for 2.20.
> 
> Python 2 to Python 3 is a major issue.
> 
> So, I offered to do the 2->3 port a long time ago but circumstances 
> prevented me from doing so. Would it be constructive if I launched into 
> that aspect? I cant understand the lilypond internals code but I have 
> extensive Python experience.
> 
> Would this be helpful to moving forward?
> 
> But have people already started on this, I thought?

I've also started looking into this and used the branch
dev/knupero/lilypy3devel as a starting point (see also 
https://lists.gnu.org/archive/html/bug-lilypond/2019-08/msg00014.html).

On top of that I've worked on the attached patches which brings the
make targets check / test and doc back to life with Python 3.7.4. Maybe
they can be added to the branch mentioned above to serve as a single
source of truth? I know the third patch is pretty large, let me know if
I should try to split it up...

As I'm pretty new to the development of Lilypond, I'm not really sure
if there's something else to do in the source code repository itself?
I'm pretty sure I didn't get to run through all error branches in all
scripts, but the targets mentioned in the documentation work for me
right now. If not, sounds like working on GUB would be next?

Regards,
Jonas
From f6595ce68dc47e4e60f94dd8b80dbd5573e4c360 Mon Sep 17 00:00:00 2001
From: Jonas Hahnfeld <hah...@hahnjo.de>
Date: Thu, 19 Sep 2019 20:26:25 +0200
Subject: [PATCH 1/5] lilylib: sys.stderr.write expects strings

... not encoded bytes. This fixes the first error when running the
check / test target.
---
 python/lilylib.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/python/lilylib.py b/python/lilylib.py
index 3850f4e2a3..ef156115a5 100644
--- a/python/lilylib.py
+++ b/python/lilylib.py
@@ -127,7 +127,7 @@ def is_verbose ():
     return is_loglevel ("DEBUG")
 
 def stderr_write (s):
-    encoded_write (sys.stderr, s)
+    sys.stderr.write (s)
 
 def print_logmessage (level, s, fullmessage = True, newline = True):
     if (is_loglevel (level)):
-- 
2.23.0

From 5630b85ec8d9a7344009a221916ba87904287848 Mon Sep 17 00:00:00 2001
From: Jonas Hahnfeld <hah...@hahnjo.de>
Date: Thu, 19 Sep 2019 19:56:24 +0200
Subject: [PATCH 2/5] Read linking files in binary mode

This fixes the check / test target up to musicxml.
---
 python/book_snippets.py | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/python/book_snippets.py b/python/book_snippets.py
index b84c269e6a..0a2f3fbb31 100644
--- a/python/book_snippets.py
+++ b/python/book_snippets.py
@@ -645,12 +645,11 @@ printing diff against existing file." % filename)
             try:
                 if (self.global_options.use_source_file_names
                         and isinstance (self, LilypondFileSnippet)):
-                    fout = open (dst, 'w')
-                    fin = open (src, 'r')
-                    for line in fin.readlines ():
-                        fout.write (line.replace (self.basename (), self.final_basename ()))
-                    fout.close ()
-                    fin.close ()
+                    content = open (src, 'rb').read ()
+                    basename = self.basename ().encode ('utf-8')
+                    final_basename = self.final_basename ().encode ('utf-8')
+                    content = content.replace (basename, final_basename)
+                    open (dst, 'wb').write (content)
                 else:
                     try:
                         os.link (src, dst)
-- 
2.23.0

From 99a7c48d8c331f5a0a61884905c140ec654b3b8f Mon Sep 17 00:00:00 2001
From: Jonas Hahnfeld <hah...@hahnjo.de>
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

From b7c6544fa97455fa9ad0f16baa739181d7f7d1f8 Mon Sep 17 00:00:00 2001
From: Jonas Hahnfeld <hah...@hahnjo.de>
Date: Thu, 19 Sep 2019 21:22:24 +0200
Subject: [PATCH 4/5] abc2ly: Fix division for Python 3

---
 scripts/abc2ly.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/scripts/abc2ly.py b/scripts/abc2ly.py
index e89b607db4..49bf5ec98c 100644
--- a/scripts/abc2ly.py
+++ b/scripts/abc2ly.py
@@ -373,7 +373,7 @@ def semitone_pitch  (tup):
     p =0
 
     t = tup[0]
-    p = p + 12 * (t / 7)
+    p = p + 12 * (t // 7)
     t = t % 7
 
     if t > 2:
@@ -471,10 +471,10 @@ def shift_key (note, acc, shift):
     s = semitone_pitch((note, acc))
     s = (s + shift + 12) % 12
     if s <= 4:
-        n = s / 2
+        n = s // 2
         a = s % 2
     else:
-        n = (s + 1) / 2
+        n = (s + 1) // 2
         a = (s + 1) % 2
     if a:
         n = n + 1
-- 
2.23.0

From fe957b247c6d60485ea02f6bc3daaf71723382e4 Mon Sep 17 00:00:00 2001
From: Jonas Hahnfeld <hah...@hahnjo.de>
Date: Fri, 20 Sep 2019 10:20:26 +0200
Subject: [PATCH 5/5] Fix the last issues with lilypond-book and Python 3

This finally makes the targets check / test and doc pass!
---
 python/book_docbook.py | 2 +-
 python/book_html.py    | 2 +-
 python/book_latex.py   | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/python/book_docbook.py b/python/book_docbook.py
index ef009935d6..ec5cbc037c 100644
--- a/python/book_docbook.py
+++ b/python/book_docbook.py
@@ -104,7 +104,7 @@ class BookDocbookOutputFormat (BookBase.BookOutputFormat):
         self.snippet_res = Docbook_snippet_res
         self.output = Docbook_output
         self.handled_extensions = ['.lyxml']
-        self.snippet_option_separator = '\s*'
+        self.snippet_option_separator = '\s+'
 
     def adjust_snippet_command (self, cmd):
         if '--formats' not in cmd:
diff --git a/python/book_html.py b/python/book_html.py
index ac70b743a0..cb9c585378 100644
--- a/python/book_html.py
+++ b/python/book_html.py
@@ -104,7 +104,7 @@ class BookHTMLOutputFormat (BookBase.BookOutputFormat):
         self.snippet_res = HTML_snippet_res
         self.output = HTML_output
         self.handled_extensions = ['.html', '.xml','.htmly']
-        self.snippet_option_separator = '\s*'
+        self.snippet_option_separator = '\s+'
 
     def split_snippet_options (self, option_string):
         if option_string:
diff --git a/python/book_latex.py b/python/book_latex.py
index ec5412b1d8..595095c9e3 100644
--- a/python/book_latex.py
+++ b/python/book_latex.py
@@ -234,7 +234,7 @@ def get_latex_textwidth (source, global_options):
     if os.path.exists (auxfile):
         os.unlink (auxfile)
     if os.path.exists (logfile):
-        parameter_string = file (logfile).read()
+        parameter_string = open (logfile).read()
         os.unlink (logfile)
 
     columns = 0
-- 
2.23.0

Attachment: signature.asc
Description: This is a digitally signed message part

_______________________________________________
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel

Reply via email to