[formatter] add basic 'Jinja2' template support (#1390)
This commit is contained in:
@@ -36,6 +36,7 @@ Optional
|
||||
- SecretStorage_: GNOME keyring passwords for ``--cookies-from-browser``
|
||||
- Psycopg_: PostgreSQL archive support
|
||||
- truststore_: Native system certificate support
|
||||
- Jinja_: Jinja template support
|
||||
|
||||
|
||||
Installation
|
||||
@@ -476,6 +477,7 @@ To authenticate with a ``mastodon`` instance, run *gallery-dl* with
|
||||
.. _SecretStorage: https://pypi.org/project/SecretStorage/
|
||||
.. _Psycopg: https://www.psycopg.org/
|
||||
.. _truststore: https://truststore.readthedocs.io/en/latest/
|
||||
.. _Jinja: https://jinja.palletsprojects.com/
|
||||
.. _Snapd: https://docs.snapcraft.io/installing-snapd
|
||||
.. _OAuth: https://en.wikipedia.org/wiki/OAuth
|
||||
.. _Chocolatey: https://chocolatey.org/install
|
||||
|
||||
@@ -381,15 +381,20 @@ Starting a format string with `\f<Type> ` allows to set a different format strin
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center"><code>E</code></td>
|
||||
<td>An arbitrary Python expression</td>
|
||||
<td><code>\fE title.upper().replace(' ', '-')</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><code>F</code></td>
|
||||
<td>An <a href="https://docs.python.org/3/tutorial/inputoutput.html#formatted-string-literals">f-string</a> literal</td>
|
||||
<td><code>\fF '{title.strip()}' by {artist.capitalize()}</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><code>E</code></td>
|
||||
<td>An arbitrary Python expression</td>
|
||||
<td><code>\fE title.upper().replace(' ', '-')</code></td>
|
||||
<td align="center"><code>J</code></td>
|
||||
<td>A <a href="https://jinja.palletsprojects.com/">Jinja</a> template</td>
|
||||
<td><code>\fJ '{{title | trim}}' by {{artist | capitalize}}</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><code>T</code></td>
|
||||
@@ -401,6 +406,11 @@ Starting a format string with `\f<Type> ` allows to set a different format strin
|
||||
<td>Path to a template file containing an <a href="https://docs.python.org/3/tutorial/inputoutput.html#formatted-string-literals">f-string</a> literal</td>
|
||||
<td><code>\fTF ~/.templates/fstr.txt</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><code>TJ</code></td>
|
||||
<td>Path to a template file containing a <a href="https://jinja.palletsprojects.com/">Jinja</a> template</td>
|
||||
<td><code>\fTF ~/.templates/jinja.txt</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><code>M</code></td>
|
||||
<td>Path or name of a Python module
|
||||
|
||||
@@ -29,7 +29,7 @@ def parse(format_string, default=NONE, fmt=format):
|
||||
pass
|
||||
|
||||
cls = StringFormatter
|
||||
if format_string.startswith("\f"):
|
||||
if format_string and format_string[0] == "\f":
|
||||
kind, _, format_string = format_string.partition(" ")
|
||||
kind = kind[1:]
|
||||
|
||||
@@ -37,12 +37,16 @@ def parse(format_string, default=NONE, fmt=format):
|
||||
cls = TemplateFormatter
|
||||
elif kind == "TF":
|
||||
cls = TemplateFStringFormatter
|
||||
elif kind == "TJ":
|
||||
cls = TemplateJinjaFormatter
|
||||
elif kind == "E":
|
||||
cls = ExpressionFormatter
|
||||
elif kind == "M":
|
||||
cls = ModuleFormatter
|
||||
elif kind == "J":
|
||||
cls = JinjaFormatter
|
||||
elif kind == "F":
|
||||
cls = FStringFormatter
|
||||
elif kind == "M":
|
||||
cls = ModuleFormatter
|
||||
|
||||
formatter = _CACHE[key] = cls(format_string, default, fmt)
|
||||
return formatter
|
||||
@@ -224,6 +228,17 @@ class FStringFormatter():
|
||||
self.format_map = util.compile_expression(f'f"""{fstring}"""')
|
||||
|
||||
|
||||
class JinjaFormatter():
|
||||
"""Generate text by evaluating a Jinja template string"""
|
||||
env = None
|
||||
|
||||
def __init__(self, source, default=NONE, fmt=None):
|
||||
if self.env is None:
|
||||
import jinja2
|
||||
JinjaFormatter.env = jinja2.Environment()
|
||||
self.format_map = self.env.from_string(source).render
|
||||
|
||||
|
||||
class TemplateFormatter(StringFormatter):
|
||||
"""Read format_string from file"""
|
||||
|
||||
@@ -242,6 +257,15 @@ class TemplateFStringFormatter(FStringFormatter):
|
||||
FStringFormatter.__init__(self, fstring, default, fmt)
|
||||
|
||||
|
||||
class TemplateJinjaFormatter(JinjaFormatter):
|
||||
"""Generate text by evaluating a Jinja template"""
|
||||
|
||||
def __init__(self, path, default=NONE, fmt=None):
|
||||
with open(util.expand_path(path)) as fp:
|
||||
source = fp.read()
|
||||
JinjaFormatter.__init__(self, source, default, fmt)
|
||||
|
||||
|
||||
def parse_field_name(field_name):
|
||||
if field_name[0] == "'":
|
||||
return "_lit", (operator.itemgetter(field_name[1:-1]),)
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
|
||||
__version__ = "1.30.0"
|
||||
__version__ = "1.30.1-dev"
|
||||
__variant__ = None
|
||||
|
||||
@@ -17,6 +17,11 @@ import tempfile
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
from gallery_dl import formatter, text, util # noqa E402
|
||||
|
||||
try:
|
||||
import jinja2
|
||||
except ImportError:
|
||||
jinja2 = None
|
||||
|
||||
|
||||
class TestFormatter(unittest.TestCase):
|
||||
|
||||
@@ -476,6 +481,35 @@ class TestFormatter(unittest.TestCase):
|
||||
with self.assertRaises(OSError):
|
||||
formatter.parse("\fTF /")
|
||||
|
||||
@unittest.skipIf(jinja2 is None, "no jinja2")
|
||||
def test_jinja(self):
|
||||
self._run_test("\fJ {{a}}", self.kwdict["a"])
|
||||
self._run_test("\fJ {{name}}{{name}} {{a}}", "{}{} {}".format(
|
||||
self.kwdict["name"], self.kwdict["name"], self.kwdict["a"]))
|
||||
self._run_test("\fJ foo-'\"{{a | upper}}\"'-bar",
|
||||
"""foo-'"{}"'-bar""".format(self.kwdict["a"].upper()))
|
||||
|
||||
@unittest.skipIf(jinja2 is None, "no jinja2")
|
||||
def test_template_jinja(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdirname:
|
||||
path1 = os.path.join(tmpdirname, "tpl1")
|
||||
path2 = os.path.join(tmpdirname, "tpl2")
|
||||
|
||||
with open(path1, "w") as fp:
|
||||
fp.write("{{a}}")
|
||||
fmt1 = formatter.parse("\fTJ " + path1)
|
||||
|
||||
with open(path2, "w") as fp:
|
||||
fp.write("foo-'\"{{a | upper}}\"'-bar")
|
||||
fmt2 = formatter.parse("\fTJ " + path2)
|
||||
|
||||
self.assertEqual(fmt1.format_map(self.kwdict), self.kwdict["a"])
|
||||
self.assertEqual(fmt2.format_map(self.kwdict),
|
||||
"""foo-'"{}"'-bar""".format(self.kwdict["a"].upper()))
|
||||
|
||||
with self.assertRaises(OSError):
|
||||
formatter.parse("\fTJ /")
|
||||
|
||||
def test_module(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdirname:
|
||||
path = os.path.join(tmpdirname, "testmod.py")
|
||||
|
||||
Reference in New Issue
Block a user