jinja2-embedded
===============
|pypi| |python| |pre-commit| |mypy| |codecov|
.. |pypi| image:: https://badge.fury.io/py/jinja2-embedded.svg
:target: https://pypi.org/project/jinja2-embedded/
:alt: Latest Version
.. |python| image:: https://img.shields.io/pypi/pyversions/jinja2-embedded
:target: https://www.python.org/
:alt: Supported Python Versions
.. |pre-commit| image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white
:target: https://github.com/pre-commit/pre-commit
:alt: Pre-Commit enabled
.. |mypy| image:: http://www.mypy-lang.org/static/mypy_badge.svg
:target: http://mypy-lang.org/
:alt: MyPy checked
.. |codecov| image:: https://codecov.io/gh/GPla/jinja2-embedded/graph/badge.svg?token=FVA4W2KHR4
:target: https://codecov.io/gh/GPla/jinja2-embedded
:alt: Code Coverage
Template loader for embedded python runtimes, e.g., `PyOxidizer <https://github.com/indygreg/PyOxidizer>`_ or `PyInstaller <https://github.com/pyinstaller/pyinstaller>`_.
The main problem with the current `PackageLoader <https://jinja.palletsprojects.com/en/3.0.x/api/#jinja2.PackageLoader>`_ is that it can only load templates from packages which are installed and materialized as directories.
However, when using a bundler from above, the resources, i.e., templates, are embedded into the executable.
Thus, the :code:`PackageLoader` will throw the following exception: :code:`The package was not installed in a way that PackageLoader understands`.
The :code:`EmbeddedPackageLoader` from this package fixes this problem and required minimal changes.
Under the hood, we utilize the :code:`Loader` and :code:`ResourceReader` implementation of the package provided through `importlib <https://docs.python.org/3/library/importlib.html>`_.
Thereby, the :code:`EmbeddedPackageLoader` will work when the package is normally installed as directory and in an embedded environment.
How to use
^^^^^^^^^^
Two changes are necessary.
First, change :code:`PackageLoader` to :code:`EmbeddedPackageLoader`:
.. code::
from jinja2 import Environment, PackageLoader
from jinja2_embedded import EmbeddedPackageLoader
# before
env = Environment(
loader=PackageLoader('my_package', 'templates'),
autoescape=True,
...
)
# after
env = Environment(
loader=EmbeddedPackageLoader('my_package.templates'),
autoescape=True, # default False, but FastAPI uses True as default
extensions=[],
)
# with FastAPI
from fastapi.templating import Jinja2Templates
templates = Jinja2Templates(env=env)
Second, declare the `templates` directory as a module by adding a :code:`__init__.py` file:
.. code::
my_package
├── __init__.py
├── main.py
└── templates
├── __init__.py # required
├── bar
│ ├── __init__.py # not required
│ └── test.html.jinja2
├── foo
│ └── test.html
└── test.html
The subdirectories inside the `templates` directory can be declared as modules (here :code:`my_package.templates.bar`), but this is not required.
The :code:`EmbeddedPackageLoader` works with either or mixed configuration.
How it works
^^^^^^^^^^^^
The :code:`EmbeddedPackageLoader` will first try to locate the template with the :code:`ResourceReader` from :code:`my_package.templates`.
From our example above, the :code:`ResourceReader` is able to see:
.. code::
>>> from importlib.util import find_spec
>>> package = 'my_package.templates'
>>> loader = find_spec(package).loader
>>> resource_reader = loader.get_resource_reader(package)
>>> contents = resource_reader.contents()
>>> print(list(contents))
['foo/test.html', 'test.html']
So we can use the provided :code:`resource_reader` to read either of those files:
.. code::
>>> with resource_reader.open_resource('foo/test.html') as file:
... content = file.read()
>>> print(content.decode('utf-8'))
FOO
Since, :code:`bar` is declared as module (directory contains a :code:`__init__.py` file), we need to use the :code:`ResourceReader` of the respective module:
.. code::
>>> resource_reader = loader.get_resource_reader('my_package.templates.bar')
>>> contents = resource_reader.contents()
>>> print(list(contents))
['test.html.jinja2']
The :code:`EmbeddedPackageLoader` will first try to find the resource in the :code:`ResourceReader` of the main package and then fallback to the :code:`ResourceReader` of the submodule (if it is declared as such).
Development
^^^^^^^^^^^
Install `rye <https://github.com/astral-sh/rye>`_, then run :code:`rye sync`. This creates a `venv <https://docs.python.org/3/library/venv.html>`_ with all necessary dependencies.
Run :code:`pytest` to run all tests.
To run the tests in a embedded Python version created with `PyOxidizer <https://github.com/indygreg/PyOxidizer>`_, run :code:`pyoxidizer run` in the root directory.
After the executable has been build, the tests will run automatically.
This repository used `ruff <https://github.com/astral-sh/ruff>`_ to enforce style standards. The formatting is automatically done for you via `pre-commit <https://pre-commit.com/>`_.
Install pre-commit with :code:`pre-commit install`.
Raw data
{
"_id": null,
"home_page": null,
"name": "jinja2-embedded",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "bundler, embedded, executable, jinja2, pyoxidizer, python",
"author": null,
"author_email": "GPla <36087062+GPla@users.noreply.github.com>",
"download_url": "https://files.pythonhosted.org/packages/c8/3b/756daf14b643b7211e8f536b537223dc8b83029ba141bfdfc2aa990bf7a8/jinja2_embedded-0.1.4.tar.gz",
"platform": null,
"description": "jinja2-embedded\n===============\n\n|pypi| |python| |pre-commit| |mypy| |codecov|\n\n.. |pypi| image:: https://badge.fury.io/py/jinja2-embedded.svg\n :target: https://pypi.org/project/jinja2-embedded/\n :alt: Latest Version\n\n.. |python| image:: https://img.shields.io/pypi/pyversions/jinja2-embedded\n :target: https://www.python.org/\n :alt: Supported Python Versions\n\n.. |pre-commit| image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white\n :target: https://github.com/pre-commit/pre-commit\n :alt: Pre-Commit enabled\n\n.. |mypy| image:: http://www.mypy-lang.org/static/mypy_badge.svg\n :target: http://mypy-lang.org/\n :alt: MyPy checked\n\n.. |codecov| image:: https://codecov.io/gh/GPla/jinja2-embedded/graph/badge.svg?token=FVA4W2KHR4\n :target: https://codecov.io/gh/GPla/jinja2-embedded\n :alt: Code Coverage\n\nTemplate loader for embedded python runtimes, e.g., `PyOxidizer <https://github.com/indygreg/PyOxidizer>`_ or `PyInstaller <https://github.com/pyinstaller/pyinstaller>`_.\n\nThe main problem with the current `PackageLoader <https://jinja.palletsprojects.com/en/3.0.x/api/#jinja2.PackageLoader>`_ is that it can only load templates from packages which are installed and materialized as directories.\nHowever, when using a bundler from above, the resources, i.e., templates, are embedded into the executable.\nThus, the :code:`PackageLoader` will throw the following exception: :code:`The package was not installed in a way that PackageLoader understands`.\n\nThe :code:`EmbeddedPackageLoader` from this package fixes this problem and required minimal changes.\nUnder the hood, we utilize the :code:`Loader` and :code:`ResourceReader` implementation of the package provided through `importlib <https://docs.python.org/3/library/importlib.html>`_.\nThereby, the :code:`EmbeddedPackageLoader` will work when the package is normally installed as directory and in an embedded environment.\n\nHow to use\n^^^^^^^^^^\n\nTwo changes are necessary.\nFirst, change :code:`PackageLoader` to :code:`EmbeddedPackageLoader`:\n\n.. code::\n\n from jinja2 import Environment, PackageLoader\n from jinja2_embedded import EmbeddedPackageLoader\n\n # before\n env = Environment(\n loader=PackageLoader('my_package', 'templates'),\n autoescape=True,\n ...\n )\n\n # after\n env = Environment(\n loader=EmbeddedPackageLoader('my_package.templates'),\n autoescape=True, # default False, but FastAPI uses True as default\n extensions=[],\n )\n\n # with FastAPI\n from fastapi.templating import Jinja2Templates\n templates = Jinja2Templates(env=env)\n\nSecond, declare the `templates` directory as a module by adding a :code:`__init__.py` file:\n\n.. code::\n\n my_package\n \u251c\u2500\u2500 __init__.py\n \u251c\u2500\u2500 main.py\n \u2514\u2500\u2500 templates\n \u251c\u2500\u2500 __init__.py # required\n \u251c\u2500\u2500 bar\n \u2502 \u251c\u2500\u2500 __init__.py # not required\n \u2502 \u2514\u2500\u2500 test.html.jinja2\n \u251c\u2500\u2500 foo\n \u2502 \u2514\u2500\u2500 test.html\n \u2514\u2500\u2500 test.html\n\n\nThe subdirectories inside the `templates` directory can be declared as modules (here :code:`my_package.templates.bar`), but this is not required.\nThe :code:`EmbeddedPackageLoader` works with either or mixed configuration.\n\nHow it works\n^^^^^^^^^^^^\n\nThe :code:`EmbeddedPackageLoader` will first try to locate the template with the :code:`ResourceReader` from :code:`my_package.templates`.\nFrom our example above, the :code:`ResourceReader` is able to see:\n\n.. code::\n\n >>> from importlib.util import find_spec\n >>> package = 'my_package.templates'\n >>> loader = find_spec(package).loader\n >>> resource_reader = loader.get_resource_reader(package)\n >>> contents = resource_reader.contents()\n >>> print(list(contents))\n ['foo/test.html', 'test.html']\n\n\nSo we can use the provided :code:`resource_reader` to read either of those files:\n\n.. code::\n\n >>> with resource_reader.open_resource('foo/test.html') as file:\n ... content = file.read()\n >>> print(content.decode('utf-8'))\n FOO\n\nSince, :code:`bar` is declared as module (directory contains a :code:`__init__.py` file), we need to use the :code:`ResourceReader` of the respective module:\n\n.. code::\n\n >>> resource_reader = loader.get_resource_reader('my_package.templates.bar')\n >>> contents = resource_reader.contents()\n >>> print(list(contents))\n ['test.html.jinja2']\n\nThe :code:`EmbeddedPackageLoader` will first try to find the resource in the :code:`ResourceReader` of the main package and then fallback to the :code:`ResourceReader` of the submodule (if it is declared as such).\n\nDevelopment\n^^^^^^^^^^^\n\nInstall `rye <https://github.com/astral-sh/rye>`_, then run :code:`rye sync`. This creates a `venv <https://docs.python.org/3/library/venv.html>`_ with all necessary dependencies.\nRun :code:`pytest` to run all tests.\n\nTo run the tests in a embedded Python version created with `PyOxidizer <https://github.com/indygreg/PyOxidizer>`_, run :code:`pyoxidizer run` in the root directory.\nAfter the executable has been build, the tests will run automatically.\n\nThis repository used `ruff <https://github.com/astral-sh/ruff>`_ to enforce style standards. The formatting is automatically done for you via `pre-commit <https://pre-commit.com/>`_.\nInstall pre-commit with :code:`pre-commit install`.\n",
"bugtrack_url": null,
"license": null,
"summary": "Jinja2 template loader for embedded Python runtimes",
"version": "0.1.4",
"project_urls": {
"Source": "https://github.com/GPla/jinja2-embedded"
},
"split_keywords": [
"bundler",
" embedded",
" executable",
" jinja2",
" pyoxidizer",
" python"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "b6fe187eb6222974ce2fe1b4c2ecc7162b320370dc7413c628e8a4e84d7d66ab",
"md5": "d721ce42a51b959551fe93283ba244ff",
"sha256": "2041bbdf1974f409539462976d5ae9bd1d034fb1d6eac228ac9cdddcfd3cd318"
},
"downloads": -1,
"filename": "jinja2_embedded-0.1.4-py3-none-any.whl",
"has_sig": false,
"md5_digest": "d721ce42a51b959551fe93283ba244ff",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 5980,
"upload_time": "2024-05-23T14:31:42",
"upload_time_iso_8601": "2024-05-23T14:31:42.682053Z",
"url": "https://files.pythonhosted.org/packages/b6/fe/187eb6222974ce2fe1b4c2ecc7162b320370dc7413c628e8a4e84d7d66ab/jinja2_embedded-0.1.4-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "c83b756daf14b643b7211e8f536b537223dc8b83029ba141bfdfc2aa990bf7a8",
"md5": "f8347baf3d1c4d45f28b9f0ee45fc9dc",
"sha256": "925803dff9a99b0e5ce998c7e69c8abde528282260395ed21113b808f82a7e21"
},
"downloads": -1,
"filename": "jinja2_embedded-0.1.4.tar.gz",
"has_sig": false,
"md5_digest": "f8347baf3d1c4d45f28b9f0ee45fc9dc",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 10738,
"upload_time": "2024-05-23T14:31:44",
"upload_time_iso_8601": "2024-05-23T14:31:44.441739Z",
"url": "https://files.pythonhosted.org/packages/c8/3b/756daf14b643b7211e8f536b537223dc8b83029ba141bfdfc2aa990bf7a8/jinja2_embedded-0.1.4.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-05-23 14:31:44",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "GPla",
"github_project": "jinja2-embedded",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "jinja2-embedded"
}