jinja2-embedded


Namejinja2-embedded JSON
Version 0.1.4 PyPI version JSON
download
home_pageNone
SummaryJinja2 template loader for embedded Python runtimes
upload_time2024-05-23 14:31:44
maintainerNone
docs_urlNone
authorNone
requires_python>=3.10
licenseNone
keywords bundler embedded executable jinja2 pyoxidizer python
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            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"
}
        
Elapsed time: 0.23303s