Browse Source

Converted to Python3

Not fully tested: base case seems to work
pull/66/head
Louis 10 years ago
parent
commit
2a3bee175e
  1. 4
      patacrep/build.py
  2. 6
      patacrep/content/__init__.py
  3. 2
      patacrep/content/cwd.py
  4. 6
      patacrep/content/section.py
  5. 8
      patacrep/content/song.py
  6. 4
      patacrep/content/songsection.py
  7. 10
      patacrep/content/sorted.py
  8. 4
      patacrep/content/tex.py
  9. 29
      patacrep/encoding.py
  10. 3
      patacrep/errors.py
  11. 2
      patacrep/files.py
  12. 33
      patacrep/index.py
  13. 2
      patacrep/latex/__init__.py
  14. 17
      patacrep/songs.py
  15. 16
      patacrep/templates.py
  16. 10
      readme.md
  17. 2
      setup.py
  18. 6
      songbook
  19. 4
      stdeb.cfg

4
patacrep/build.py

@ -57,7 +57,7 @@ class Songbook(object):
def _set_datadir(self): def _set_datadir(self):
"""Set the default values for datadir""" """Set the default values for datadir"""
try: try:
if isinstance(self.config['datadir'], basestring): if isinstance(self.config['datadir'], str):
self.config['datadir'] = [self.config['datadir']] self.config['datadir'] = [self.config['datadir']]
except KeyError: # No datadir in the raw_songbook except KeyError: # No datadir in the raw_songbook
self.config['datadir'] = [os.path.abspath('.')] self.config['datadir'] = [os.path.abspath('.')]
@ -213,7 +213,7 @@ class SongbookBuilder(object):
log = '' log = ''
line = process.stdout.readline() line = process.stdout.readline()
while line: while line:
log += line log += str(line)
line = process.stdout.readline() line = process.stdout.readline()
LOGGER.debug(log) LOGGER.debug(log)

6
patacrep/content/__init__.py

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""Content plugin management. """Content plugin management.
@ -225,11 +225,11 @@ def process_content(content, config=None):
""" """
contentlist = [] contentlist = []
plugins = load_plugins(config) plugins = load_plugins(config)
keyword_re = re.compile(ur'^ *(?P<keyword>\w*) *(\((?P<argument>.*)\))? *$') keyword_re = re.compile(r'^ *(?P<keyword>\w*) *(\((?P<argument>.*)\))? *$')
if not content: if not content:
content = [["song"]] content = [["song"]]
for elem in content: for elem in content:
if isinstance(elem, basestring): if isinstance(elem, str):
elem = ["song", elem] elem = ["song", elem]
if len(content) == 0: if len(content) == 0:
content = ["song"] content = ["song"]

2
patacrep/content/cwd.py

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""Change base directory before importing songs.""" """Change base directory before importing songs."""

6
patacrep/content/section.py

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""Allow LaTeX sections (starred or not) as content of a songbook.""" """Allow LaTeX sections (starred or not) as content of a songbook."""
@ -26,9 +26,9 @@ class Section(Content):
def render(self, __context): def render(self, __context):
if self.short is None: if self.short is None:
return ur'\{}{{{}}}'.format(self.keyword, self.name) return r'\{}{{{}}}'.format(self.keyword, self.name)
else: else:
return ur'\{}[{}]{{{}}}'.format(self.keyword, self.short, self.name) return r'\{}[{}]{{{}}}'.format(self.keyword, self.short, self.name)
#pylint: disable=unused-argument #pylint: disable=unused-argument
def parse(keyword, argument, contentlist, config): def parse(keyword, argument, contentlist, config):

8
patacrep/content/song.py

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""Plugin to include songs to the songbook.""" """Plugin to include songs to the songbook."""
@ -26,15 +26,15 @@ class SongRenderer(Content, Song):
indexes = context.resolve("indexes") indexes = context.resolve("indexes")
if isinstance(indexes, jinja2.runtime.Undefined): if isinstance(indexes, jinja2.runtime.Undefined):
indexes = "" indexes = ""
return ur'\begin{songs}{%s}' % indexes return r'\begin{songs}{%s}' % indexes
def end_block(self, __context): def end_block(self, __context):
"""Return the string to end a block.""" """Return the string to end a block."""
return ur'\end{songs}' return r'\end{songs}'
def render(self, context): def render(self, context):
"""Return the string that will render the song.""" """Return the string that will render the song."""
return ur'\input{{{}}}'.format(files.path2posix( return r'\input{{{}}}'.format(files.path2posix(
files.relpath( files.relpath(
self.fullpath, self.fullpath,
os.path.dirname(context['filename']) os.path.dirname(context['filename'])

4
patacrep/content/songsection.py

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""Allow 'songchapter' and 'songsection' as content of a songbook.""" """Allow 'songchapter' and 'songsection' as content of a songbook."""
@ -19,7 +19,7 @@ class SongSection(Content):
def render(self, __context): def render(self, __context):
"""Render this section or chapter.""" """Render this section or chapter."""
return ur'\{}{{{}}}'.format(self.keyword, self.name) return r'\{}{{{}}}'.format(self.keyword, self.name)
#pylint: disable=unused-argument #pylint: disable=unused-argument
def parse(keyword, argument, contentlist, config): def parse(keyword, argument, contentlist, config):

10
patacrep/content/sorted.py

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""Sorted list of songs. """Sorted list of songs.
@ -9,9 +9,9 @@ to a songbook.
import locale import locale
import logging import logging
import unidecode
from patacrep import files from patacrep import files
from patacrep import encoding
from patacrep.content import ContentError from patacrep.content import ContentError
from patacrep.content.song import OnlySongsError, process_songs from patacrep.content.song import OnlySongsError, process_songs
@ -27,11 +27,11 @@ def normalize_string(string):
- lower case; - lower case;
- passed through locale.strxfrm(). - passed through locale.strxfrm().
""" """
return locale.strxfrm(encoding.unidecode(string.lower().strip())) return locale.strxfrm(unidecode.unidecode(string.lower().strip()))
def normalize_field(field): def normalize_field(field):
"""Return a normalized field, it being a string or a list of strings.""" """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) return normalize_string(field)
elif isinstance(field, list) or isinstance(field, tuple): elif isinstance(field, list) or isinstance(field, tuple):
return [normalize_field(string) for string in field] return [normalize_field(string) for string in field]
@ -63,7 +63,7 @@ def key_generator(sort):
files.relpath(song.fullpath), files.relpath(song.fullpath),
) )
) )
field = u"" field = ""
songkey.append(normalize_field(field)) songkey.append(normalize_field(field))
return songkey return songkey
return ordered_song_keys return ordered_song_keys

4
patacrep/content/tex.py

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""Include LaTeX raw code in the songbook.""" """Include LaTeX raw code in the songbook."""
@ -18,7 +18,7 @@ class LaTeX(Content):
self.filename = filename self.filename = filename
def render(self, context): def render(self, context):
return ur'\input{{{}}}'.format(files.relpath( return r'\input{{{}}}'.format(files.relpath(
self.filename, self.filename,
os.path.dirname(context['filename']), os.path.dirname(context['filename']),
)) ))

29
patacrep/encoding.py

@ -5,7 +5,6 @@
import codecs import codecs
import chardet import chardet
import logging import logging
from unidecode import unidecode as unidecode_orig
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
@ -17,32 +16,6 @@ def open_read(filename, mode='r'):
return codecs.open( return codecs.open(
filename, filename,
mode=mode, mode=mode,
encoding=chardet.detect(open(filename, "r").read())['encoding'], encoding=chardet.detect(open(filename, 'rb').read())['encoding'],
errors='replace', 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))

3
patacrep/errors.py

@ -17,9 +17,6 @@ class SBFileError(SongbookError):
self.message = message self.message = message
def __str__(self): def __str__(self):
if self.message is None:
return str(self.original)
else:
return self.message return self.message
class TemplateError(SongbookError): class TemplateError(SongbookError):

2
patacrep/files.py

@ -16,7 +16,7 @@ def recursive_find(root_directory, pattern):
matches = [] matches = []
with chdir(root_directory): 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): for filename in fnmatch.filter(filenames, pattern):
matches.append(os.path.join(root, filename)) matches.append(os.path.join(root, filename))
return matches return matches

33
patacrep/index.py

@ -8,17 +8,18 @@ from a file generated by the latex compilation of the songbook (.sxd).
""" """
import locale import locale
import unidecode
import re import re
from patacrep import authors from patacrep import authors
from patacrep import encoding from patacrep import encoding
from patacrep.latex import latex2unicode from patacrep.latex import latex2unicode
EOL = u"\n" EOL = "\n"
# Pattern set to ignore latex command in title prefix # Pattern set to ignore latex command in title prefix
KEYWORD_PATTERN = re.compile(ur"^%(\w+)\s?(.*)$", re.LOCALE) KEYWORD_PATTERN = re.compile(r"^%(\w+)\s?(.*)$", re.LOCALE)
FIRST_LETTER_PATTERN = re.compile(ur"^(?:\{?\\\w+\}?)*[^\w]*(\w)", re.LOCALE) FIRST_LETTER_PATTERN = re.compile(r"^(?:\{?\\\w+\}?)*[^\w]*(\w)", re.LOCALE)
def process_sxd(filename): def process_sxd(filename):
@ -77,13 +78,13 @@ class Index(object):
except AttributeError: except AttributeError:
# classify as number all the non letter characters # classify as number all the non letter characters
letter = "0" letter = "0"
if re.match(ur'\d', letter): if re.match(r'\d', letter):
letter = '0-9' letter = '0-9'
return letter.upper() return letter.upper()
def add_keyword(self, key, word): def add_keyword(self, key, word):
"""Add 'word' to self.keywords[key].""" """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] = []
self.keywords[key].append(word) self.keywords[key].append(word)
@ -93,7 +94,7 @@ class Index(object):
if 'prefix' in self.keywords: if 'prefix' in self.keywords:
for prefix in self.keywords['prefix']: for prefix in self.keywords['prefix']:
self.prefix_patterns.append(re.compile( self.prefix_patterns.append(re.compile(
ur"^({prefix})(\b|\\)(\s*.*)$".format(prefix=prefix), r"^({prefix})(\b|\\)(\s*.*)$".format(prefix=prefix),
re.LOCALE re.LOCALE
)) ))
@ -107,12 +108,12 @@ class Index(object):
similar method with processing. similar method with processing.
""" """
first = self.get_first_letter(key[0]) first = self.get_first_letter(key[0])
if not first in self.data.keys(): if not first in self.data:
self.data[first] = dict() self.data[first] = dict()
if not key in self.data[first].keys(): if not key in self.data[first]:
self.data[first][key] = { self.data[first][key] = {
'sortingkey': [ 'sortingkey': [
encoding.unidecode(latex2unicode(item)).lower() unidecode.unidecode(latex2unicode(item)).lower()
for item in key for item in key
], ],
'entries': [], 'entries': [],
@ -150,26 +151,26 @@ class Index(object):
@staticmethod @staticmethod
def ref_to_str(ref): def ref_to_str(ref):
"""Return the LaTeX code corresponding to the reference.""" """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): def key_to_str(self, key):
"""Convert the key (title or author) to the LaTeX command rendering it. """Convert the key (title or author) to the LaTeX command rendering it.
""" """
if self.indextype == "AUTHOR": if self.indextype == "AUTHOR":
return ur"\indexauthor{{{first}}}{{{last}}}".format( return r"\indexauthor{{{first}}}{{{last}}}".format(
first=key[1], first=key[1],
last=key[0], last=key[0],
) )
if self.indextype == "TITLE": 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): def entry_to_str(self, key, entry):
"""Return the LaTeX code corresponding to the 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), 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): def idxblock_to_str(self, letter, entries):
@ -185,10 +186,10 @@ class Index(object):
for item for item
in entries[key]['sortingkey'] in entries[key]['sortingkey']
] ]
string = ur'\begin{idxblock}{' + letter + '}' + EOL string = r'\begin{idxblock}{' + letter + '}' + EOL
for key in sorted(entries, key=sortkey): for key in sorted(entries, key=sortkey):
string += self.entry_to_str(key, entries[key]['entries']) string += self.entry_to_str(key, entries[key]['entries'])
string += ur'\end{idxblock}' + EOL string += r'\end{idxblock}' + EOL
return string return string
def entries_to_str(self): def entries_to_str(self):

2
patacrep/latex/__init__.py

@ -4,7 +4,7 @@
def latex2unicode(string): def latex2unicode(string):
"""Convert LaTeX string to unicode""" """Convert LaTeX string to unicode"""
return u"TODO" return "TODO"
def parsetex(path): def parsetex(path):
"""Return a dictonary of data read from the latex file `path`. """Return a dictonary of data read from the latex file `path`.

17
patacrep/songs.py

@ -6,12 +6,8 @@ import errno
import hashlib import hashlib
import logging import logging
import os import os
import re
try:
import cPickle as pickle
except ImportError:
import pickle import pickle
import re
from patacrep.authors import processauthors from patacrep.authors import processauthors
from patacrep.latex import parsetex from patacrep.latex import parsetex
@ -128,7 +124,7 @@ class Song(object):
self.args = data['args'] self.args = data['args']
self.subpath = subpath self.subpath = subpath
self.languages = data['languages'] self.languages = data['languages']
if "by" in self.args.keys(): if "by" in self.args:
self.authors = processauthors( self.authors = processauthors(
self.args["by"], self.args["by"],
**config["_compiled_authwords"] **config["_compiled_authwords"]
@ -144,13 +140,6 @@ class Song(object):
if self.datadir: if self.datadir:
cached = {} cached = {}
for attribute in self.cached_attributes: 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( pickle.dump(
cached, cached,
@ -165,7 +154,7 @@ def unprefixed_title(title, prefixes):
"""Remove the first prefix of the list in the beginning of title (if any). """Remove the first prefix of the list in the beginning of title (if any).
""" """
for prefix in prefixes: 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: if match:
return match.group(2) return match.group(2)
return title return title

16
patacrep/templates.py

@ -12,15 +12,15 @@ import json
from patacrep import encoding, errors, files from patacrep import encoding, errors, files
_LATEX_SUBS = ( _LATEX_SUBS = (
(re.compile(ur'\\'), ur'\\textbackslash'), (re.compile(r'\\'), r'\\textbackslash'),
(re.compile(ur'([{}_#%&$])'), ur'\\\1'), (re.compile(r'([{}_#%&$])'), r'\\\1'),
(re.compile(ur'~'), ur'\~{}'), (re.compile(r'~'), r'\~{}'),
(re.compile(ur'\^'), ur'\^{}'), (re.compile(r'\^'), r'\^{}'),
(re.compile(ur'"'), ur"''"), (re.compile(r'"'), r"''"),
(re.compile(ur'\.\.\.+'), ur'\\ldots'), (re.compile(r'\.\.\.+'), r'\\ldots'),
) )
_VARIABLE_REGEXP = re.compile(ur""" _VARIABLE_REGEXP = re.compile(r"""
\(\*\ *variables\ *\*\) # Match (* variables *) \(\*\ *variables\ *\*\) # Match (* variables *)
( # Match and capture the following: ( # Match and capture the following:
(?: # Start of non-capturing group, used to match a single character (?: # Start of non-capturing group, used to match a single character
@ -48,7 +48,7 @@ class VariablesExtension(Extension):
tags = set(['variables']) tags = set(['variables'])
def parse(self, parser): def parse(self, parser):
parser.stream.next() next(parser.stream)
parser.parse_statements( parser.parse_statements(
end_tokens=['name:endvariables'], end_tokens=['name:endvariables'],
drop_needle=True, drop_needle=True,

10
readme.md

@ -12,7 +12,7 @@ is precised in the header.
# Python version # Python version
Patacrep is compatible with Python 2.7 (no Python3 yet). Patacrep is compatible with Python 3.
# Download # Download
@ -26,7 +26,7 @@ Clone Patacrep repos:
Make sure you have [pip](https://pip.pypa.io/en/latest/) installed, and then run Make sure you have [pip](https://pip.pypa.io/en/latest/) installed, and then run
> pip install -r Requirements.txt > pip install -r Requirements.txt
> python setup.py install > python3 setup.py install
# Run # Run
@ -40,10 +40,10 @@ Look for existing songbook files in `<patadata>/books/`. For example:
# Quick and dirty deb packages # Quick and dirty deb packages
Install `python-stdeb`, then: Install `python3-stdeb`, then:
> python setup.py --command-packages=stdeb.command bdist_deb > python3 setup.py --command-packages=stdeb.command bdist_deb
> sudo dpkg -i deb_dist/python-patacrep_<version>-1_all.deb > sudo dpkg -i deb_dist/python3-patacrep_<version>-1_all.deb
# Documentation # Documentation

2
setup.py

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
"""Installation script for songbook. """Installation script for songbook.

6
songbook

@ -1,4 +1,4 @@
#! /usr/bin/env python2 #! /usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""Command line tool to compile songbooks using the songbook library.""" """Command line tool to compile songbooks using the songbook library."""
@ -92,7 +92,7 @@ def main():
locale.setlocale(locale.LC_ALL, '') locale.setlocale(locale.LC_ALL, '')
except locale.Error as error: except locale.Error as error:
# Locale is not installed on user's system, or wrongly configured. # 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:]) options = argument_parser(sys.argv[1:])
@ -119,7 +119,7 @@ def main():
datadirs += [item[0] for item in options.datadir] datadirs += [item[0] for item in options.datadir]
if 'datadir' in songbook: if 'datadir' in songbook:
# .sg file # .sg file
if isinstance(songbook['datadir'], basestring): if isinstance(songbook['datadir'], str):
songbook['datadir'] = [songbook['datadir']] songbook['datadir'] = [songbook['datadir']]
datadirs += [ datadirs += [
os.path.join( os.path.join(

4
stdeb.cfg

@ -1,6 +1,6 @@
[DEFAULT] [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 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 Section: tex

Loading…
Cancel
Save