3

I have a command line python app using poetry, that I would like to ship with a couple of jinja2 template files. As of know I have them in a template directory. How would I go about shipping those templates with the app and how do I make sure that the app knows where the templates are located?

Here's my (naive) attempt at using pkgutil to accomplish this:

directory structure:

.
├── app.py
├── LICENSE
├── poetry.lock
├── pyproject.toml
└── templates
    └── message.en_US.jinja2

app.py:

import pkgutil
from jinja2 import Environment, FileSystemLoader

data = pkgutil.get_data(__name__, "templates")

env = Environment(loader=FileSystemLoader(data))
template = env.get_template("message.en_US.jinja2")
print(template.render({"foo": 1, "bar": 2}))

pyproject.toml:

[tool.poetry]
name = "app"
version = "0.1.0"
description = "Does stuff"
authors = ["Foo Bar <foo@bar.com>"]
license = "AGPL-3.0-or-later"

[tool.poetry.dependencies]
python = "^3.8"
jinja2 = "^2.11.3"

[tool.poetry.scripts]
app = "app:main"

[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"

This fails because the templates folder doesn't get packaged. I also don't know if I can pass the folder to the Environment constructor like this.

Philippe
  • 1,715
  • 4
  • 25
  • 49
  • 2
    Show your project directory structure and your `pyproject.toml`. In common cases poetry automatically adds those (see [packages](https://python-poetry.org/docs/pyproject/#packages) and [include and exclude](https://python-poetry.org/docs/pyproject/#include-and-exclude)). -- To read the templates at run-time use [pkgutil](https://docs.python.org/3/library/pkgutil.html) or [importlib.resources](https://docs.python.org/3/library/importlib.html#module-importlib.resources), and read [this answer](https://stackoverflow.com/a/58941536/11138259). – sinoroc Mar 17 '21 at 22:01
  • I see your edit and looks good, but you still need to add `templates` as a package, so you would need to use `include` or `packages` as documented. – sinoroc Mar 18 '21 at 17:28
  • pkgutil doesn't accept it when you instruct it to load a directory. How do I go about marrying pkgutils and Environment? – Philippe Mar 18 '21 at 17:35
  • I don't know Jinja, but maybe use [_`PackageLoader`_](https://jinja.palletsprojects.com/en/2.11.x/api/?highlight=environment#jinja2.PackageLoader) instead (and probably you don't need pkgutil at all). Also you need to fix your packaging. – sinoroc Mar 18 '21 at 17:40
  • for a start, you could add `include = ["templates/*"]` in your `[tool.poetry]` section, run `poetry build`, and then unzip the resulting `.whl` file to look inside and check whether the templates got packaged or not. If you get that far, you should be able to handle the rest with `importlib.resources`, as sinoroc linked. – Arne Mar 19 '21 at 08:05
  • `PackageLoader` is what did it in the end. You have to add an empty `__init__.py` however to turn it into a package. – Philippe Mar 19 '21 at 17:39

2 Answers2

2

In the context of Jinja this can be done easily with PackageLoader, which is able to load resources from a Python import package directly.

And of course make sure the resource files (the template files) are correctly "packaged": the files should be part of an import package. In the case of poetry, make sure to have the correct values for the include and packages fields in your pyproject.toml.

sinoroc
  • 18,409
  • 2
  • 39
  • 70
1

Poetry provides a fairly simple but non-obvious solution (tucked away): pyproject.toml - [poetry.tool]include = [...]

[tool.poetry]
...
include = ["resource/resource.txt"]
Sam
  • 91
  • 5