From 2a3bee175ee635cf8d5fc30e0dc8174fd6f6accd Mon Sep 17 00:00:00 2001 From: Louis Date: Sat, 27 Sep 2014 09:43:29 +0200 Subject: [PATCH] Converted to Python3 Not fully tested: base case seems to work --- patacrep/build.py | 4 ++-- patacrep/content/__init__.py | 6 +++--- patacrep/content/cwd.py | 2 +- patacrep/content/section.py | 6 +++--- patacrep/content/song.py | 8 ++++---- patacrep/content/songsection.py | 4 ++-- patacrep/content/sorted.py | 10 +++++----- patacrep/content/tex.py | 4 ++-- patacrep/encoding.py | 29 +---------------------------- patacrep/errors.py | 5 +---- patacrep/files.py | 2 +- patacrep/index.py | 33 +++++++++++++++++---------------- patacrep/latex/__init__.py | 2 +- patacrep/songs.py | 19 ++++--------------- patacrep/templates.py | 16 ++++++++-------- readme.md | 10 +++++----- setup.py | 2 +- songbook | 6 +++--- stdeb.cfg | 4 ++-- 19 files changed, 66 insertions(+), 106 deletions(-) diff --git a/patacrep/build.py b/patacrep/build.py index c3a414d4..9f79bb6a 100644 --- a/patacrep/build.py +++ b/patacrep/build.py @@ -57,7 +57,7 @@ class Songbook(object): def _set_datadir(self): """Set the default values for datadir""" try: - if isinstance(self.config['datadir'], basestring): + if isinstance(self.config['datadir'], str): self.config['datadir'] = [self.config['datadir']] except KeyError: # No datadir in the raw_songbook self.config['datadir'] = [os.path.abspath('.')] @@ -213,7 +213,7 @@ class SongbookBuilder(object): log = '' line = process.stdout.readline() while line: - log += line + log += str(line) line = process.stdout.readline() LOGGER.debug(log) diff --git a/patacrep/content/__init__.py b/patacrep/content/__init__.py index 2f4fea0e..a53d4d7e 100755 --- a/patacrep/content/__init__.py +++ b/patacrep/content/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Content plugin management. @@ -225,11 +225,11 @@ def process_content(content, config=None): """ contentlist = [] plugins = load_plugins(config) - keyword_re = re.compile(ur'^ *(?P\w*) *(\((?P.*)\))? *$') + keyword_re = re.compile(r'^ *(?P\w*) *(\((?P.*)\))? *$') if not content: content = [["song"]] for elem in content: - if isinstance(elem, basestring): + if isinstance(elem, str): elem = ["song", elem] if len(content) == 0: content = ["song"] diff --git a/patacrep/content/cwd.py b/patacrep/content/cwd.py index 338adb76..5e55d68d 100755 --- a/patacrep/content/cwd.py +++ b/patacrep/content/cwd.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Change base directory before importing songs.""" diff --git a/patacrep/content/section.py b/patacrep/content/section.py index 2bde2eb2..96215f68 100755 --- a/patacrep/content/section.py +++ b/patacrep/content/section.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Allow LaTeX sections (starred or not) as content of a songbook.""" @@ -26,9 +26,9 @@ class Section(Content): def render(self, __context): if self.short is None: - return ur'\{}{{{}}}'.format(self.keyword, self.name) + return r'\{}{{{}}}'.format(self.keyword, self.name) else: - return ur'\{}[{}]{{{}}}'.format(self.keyword, self.short, self.name) + return r'\{}[{}]{{{}}}'.format(self.keyword, self.short, self.name) #pylint: disable=unused-argument def parse(keyword, argument, contentlist, config): diff --git a/patacrep/content/song.py b/patacrep/content/song.py index b83fd134..02acf463 100755 --- a/patacrep/content/song.py +++ b/patacrep/content/song.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Plugin to include songs to the songbook.""" @@ -26,15 +26,15 @@ class SongRenderer(Content, Song): indexes = context.resolve("indexes") if isinstance(indexes, jinja2.runtime.Undefined): indexes = "" - return ur'\begin{songs}{%s}' % indexes + return r'\begin{songs}{%s}' % indexes def end_block(self, __context): """Return the string to end a block.""" - return ur'\end{songs}' + return r'\end{songs}' def render(self, context): """Return the string that will render the song.""" - return ur'\input{{{}}}'.format(files.path2posix( + return r'\input{{{}}}'.format(files.path2posix( files.relpath( self.fullpath, os.path.dirname(context['filename']) diff --git a/patacrep/content/songsection.py b/patacrep/content/songsection.py index b4c9d446..07153591 100755 --- a/patacrep/content/songsection.py +++ b/patacrep/content/songsection.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Allow 'songchapter' and 'songsection' as content of a songbook.""" @@ -19,7 +19,7 @@ class SongSection(Content): def render(self, __context): """Render this section or chapter.""" - return ur'\{}{{{}}}'.format(self.keyword, self.name) + return r'\{}{{{}}}'.format(self.keyword, self.name) #pylint: disable=unused-argument def parse(keyword, argument, contentlist, config): diff --git a/patacrep/content/sorted.py b/patacrep/content/sorted.py index e9a5e677..651bd07e 100755 --- a/patacrep/content/sorted.py +++ b/patacrep/content/sorted.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Sorted list of songs. @@ -9,9 +9,9 @@ to a songbook. import locale import logging +import unidecode from patacrep import files -from patacrep import encoding from patacrep.content import ContentError from patacrep.content.song import OnlySongsError, process_songs @@ -27,11 +27,11 @@ def normalize_string(string): - lower case; - passed through locale.strxfrm(). """ - return locale.strxfrm(encoding.unidecode(string.lower().strip())) + return locale.strxfrm(unidecode.unidecode(string.lower().strip())) def normalize_field(field): """Return a normalized field, it being a string or a list of strings.""" - if isinstance(field, basestring): + if isinstance(field, str): return normalize_string(field) elif isinstance(field, list) or isinstance(field, tuple): return [normalize_field(string) for string in field] @@ -63,7 +63,7 @@ def key_generator(sort): files.relpath(song.fullpath), ) ) - field = u"" + field = "" songkey.append(normalize_field(field)) return songkey return ordered_song_keys diff --git a/patacrep/content/tex.py b/patacrep/content/tex.py index 5f80fcfc..38593f38 100755 --- a/patacrep/content/tex.py +++ b/patacrep/content/tex.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Include LaTeX raw code in the songbook.""" @@ -18,7 +18,7 @@ class LaTeX(Content): self.filename = filename def render(self, context): - return ur'\input{{{}}}'.format(files.relpath( + return r'\input{{{}}}'.format(files.relpath( self.filename, os.path.dirname(context['filename']), )) diff --git a/patacrep/encoding.py b/patacrep/encoding.py index 8ba7de61..ca917295 100644 --- a/patacrep/encoding.py +++ b/patacrep/encoding.py @@ -5,7 +5,6 @@ import codecs import chardet import logging -from unidecode import unidecode as unidecode_orig LOGGER = logging.getLogger(__name__) @@ -17,32 +16,6 @@ def open_read(filename, mode='r'): return codecs.open( filename, mode=mode, - encoding=chardet.detect(open(filename, "r").read())['encoding'], + encoding=chardet.detect(open(filename, 'rb').read())['encoding'], errors='replace', ) - -def basestring2unicode(arg): - """Return the unicode version of the argument, guessing original encoding. - """ - if isinstance(arg, unicode): - return arg - elif isinstance(arg, basestring): - return arg.decode( - encoding=chardet.detect(arg)['encoding'], - errors='replace', - ) - else: - LOGGER.warning("Cannot decode string {}. Ignored.".format(str(arg))) - return "" - -def list2unicode(arg): - """Return the unicode version of the argument, guessing original encoding. - - Argument is a list of strings. If an item is of another type, it is - silently ignored (an empty string is returned). - """ - return [basestring2unicode(item) for item in arg] - -def unidecode(arg): - """Return a unicode version of a unidecoded string.""" - return unicode(unidecode_orig(arg)) diff --git a/patacrep/errors.py b/patacrep/errors.py index ff3d210d..a065c322 100644 --- a/patacrep/errors.py +++ b/patacrep/errors.py @@ -17,10 +17,7 @@ class SBFileError(SongbookError): self.message = message def __str__(self): - if self.message is None: - return str(self.original) - else: - return self.message + return self.message class TemplateError(SongbookError): """Error during template generation""" diff --git a/patacrep/files.py b/patacrep/files.py index 48928a00..6e02d481 100644 --- a/patacrep/files.py +++ b/patacrep/files.py @@ -16,7 +16,7 @@ def recursive_find(root_directory, pattern): matches = [] with chdir(root_directory): - for root, _, filenames in os.walk(os.curdir): + for root, __ignored, filenames in os.walk(os.curdir): for filename in fnmatch.filter(filenames, pattern): matches.append(os.path.join(root, filename)) return matches diff --git a/patacrep/index.py b/patacrep/index.py index c1b74806..ac927569 100644 --- a/patacrep/index.py +++ b/patacrep/index.py @@ -8,17 +8,18 @@ from a file generated by the latex compilation of the songbook (.sxd). """ import locale +import unidecode import re from patacrep import authors from patacrep import encoding from patacrep.latex import latex2unicode -EOL = u"\n" +EOL = "\n" # Pattern set to ignore latex command in title prefix -KEYWORD_PATTERN = re.compile(ur"^%(\w+)\s?(.*)$", re.LOCALE) -FIRST_LETTER_PATTERN = re.compile(ur"^(?:\{?\\\w+\}?)*[^\w]*(\w)", re.LOCALE) +KEYWORD_PATTERN = re.compile(r"^%(\w+)\s?(.*)$", re.LOCALE) +FIRST_LETTER_PATTERN = re.compile(r"^(?:\{?\\\w+\}?)*[^\w]*(\w)", re.LOCALE) def process_sxd(filename): @@ -77,13 +78,13 @@ class Index(object): except AttributeError: # classify as number all the non letter characters letter = "0" - if re.match(ur'\d', letter): + if re.match(r'\d', letter): letter = '0-9' return letter.upper() def add_keyword(self, key, word): """Add 'word' to self.keywords[key].""" - if not key in self.keywords.keys(): + if not key in self.keywords: self.keywords[key] = [] self.keywords[key].append(word) @@ -93,7 +94,7 @@ class Index(object): if 'prefix' in self.keywords: for prefix in self.keywords['prefix']: self.prefix_patterns.append(re.compile( - ur"^({prefix})(\b|\\)(\s*.*)$".format(prefix=prefix), + r"^({prefix})(\b|\\)(\s*.*)$".format(prefix=prefix), re.LOCALE )) @@ -107,12 +108,12 @@ class Index(object): similar method with processing. """ first = self.get_first_letter(key[0]) - if not first in self.data.keys(): + if not first in self.data: self.data[first] = dict() - if not key in self.data[first].keys(): + if not key in self.data[first]: self.data[first][key] = { 'sortingkey': [ - encoding.unidecode(latex2unicode(item)).lower() + unidecode.unidecode(latex2unicode(item)).lower() for item in key ], 'entries': [], @@ -150,26 +151,26 @@ class Index(object): @staticmethod def ref_to_str(ref): """Return the LaTeX code corresponding to the reference.""" - return ur'\hyperlink{{{0[link]}}}{{{0[num]}}}'.format(ref) + return r'\hyperlink{{{0[link]}}}{{{0[num]}}}'.format(ref) def key_to_str(self, key): """Convert the key (title or author) to the LaTeX command rendering it. """ if self.indextype == "AUTHOR": - return ur"\indexauthor{{{first}}}{{{last}}}".format( + return r"\indexauthor{{{first}}}{{{last}}}".format( first=key[1], last=key[0], ) if self.indextype == "TITLE": - return ur"\indextitle{{{0[1]}}}{{{0[0]}}}".format(key) + return r"\indextitle{{{0[1]}}}{{{0[0]}}}".format(key) def entry_to_str(self, key, entry): """Return the LaTeX code corresponding to the entry.""" - return unicode(ur'\idxentry{{{0}}}{{{1}}}' + EOL).format( + return (r'\idxentry{{{0}}}{{{1}}}' + EOL).format( self.key_to_str(key), - ur'\\'.join([self.ref_to_str(ref) for ref in entry]), + r'\\'.join([self.ref_to_str(ref) for ref in entry]), ) def idxblock_to_str(self, letter, entries): @@ -185,10 +186,10 @@ class Index(object): for item in entries[key]['sortingkey'] ] - string = ur'\begin{idxblock}{' + letter + '}' + EOL + string = r'\begin{idxblock}{' + letter + '}' + EOL for key in sorted(entries, key=sortkey): string += self.entry_to_str(key, entries[key]['entries']) - string += ur'\end{idxblock}' + EOL + string += r'\end{idxblock}' + EOL return string def entries_to_str(self): diff --git a/patacrep/latex/__init__.py b/patacrep/latex/__init__.py index f284c094..1de7d1b9 100644 --- a/patacrep/latex/__init__.py +++ b/patacrep/latex/__init__.py @@ -4,7 +4,7 @@ def latex2unicode(string): """Convert LaTeX string to unicode""" - return u"TODO" + return "TODO" def parsetex(path): """Return a dictonary of data read from the latex file `path`. diff --git a/patacrep/songs.py b/patacrep/songs.py index 1cb69e8c..bf3ff3a6 100644 --- a/patacrep/songs.py +++ b/patacrep/songs.py @@ -6,13 +6,9 @@ import errno import hashlib import logging import os +import pickle import re -try: - import cPickle as pickle -except ImportError: - import pickle - from patacrep.authors import processauthors from patacrep.latex import parsetex @@ -128,7 +124,7 @@ class Song(object): self.args = data['args'] self.subpath = subpath self.languages = data['languages'] - if "by" in self.args.keys(): + if "by" in self.args: self.authors = processauthors( self.args["by"], **config["_compiled_authwords"] @@ -144,14 +140,7 @@ class Song(object): if self.datadir: cached = {} for attribute in self.cached_attributes: - if attribute == "args": - cached[attribute] = dict([ - (key, u"{}".format(value)) # Force conversion to unicode - for (key, value) - in self.args.iteritems() - ]) - else: - cached[attribute] = getattr(self, attribute) + cached[attribute] = getattr(self, attribute) pickle.dump( cached, open(cached_name(self.datadir, self.subpath), 'wb'), @@ -165,7 +154,7 @@ def unprefixed_title(title, prefixes): """Remove the first prefix of the list in the beginning of title (if any). """ for prefix in prefixes: - match = re.compile(ur"^(%s)\b\s*(.*)$" % prefix, re.LOCALE).match(title) + match = re.compile(r"^(%s)\b\s*(.*)$" % prefix, re.LOCALE).match(title) if match: return match.group(2) return title diff --git a/patacrep/templates.py b/patacrep/templates.py index ec8ad99c..d1e9a616 100644 --- a/patacrep/templates.py +++ b/patacrep/templates.py @@ -12,15 +12,15 @@ import json from patacrep import encoding, errors, files _LATEX_SUBS = ( - (re.compile(ur'\\'), ur'\\textbackslash'), - (re.compile(ur'([{}_#%&$])'), ur'\\\1'), - (re.compile(ur'~'), ur'\~{}'), - (re.compile(ur'\^'), ur'\^{}'), - (re.compile(ur'"'), ur"''"), - (re.compile(ur'\.\.\.+'), ur'\\ldots'), + (re.compile(r'\\'), r'\\textbackslash'), + (re.compile(r'([{}_#%&$])'), r'\\\1'), + (re.compile(r'~'), r'\~{}'), + (re.compile(r'\^'), r'\^{}'), + (re.compile(r'"'), r"''"), + (re.compile(r'\.\.\.+'), r'\\ldots'), ) -_VARIABLE_REGEXP = re.compile(ur""" +_VARIABLE_REGEXP = re.compile(r""" \(\*\ *variables\ *\*\) # Match (* variables *) ( # Match and capture the following: (?: # Start of non-capturing group, used to match a single character @@ -48,7 +48,7 @@ class VariablesExtension(Extension): tags = set(['variables']) def parse(self, parser): - parser.stream.next() + next(parser.stream) parser.parse_statements( end_tokens=['name:endvariables'], drop_needle=True, diff --git a/readme.md b/readme.md index a4ae03a4..ed4b1860 100644 --- a/readme.md +++ b/readme.md @@ -12,7 +12,7 @@ is precised in the header. # Python version -Patacrep is compatible with Python 2.7 (no Python3 yet). +Patacrep is compatible with Python 3. # Download @@ -26,7 +26,7 @@ Clone Patacrep repos: Make sure you have [pip](https://pip.pypa.io/en/latest/) installed, and then run > pip install -r Requirements.txt -> python setup.py install +> python3 setup.py install # Run @@ -40,10 +40,10 @@ Look for existing songbook files in `/books/`. For example: # Quick and dirty deb packages -Install `python-stdeb`, then: +Install `python3-stdeb`, then: -> python setup.py --command-packages=stdeb.command bdist_deb -> sudo dpkg -i deb_dist/python-patacrep_-1_all.deb +> python3 setup.py --command-packages=stdeb.command bdist_deb +> sudo dpkg -i deb_dist/python3-patacrep_-1_all.deb # Documentation diff --git a/setup.py b/setup.py index 6936bd35..0f616b30 100755 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """Installation script for songbook. diff --git a/songbook b/songbook index bcf015ee..1bd48f92 100755 --- a/songbook +++ b/songbook @@ -1,4 +1,4 @@ -#! /usr/bin/env python2 +#! /usr/bin/env python3 # -*- coding: utf-8 -*- """Command line tool to compile songbooks using the songbook library.""" @@ -92,7 +92,7 @@ def main(): locale.setlocale(locale.LC_ALL, '') except locale.Error as error: # Locale is not installed on user's system, or wrongly configured. - sys.stderr.write("Locale error: {}\n".format(error.message)) + sys.stderr.write("Locale error: {}\n".format(str(error))) options = argument_parser(sys.argv[1:]) @@ -119,7 +119,7 @@ def main(): datadirs += [item[0] for item in options.datadir] if 'datadir' in songbook: # .sg file - if isinstance(songbook['datadir'], basestring): + if isinstance(songbook['datadir'], str): songbook['datadir'] = [songbook['datadir']] datadirs += [ os.path.join( diff --git a/stdeb.cfg b/stdeb.cfg index d654b578..ed8a5bed 100644 --- a/stdeb.cfg +++ b/stdeb.cfg @@ -1,6 +1,6 @@ [DEFAULT] -Depends: python-jinja2, python-pkg-resources, python-chardet, python-unidecode, texlive-latex-base, texlive-latex-recommended, texlive-latex-extra, lilypond, texlive-fonts-recommended +Depends: python3-jinja2, python3-pkg-resources, python3-chardet, python3-unidecode, texlive-latex-base, texlive-latex-recommended, texlive-latex-extra, lilypond, texlive-fonts-recommended Recommends: texlive-lang-english, texlive-lang-french, texlive-lang-portuguese, texlive-lang-spanish, texlive-fonts-extra -XS-Python-Version: >=2.7 +X-Python3-Version: Section: tex