# --- T2-COPYRIGHT-BEGIN --- # t2/package/*/gnome-doc-utils/python3.patch # Copyright (C) 2026 The T2 SDE Project # SPDX-License-Identifier: GPL-2.0 or patched project license # --- T2-COPYRIGHT-END --- Port xml2po to Python 3, and let the installed xml2po find its module. diff -ru gnome-doc-utils-0.20.10/xml2po/xml2po/Makefile.am gnome-doc-utils-0.20.10-py3/xml2po/xml2po/Makefile.am --- gnome-doc-utils-0.20.10/xml2po/xml2po/Makefile.am 2012-03-02 20:13:49.000000000 +0100 +++ gnome-doc-utils-0.20.10-py3/xml2po/xml2po/Makefile.am 2026-09-25 19:35:05.808575985 +0200 @@ -8,6 +8,7 @@ nodist_bin_SCRIPTS = xml2po xml2po: xml2po.py.in $(AM_V_GEN)sed -e "s/^VERSION =.*/VERSION = \"@VERSION@\"/" \ -e "s+^#!.*python.*+#!$(PYTHON)+" \ + -e "s+^PYTHONDIR =.*+PYTHONDIR = \"$(pythondir)\"+" \ < $(srcdir)/xml2po.py.in > xml2po $(AM_V_at)chmod +x xml2po diff -ru gnome-doc-utils-0.20.10/xml2po/xml2po/Makefile.in gnome-doc-utils-0.20.10-py3/xml2po/xml2po/Makefile.in --- gnome-doc-utils-0.20.10/xml2po/xml2po/Makefile.in 2012-03-26 20:23:19.000000000 +0200 +++ gnome-doc-utils-0.20.10-py3/xml2po/xml2po/Makefile.in 2026-09-25 19:35:05.808618177 +0200 @@ -687,6 +687,7 @@ uninstall-am: uninstall-nodist_binSCRIPT xml2po: xml2po.py.in $(AM_V_GEN)sed -e "s/^VERSION =.*/VERSION = \"@VERSION@\"/" \ -e "s+^#!.*python.*+#!$(PYTHON)+" \ + -e "s+^PYTHONDIR =.*+PYTHONDIR = \"$(pythondir)\"+" \ < $(srcdir)/xml2po.py.in > xml2po $(AM_V_at)chmod +x xml2po diff -ru gnome-doc-utils-0.20.10/xml2po/xml2po/__init__.py gnome-doc-utils-0.20.10-py3/xml2po/xml2po/__init__.py --- gnome-doc-utils-0.20.10/xml2po/xml2po/__init__.py 2011-08-04 16:36:03.000000000 +0200 +++ gnome-doc-utils-0.20.10-py3/xml2po/xml2po/__init__.py 2026-09-25 19:31:39.783968409 +0200 @@ -158,7 +158,7 @@ def generate_messages(self): self.doSerialize(self.doc) def normalizeNode(self, node): - #print >>sys.stderr, "<%s> (%s) [%s]" % (node.name, node.type, node.serialize('utf-8')) + #print("<%s> (%s) [%s]" % (node.name, node.type, node.serialize('utf-8')), file=sys.stderr) if not node: return elif self.app.isSpacePreserveNode(node): @@ -167,10 +167,10 @@ def normalizeNode(self, node): if node.isBlankNode(): if self.app.options.get('expand_entities') or \ (not (node.prev and not node.prev.isBlankNode() and node.next and not node.next.isBlankNode()) ): - #print >>sys.stderr, "BLANK" + #print("BLANK", file=sys.stderr) node.setContent('') else: - node.setContent(re.sub('\s+',' ', node.content)) + node.setContent(re.sub(r'\s+',' ', node.content)) elif node.children and node.type == 'element': child = node.children @@ -200,7 +200,7 @@ def normalizeString(self, text, spacepre tree = ctxt.doc() newnode = tree.getRootElement() except: - print >> sys.stderr, """Error while normalizing string as XML:\n"%s"\n""" % (text) + print("""Error while normalizing string as XML:\n"%s"\n""" % (text), file=sys.stderr) return text self.normalizeNode(newnode) @@ -259,7 +259,7 @@ def myAttributeSerialize(self, node): if not self.expand_entities: result += '&' + child.name + ';' else: - result += child.content.decode('utf-8') + result += child.content else: result += self.myAttributeSerialize(child) child = child.next @@ -326,7 +326,7 @@ def replaceNodeContentsWithText(self, no pass content = '<%s>%s' % (starttag, text, endtag) - tmp = tmp + content.encode('utf-8') + tmp = tmp + content newnode = None try: @@ -338,7 +338,7 @@ def replaceNodeContentsWithText(self, no pass if not newnode: - print >> sys.stderr, """Error while parsing translation as XML:\n"%s"\n""" % (text.encode('utf-8')) + print("""Error while parsing translation as XML:\n"%s"\n""" % (text), file=sys.stderr) return newelem = newnode.getRootElement() @@ -351,10 +351,7 @@ def replaceNodeContentsWithText(self, no free = next if node: - copy = newelem.copyNodeList() - next = node.next node.replaceNode(newelem.copyNodeList()) - node.next = next else: # In practice, this happens with tags such as " " (only whitespace in between) @@ -422,7 +419,7 @@ def processAttribute(self, node, attr): outtxt = self.normalizeString(attr.content) if self.app.operation == 'merge': translation = self.app.getTranslation(outtxt) - self.replaceAttributeContentsWithText(attr, translation.encode('utf-8')) + self.replaceAttributeContentsWithText(attr, translation) else: self.app.msg.outputMessage(outtxt, node.lineNo(), "", spacepreserve=False, tag = node.name + ":" + attr.name) @@ -463,14 +460,14 @@ def processElementTag(self, node, replac norm_outtxt = self.normalizeString(outtxt, self.app.isSpacePreserveNode(node)) translation = self.app.getTranslation(norm_outtxt) else: - translation = outtxt.decode('utf-8') + translation = outtxt starttag = self.startTagForNode(node) endtag = self.endTagForNode(node) worth = self.worthOutputting(node) if not translation: - translation = outtxt.decode('utf-8') + translation = outtxt if worth and self.app.options.get('mark_untranslated'): node.setLang('C') @@ -492,7 +489,7 @@ def processElementTag(self, node, replac def isExternalGeneralParsedEntity(self, node): try: # it would be nice if debugDumpNode could use StringIO, but it apparently cannot - tmp = tempfile.TemporaryFile() + tmp = tempfile.TemporaryFile(mode='w+') node.debugDumpNode(tmp,0) tmp.seek(0) tmpstr = tmp.read() @@ -551,11 +548,11 @@ def __init__(self, mode, operation, outp self.current_mode = self.load_mode(mode)() # Prepare output if operation == 'update': - self.out = tempfile.TemporaryFile() + self.out = tempfile.TemporaryFile(mode='w+', encoding='utf-8') elif output == '-': self.out = sys.stdout else: - self.out = file(output, 'w') + self.out = open(output, 'w', encoding='utf-8') def load_mode(self, modename): try: @@ -577,8 +574,8 @@ def to_pot(self, xmlfiles): raise IOError("Unable to read file '%s'" % xmlfile) try: doc = XMLDocument(xmlfile, self) - except Exception, e: - print >> sys.stderr, "Unable to parse XML file '%s': %s" % (xmlfile, str(e)) + except Exception as e: + print("Unable to parse XML file '%s': %s" % (xmlfile, str(e)), file=sys.stderr) sys.exit(1) self.current_mode.preProcessXml(doc.doc, self.msg) doc.generate_messages() @@ -590,14 +587,14 @@ def merge(self, mofile, xmlfile): raise IOError("Unable to read file '%s'" % xmlfile) try: doc = XMLDocument(xmlfile, self) - except Exception, e: - print >> sys.stderr, str(e) + except Exception as e: + print(str(e), file=sys.stderr) sys.exit(1) try: mfile = open(mofile, "rb") except: - print >> sys.stderr, "Can't open MO file '%s'." % (mofile) + print("Can't open MO file '%s'." % (mofile), file=sys.stderr) self.gt = gettext.GNUTranslations(mfile) self.gt.add_fallback(NoneTranslations()) # Has preProcessXml use cases for merge? @@ -608,6 +605,7 @@ def merge(self, mofile, xmlfile): outtxt = self.getTranslation(tcmsg) self.current_mode.postProcessXmlTranslation(doc.doc, self.options.get('translationlanguage'), outtxt) self.out.write(doc.doc.serialize('utf-8', 1)) + self.out.flush() def reuse(self, origxml, xmlfile): """ Produce a po file from xmlfile pot and using translations from origxml """ @@ -619,16 +617,16 @@ def reuse(self, origxml, xmlfile): raise IOError("Unable to read file '%s'" % xmlfile) try: doc = XMLDocument(xmlfile, self) - except Exception, e: - print >> sys.stderr, str(e) + except Exception as e: + print(str(e), file=sys.stderr) sys.exit(1) doc.generate_messages() self.msg.translationsFollow() try: doc = XMLDocument(origxml, self) - except Exception, e: - print >> sys.stderr, str(e) + except Exception as e: + print(str(e), file=sys.stderr) sys.exit(1) doc.generate_messages() self.output_po() @@ -659,11 +657,11 @@ def getTranslation(self, text): text should be a string to look for. """ - #print >>sys.stderr,"getTranslation('%s')" % (text.encode('utf-8')) + #print("getTranslation('%s')" % (text), file=sys.stderr) if not text or text.strip() == '': return text if self.gt: - res = self.gt.ugettext(text.decode('utf-8')) + res = self.gt.gettext(text) return res return text @@ -676,6 +674,7 @@ def output_po(self): self.msg.outputMessage(tcmsg, lineno=0, comment=tccom) self.msg.outputAll(self.out) + self.out.flush() # **** XML utility functions **** def isSpacePreserveNode(self, node): diff -ru gnome-doc-utils-0.20.10/xml2po/xml2po/modes/docbook.py gnome-doc-utils-0.20.10-py3/xml2po/xml2po/modes/docbook.py --- gnome-doc-utils-0.20.10/xml2po/xml2po/modes/docbook.py 2011-01-10 17:08:10.000000000 +0100 +++ gnome-doc-utils-0.20.10-py3/xml2po/xml2po/modes/docbook.py 2026-09-25 19:30:49.232667745 +0200 @@ -43,7 +43,7 @@ try: except ImportError: from md5 import new as md5_new -from basic import basicXmlMode +from .basic import basicXmlMode class docbookXmlMode(basicXmlMode): """Class for special handling of DocBook document types. @@ -131,7 +131,7 @@ def _output_images(self, node, msg): hash = self._md5_for_file(fullpath) else: hash = "THIS FILE DOESN'T EXIST" - print >>sys.stderr, "Warning: image file '%s' not found." % fullpath + print("Warning: image file '%s' not found." % fullpath, file=sys.stderr) msg.outputMessage("@@image: '%s'; md5=%s" % (attr, hash), node.lineNo(), "When image changes, this message will be marked fuzzy or untranslated for you.\n"+ @@ -184,7 +184,7 @@ def postProcessXmlTranslation(self, doc, else: ai.addChild(copy) if match.group(3): - copy.newChild(None, "year", match.group(3).encode('utf-8')) + copy.newChild(None, "year", match.group(3)) if match.group(1) and match.group(2): holder = match.group(1)+"(%s)" % match.group(2) elif match.group(1): @@ -193,15 +193,15 @@ def postProcessXmlTranslation(self, doc, holder = match.group(2) else: holder = "???" - copy.newChild(None, "holder", holder.encode('utf-8')) + copy.newChild(None, "holder", holder) # Perform some tests when ran standalone if __name__ == '__main__': test = docbookXmlMode() - print "Ignored tags : " + repr(test.getIgnoredTags()) - print "Final tags : " + repr(test.getFinalTags()) - print "Space-preserve tags: " + repr(test.getSpacePreserveTags()) + print("Ignored tags : " + repr(test.getIgnoredTags())) + print("Final tags : " + repr(test.getFinalTags())) + print("Space-preserve tags: " + repr(test.getSpacePreserveTags())) - print "Credits from string: '%s'" % test.getStringForTranslators() - print "Explanation for credits:\n\t'%s'" % test.getCommentForTranslators() + print("Credits from string: '%s'" % test.getStringForTranslators()) + print("Explanation for credits:\n\t'%s'" % test.getCommentForTranslators()) diff -ru gnome-doc-utils-0.20.10/xml2po/xml2po/modes/gs.py gnome-doc-utils-0.20.10-py3/xml2po/xml2po/modes/gs.py --- gnome-doc-utils-0.20.10/xml2po/xml2po/modes/gs.py 2010-12-13 17:14:07.000000000 +0100 +++ gnome-doc-utils-0.20.10-py3/xml2po/xml2po/modes/gs.py 2026-09-25 19:30:58.413647329 +0200 @@ -20,7 +20,7 @@ # Special case Gnome Summary # -from basic import basicXmlMode +from .basic import basicXmlMode class gsXmlMode(basicXmlMode): """Abstract class for special handling of document types.""" diff -ru gnome-doc-utils-0.20.10/xml2po/xml2po/modes/mallard.py gnome-doc-utils-0.20.10-py3/xml2po/xml2po/modes/mallard.py --- gnome-doc-utils-0.20.10/xml2po/xml2po/modes/mallard.py 2011-01-10 17:08:50.000000000 +0100 +++ gnome-doc-utils-0.20.10-py3/xml2po/xml2po/modes/mallard.py 2026-09-25 19:30:49.232728437 +0200 @@ -39,7 +39,7 @@ try: except ImportError: from md5 import new as md5_new -from basic import basicXmlMode +from .basic import basicXmlMode class mallardXmlMode(basicXmlMode): """Class for special handling of Mallard document types.""" @@ -112,7 +112,7 @@ def _output_images(self, node, msg): hash = self._md5_for_file(fullpath) else: hash = "THIS FILE DOESN'T EXIST" - print >>sys.stderr, "Warning: image file '%s' not found." % fullpath + print("Warning: image file '%s' not found." % fullpath, file=sys.stderr) msg.outputMessage("@@image: '%s'; md5=%s" % (attr, hash), node.lineNo(), "When image changes, this message will be marked fuzzy or untranslated for you.\n"+ diff -ru gnome-doc-utils-0.20.10/xml2po/xml2po/modes/ubuntu.py gnome-doc-utils-0.20.10-py3/xml2po/xml2po/modes/ubuntu.py --- gnome-doc-utils-0.20.10/xml2po/xml2po/modes/ubuntu.py 2010-12-13 17:14:07.000000000 +0100 +++ gnome-doc-utils-0.20.10-py3/xml2po/xml2po/modes/ubuntu.py 2026-09-25 19:30:49.232777560 +0200 @@ -2,7 +2,7 @@ import libxml2 -from docbook import docbookXmlMode +from .docbook import docbookXmlMode class ubuntuXmlMode (docbookXmlMode): """Special-casing Ubuntu DocBook website documentation.""" diff -ru gnome-doc-utils-0.20.10/xml2po/xml2po/modes/xhtml.py gnome-doc-utils-0.20.10-py3/xml2po/xml2po/modes/xhtml.py --- gnome-doc-utils-0.20.10/xml2po/xml2po/modes/xhtml.py 2010-12-13 17:14:07.000000000 +0100 +++ gnome-doc-utils-0.20.10-py3/xml2po/xml2po/modes/xhtml.py 2026-09-25 19:30:58.413672149 +0200 @@ -21,7 +21,7 @@ # This implements special instructions for handling XHTML documents # in a better way, particularly to extract some attributes in HTML tags -from basic import basicXmlMode +from .basic import basicXmlMode class xhtmlXmlMode(basicXmlMode): """Class for special handling of XHTML document types.""" diff -ru gnome-doc-utils-0.20.10/xml2po/xml2po/xml2po.py.in gnome-doc-utils-0.20.10-py3/xml2po/xml2po/xml2po.py.in --- gnome-doc-utils-0.20.10/xml2po/xml2po/xml2po.py.in 2010-12-13 17:14:07.000000000 +0100 +++ gnome-doc-utils-0.20.10-py3/xml2po/xml2po/xml2po.py.in 2026-09-25 19:35:05.808435448 +0200 @@ -22,6 +22,7 @@ # xml2po -- translate XML documents VERSION = "1.0.5" +PYTHONDIR = "" # Versioning system (I use this for a long time, so lets explain it to # those Linux-versioning-scheme addicts): @@ -41,9 +42,9 @@ import tempfile if not os.path.exists('/dev/null'): NULL_STRING = 'NUL' def usage (with_help = False): - print >> sys.stderr, "Usage: %s [OPTIONS] [XMLFILE]..." % (sys.argv[0]) + print("Usage: %s [OPTIONS] [XMLFILE]..." % (sys.argv[0]), file=sys.stderr) if with_help: - print >> sys.stderr, """ + print(""" OPTIONS may be some of: -a --automatic-tags Automatically decides if tags are to be considered "final" or not @@ -72,7 +73,7 @@ def usage (with_help = False): using -p option for each XML file: %(command)s -p de.po chapter1.xml > chapter1.de.xml %(command)s -p de.po chapter2.xml > chapter2.de.xml -""" % {'command': sys.argv[0]} +""" % {'command': sys.argv[0]}, file=sys.stderr) def main(argv): @@ -82,8 +83,9 @@ def main(argv): name = os.path.join(os.path.dirname(__file__), '..') if os.path.exists(os.path.join(name, 'tests')): - print >> sys.stderr, 'Running from source folder, modifying PYTHONPATH' + print('Running from source folder, modifying PYTHONPATH', file=sys.stderr) sys.path.insert(0, name) + sys.path.append(PYTHONDIR) from xml2po import Main @@ -142,14 +144,14 @@ def main(argv): elif opt in ('-o', '--output'): output = arg elif opt in ('-v', '--version'): - print VERSION + print(VERSION) sys.exit(0) elif opt in ('-h', '--help'): usage(True) sys.exit(0) if operation == 'update' and output != "-": - print >> sys.stderr, "Option '-o' is not yet supported when updating translations directly. Ignoring this option." + print("Option '-o' is not yet supported when updating translations directly. Ignoring this option.", file=sys.stderr) # Treat remaining arguments as XML files filenames = [] @@ -159,16 +161,16 @@ def main(argv): try: xml2po_main = Main(default_mode, operation, output, options) except IOError: - print >> sys.stderr, "Error: cannot open file %s for writing." % (output) + print("Error: cannot open file %s for writing." % (output), file=sys.stderr) sys.exit(5) if operation == 'merge': if len(filenames) > 1: - print >> sys.stderr, "Error: You can merge translations with only one XML file at a time." + print("Error: You can merge translations with only one XML file at a time.", file=sys.stderr) sys.exit(2) if not mofile: - print >> sys.stderr, "Error: You must specify MO file when merging translations." + print("Error: You must specify MO file when merging translations.", file=sys.stderr) sys.exit(3) xml2po_main.merge(mofile, filenames[0])