diff --git a/beets/dbcore/db.py b/beets/dbcore/db.py index 97a4a7ce3..3195b52c9 100755 --- a/beets/dbcore/db.py +++ b/beets/dbcore/db.py @@ -25,7 +25,7 @@ import sqlite3 import contextlib import beets -from beets.util.functemplate import Template +from beets.util import functemplate from beets.util import py3_path from beets.dbcore import types from .query import MatchQuery, NullSort, TrueQuery @@ -597,7 +597,7 @@ class Model(object): """ # Perform substitution. if isinstance(template, six.string_types): - template = Template(template) + template = functemplate.template(template) return template.substitute(self.formatted(for_path), self._template_funcs()) diff --git a/beets/library.py b/beets/library.py index 103054fd3..9a9d95256 100644 --- a/beets/library.py +++ b/beets/library.py @@ -31,7 +31,7 @@ from beets import plugins from beets import util from beets.util import bytestring_path, syspath, normpath, samefile, \ MoveOperation, lazy_property -from beets.util.functemplate import Template +from beets.util.functemplate import template, Template from beets import dbcore from beets.dbcore import types import beets @@ -867,7 +867,7 @@ class Item(LibModel): if isinstance(path_format, Template): subpath_tmpl = path_format else: - subpath_tmpl = Template(path_format) + subpath_tmpl = template(path_format) # Evaluate the selected template. subpath = self.evaluate_template(subpath_tmpl, True) @@ -947,7 +947,7 @@ class Album(LibModel): 'releasegroupdisambig': types.STRING, 'rg_album_gain': types.NULL_FLOAT, 'rg_album_peak': types.NULL_FLOAT, - 'r128_album_gain': types.PaddedInt(6), + 'r128_album_gain': types.NullPaddedInt(6), 'original_year': types.PaddedInt(4), 'original_month': types.PaddedInt(2), 'original_day': types.PaddedInt(2), @@ -1146,7 +1146,7 @@ class Album(LibModel): image = bytestring_path(image) item_dir = item_dir or self.item_dir() - filename_tmpl = Template( + filename_tmpl = template( beets.config['art_filename'].as_str()) subpath = self.evaluate_template(filename_tmpl, True) if beets.config['asciify_paths']: diff --git a/beets/ui/__init__.py b/beets/ui/__init__.py index 327db6b04..622a1e7f0 100644 --- a/beets/ui/__init__.py +++ b/beets/ui/__init__.py @@ -36,7 +36,7 @@ from beets import logging from beets import library from beets import plugins from beets import util -from beets.util.functemplate import Template +from beets.util.functemplate import template from beets import config from beets.util import confit, as_string from beets.autotag import mb @@ -616,7 +616,7 @@ def get_path_formats(subview=None): subview = subview or config['paths'] for query, view in subview.items(): query = PF_KEY_QUERIES.get(query, query) # Expand common queries. - path_formats.append((query, Template(view.as_str()))) + path_formats.append((query, template(view.as_str()))) return path_formats diff --git a/beets/util/functemplate.py b/beets/util/functemplate.py index 6a34a3bb3..5d9900f0b 100644 --- a/beets/util/functemplate.py +++ b/beets/util/functemplate.py @@ -35,6 +35,7 @@ import dis import types import sys import six +import functools SYMBOL_DELIM = u'$' FUNC_DELIM = u'%' @@ -553,8 +554,23 @@ def _parse(template): return Expression(parts) -# External interface. +def cached(func): + """Like the `functools.lru_cache` decorator, but works (as a no-op) + on Python < 3.2. + """ + if hasattr(functools, 'lru_cache'): + return functools.lru_cache(maxsize=128)(func) + else: + # Do nothing when lru_cache is not available. + return func + +@cached +def template(fmt): + return Template(fmt) + + +# External interface. class Template(object): """A string template, including text, Symbols, and Calls. """ diff --git a/docs/changelog.rst b/docs/changelog.rst index 145eed330..53be0befd 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -117,6 +117,10 @@ Some improvements have been focused on improving beets' performance: to be displayed. Thanks to :user:`pprkut`. :bug:`3089` +* Another query optimization works by compiling templates once and reusing + them instead of recompiling them to print out each matching object. + Thanks to :user:`SimonPersson`. + :bug:`3258` * :doc:`/plugins/absubmit`, :doc:`/plugins/badfiles`: Analysis now works in parallel (on Python 3 only). Thanks to :user:`bemeurer`. diff --git a/tox.ini b/tox.ini index e3250bd6b..8736f0f3c 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ # and then run "tox" from this directory. [tox] -envlist = py27-test, py37-test, py38-test, py27-flake8, docs +envlist = py27-test, py37-test, py27-flake8, docs # The exhaustive list of environments is: # envlist = py{27,34,35}-{test,cov}, py{27,34,35}-flake8, docs