Skip to content

Commit

Permalink
Merge pull request #51 from patacrep/cache
Browse files Browse the repository at this point in the history
Mise en place d'un cache
  • Loading branch information
Luthaf committed Jul 6, 2014
2 parents 16c43e8 + 443783b commit a3beec2
Show file tree
Hide file tree
Showing 12 changed files with 243 additions and 76 deletions.
38 changes: 12 additions & 26 deletions patacrep/authors.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def split_author_names(string):
brace_count += 1
if char == "{":
brace_count -= 1
return string[:last_space], string[last_space:]
return string[last_space:], string[:last_space]


def split_sep_author(string, sep):
Expand Down Expand Up @@ -162,23 +162,6 @@ def processauthors_clean_authors(authors_list):
if author.lstrip()
]

def processauthors_invert_names(authors_list):
"""Move first names after last names
See docstring of processauthors() for more information.
"""
dest = []
for author in authors_list:
first, last = split_author_names(author)
if first:
dest.append(ur"\indexauthor{{{first}}}{{{last}}}".format(
first=first.strip(),
last=last.strip(),
))
else:
dest.append(last.lstrip())
return dest

def processauthors(authors_string, after=None, ignore=None, sep=None):
r"""Return a list of authors
Expand Down Expand Up @@ -210,10 +193,12 @@ def processauthors(authors_string, after=None, ignore=None, sep=None):
4) Strings containing words of "ignore" are dropped.
# ["William Blake", "Hubert Parry", The Royal\ Choir~of~Nowhere"]
5) First and last names are processed through LaTeX command \indexauthor
(which will, by default, invert first and last names).
# ["\indexauthor{William}{Blake}", "\indexauthor{Hubert}{Parry}",
# \indexthaor{The}{Royal\ Choir~of~Nowhere}"]
5) First and last names are splitted
# [
# ("Blake", "William"),
# ("Parry", "Hubert"),
# ("Royal\ Choir~of~Nowhere", "The"),
# ]
"""

if not sep:
Expand All @@ -223,8 +208,10 @@ def processauthors(authors_string, after=None, ignore=None, sep=None):
if not ignore:
ignore = []

return processauthors_invert_names(
processauthors_clean_authors(
return [
split_author_names(author)
for author
in processauthors_clean_authors(
processauthors_ignore_authors(
processauthors_remove_after(
processauthors_split_string(
Expand All @@ -235,5 +222,4 @@ def processauthors(authors_string, after=None, ignore=None, sep=None):
after),
ignore)
)
)

]
3 changes: 2 additions & 1 deletion patacrep/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from patacrep import __DATADIR__, authors, content, errors
from patacrep.index import process_sxd
from patacrep.templates import TexRenderer
from patacrep.songs import DataSubpath

LOGGER = logging.getLogger(__name__)
EOL = "\n"
Expand Down Expand Up @@ -75,7 +76,7 @@ def _set_datadir(self):

self.config['datadir'] = abs_datadir
self.config['_songdir'] = [
os.path.join(path, 'songs')
DataSubpath(path, 'songs')
for path in self.config['datadir']
]

Expand Down
7 changes: 3 additions & 4 deletions patacrep/content/cwd.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@

"""Change base directory before importing songs."""

import os

from patacrep.content import process_content
from patacrep.songs import DataSubpath

#pylint: disable=unused-argument
def parse(keyword, config, argument, contentlist):
Expand All @@ -28,8 +27,8 @@ def parse(keyword, config, argument, contentlist):
"""
old_songdir = config['_songdir']
config['_songdir'] = (
[argument] +
[os.path.join(path, argument) for path in config['_songdir']] +
[DataSubpath("", argument)] +
[path.clone().join(argument) for path in config['_songdir']] +
config['_songdir']
)
processed_content = process_content(contentlist, config)
Expand Down
4 changes: 4 additions & 0 deletions patacrep/content/include.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
LOGGER = logging.getLogger(__name__)

def load_from_datadirs(path, config=None):
"""Load 'path' from one of the datadirs.
Raise an exception if it was found if none of the datadirs of 'config'.
"""
for datadir in config.get("datadir", []):
filepath = os.path.join(datadir, path)
if os.path.exists(filepath):
Expand Down
25 changes: 16 additions & 9 deletions patacrep/content/song.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def end_block(self, __context):
def render(self, context):
"""Return the string that will render the song."""
return ur'\input{{{}}}'.format(files.relpath(
self.path,
self.fullpath,
os.path.dirname(context['filename'])
))

Expand All @@ -59,21 +59,28 @@ def parse(keyword, argument, contentlist, config):
if contentlist:
break
contentlist = [
files.relpath(filename, songdir)
filename
for filename
in (
files.recursive_find(songdir, "*.sg")
+ files.recursive_find(songdir, "*.is")
files.recursive_find(songdir.fullpath, "*.sg")
+ files.recursive_find(songdir.fullpath, "*.is")
)
]
for elem in contentlist:
before = len(songlist)
for songdir in config['_songdir']:
for filename in glob.iglob(os.path.join(songdir, elem)):
LOGGER.debug('Parsing file "{}"…'.format(filename))
song = SongRenderer(filename, config)
songlist.append(song)
config["_languages"].update(song.languages)
if songdir.datadir and not os.path.isdir(songdir.datadir):
continue
with files.chdir(songdir.datadir):
for filename in glob.iglob(os.path.join(songdir.subpath, elem)):
LOGGER.debug('Parsing file "{}"…'.format(filename))
song = SongRenderer(
songdir.datadir,
filename,
config,
)
songlist.append(song)
config["_languages"].update(song.languages)
if len(songlist) > before:
break
if len(songlist) == before:
Expand Down
8 changes: 4 additions & 4 deletions patacrep/content/sorted.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ def normalize_field(field):
"""Return a normalized field, it being a string or a list of strings."""
if isinstance(field, basestring):
return normalize_string(field)
elif isinstance(field, list):
return [normalize_string(string) for string in field]
elif isinstance(field, list) or isinstance(field, tuple):
return [normalize_field(string) for string in field]

def key_generator(sort):
"""Return a function that returns the list of values used to sort the song.
Expand All @@ -50,7 +50,7 @@ def ordered_song_keys(song):
if key == "@title":
field = song.unprefixed_titles
elif key == "@path":
field = song.path
field = song.fullpath
elif key == "by":
field = song.authors
else:
Expand All @@ -60,7 +60,7 @@ def ordered_song_keys(song):
LOGGER.debug(
"Ignoring unknown key '{}' for song {}.".format(
key,
files.relpath(song.path),
files.relpath(song.fullpath),
)
)
field = u""
Expand Down
7 changes: 5 additions & 2 deletions patacrep/content/tex.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,11 @@ def parse(keyword, argument, contentlist, config):
for filename in contentlist:
checked_file = None
for path in config['_songdir']:
if os.path.exists(os.path.join(path, filename)):
checked_file = os.path.relpath(os.path.join(path, filename))
if os.path.exists(os.path.join(path.fullpath, filename)):
checked_file = os.path.relpath(os.path.join(
path.fullpath,
filename,
))
break
if not checked_file:
LOGGER.warning(
Expand Down
1 change: 1 addition & 0 deletions patacrep/data/examples/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/.cache
30 changes: 27 additions & 3 deletions patacrep/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

"""File system utilities."""

from contextlib import contextmanager
import fnmatch
import os

Expand All @@ -12,10 +13,14 @@ def recursive_find(root_directory, pattern):
Return a list of files matching the pattern.
"""
if not os.path.isdir(root_directory):
return []

matches = []
for root, _, filenames in os.walk(root_directory):
for filename in fnmatch.filter(filenames, pattern):
matches.append(os.path.join(root, filename))
with chdir(root_directory):
for root, _, filenames in os.walk(os.curdir):
for filename in fnmatch.filter(filenames, pattern):
matches.append(os.path.join(root, filename))
return matches

def relpath(path, start=None):
Expand All @@ -26,3 +31,22 @@ def relpath(path, start=None):
return os.path.relpath(path, start)
else:
return os.path.abspath(path)

@contextmanager
def chdir(path):
"""Locally change dir
Can be used as:
with chdir("some/directory"):
do_stuff()
"""
olddir = os.getcwd()
if path:
os.chdir(path)
yield
os.chdir(olddir)
else:
yield

61 changes: 39 additions & 22 deletions patacrep/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,6 @@
FIRST_LETTER_PATTERN = re.compile(ur"^(?:\{?\\\w+\}?)*[^\w]*(\w)", re.LOCALE)


def sortkey(value):
"""From a title, return something usable for sorting.
It handles locale (but
don't forget to call locale.setlocale(locale.LC_ALL, '')). It also handles
the sort with latex escape sequences.
"""
return locale.strxfrm(
encoding.unidecode(simpleparse(value).replace(' ', 'A')).lower()
)


def process_sxd(filename):
"""Parse sxd file.
Expand Down Expand Up @@ -115,12 +103,18 @@ def _raw_add(self, key, number, link):
No processing is done on data. It is added raw. See add() for a
similar method with processing.
"""
first = self.get_first_letter(key)
first = self.get_first_letter(key[0])
if not first in self.data.keys():
self.data[first] = dict()
if not key in self.data[first].keys():
self.data[first][key] = []
self.data[first][key].append({'num': number, 'link': link})
self.data[first][key] = {
'sortingkey': [
encoding.unidecode(simpleparse(item)).lower()
for item in key
],
'entries': [],
}
self.data[first][key]['entries'].append({'num': number, 'link': link})

def add(self, key, number, link):
"""Add a song to the list.
Expand All @@ -133,15 +127,15 @@ def add(self, key, number, link):
match = pattern.match(key)
if match:
self._raw_add(
ur"\indextitle{{{}}}{{{}}}".format(
(
match.group(1).strip(),
(match.group(2) + match.group(3)).strip(),
),
(match.group(2) + match.group(3)).strip()
),
number,
link
)
return
self._raw_add(key, number, link)
self._raw_add((key, ""), number, link)

if self.indextype == "AUTHOR":
# Processing authors
Expand All @@ -155,10 +149,26 @@ def ref_to_str(ref):
"""Return the LaTeX code corresponding to the reference."""
return ur'\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":
if key[1]:
return ur"\indexauthor{{{first}}}{{{last}}}".format(
first=key[1],
last=key[0],
)
else:
return key[0]

if self.indextype == "TITLE":
return ur"\indextitle{{{0[0]}}}{{{0[1]}}}".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(
key,
self.key_to_str(key),
ur'\\'.join([self.ref_to_str(ref) for ref in entry]),
)

Expand All @@ -168,9 +178,16 @@ def idxblock_to_str(self, letter, entries):
Here, an index block is a letter, and all data beginning with this
letter.
"""
def sortkey(key):
"""Return something sortable for `entries[key]`."""
return [
locale.strxfrm(item)
for item
in entries[key]['sortingkey']
]
string = ur'\begin{idxblock}{' + letter + '}' + EOL
for key in sorted(entries.keys(), key=sortkey):
string += self.entry_to_str(key, entries[key])
for key in sorted(entries, key=sortkey):
string += self.entry_to_str(key, entries[key]['entries'])
string += ur'\end{idxblock}' + EOL
return string

Expand Down
1 change: 1 addition & 0 deletions patacrep/plastex.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ def simpleparse(text):
"""Parse a simple LaTeX string.
"""
tex = TeX()
tex.disableLogging()
tex.input(text)
doc = tex.parse()
return process_unbr_spaces(doc.textContent)
Expand Down

0 comments on commit a3beec2

Please sign in to comment.