flask-themer


Nameflask-themer JSON
Version 2.0.0 PyPI version JSON
download
home_pagehttps://github.com/tktech/flask-themer
SummarySimple theme mechanism for Flask
upload_time2023-06-06 05:05:45
maintainer
docs_urlNone
authorTyler Kennedy
requires_python>=3.8
license
keywords flask themes jinja2
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            ![PyPI](https://img.shields.io/pypi/v/flask-themer?style=flat-square)
![PyPI - License](https://img.shields.io/pypi/l/flask-themer?style=flat-square)
![GitHub Workflow Status](https://img.shields.io/github/workflow/status/TkTech/flask-themer/Run%20tests?style=flat-square)


# Flask-Themer

Simple theme support for flask apps.

Flask-Themer is inspired by the (seemingly) abandoned [flask-themes][] project,
but has been written from scratch for Python 3.8+. However, it is _not_ immediately
compatible with flask-themes and does not seek to be. Flask-Themer tries to have
little opinion on how you actually structure your project and its themes and does
not require a particular metadata format/file.

Flask-Themer releases follow [Semantic Versioning][semver].
Flask-Themer has 100% test coverage and considers it an error to fall below
100%.

## Installation

Install the latest release from [PyPi][]:

```
pip install flask-themer
```

or get the latest development version from GitHub:

```
git clone https://github.com/TkTech/flask-themer.git
cd flask-themer
python setup.py develop
```

## Quickstart


Flask-Themer usage is usually very basic, and once setup you likely won't need
to touch it again. Let's do a quickstart. Notice how we import `render_template`
from `flask_themer` instead of `flask`.


Our `app.py` looks like this:

```python
from flask import Flask
from flask_themer import Themer, render_template

app = Flask(__name__)
themer = Themer(app)


@themer.current_theme_loader
def get_current_theme():
    # This is where you would look up the current user's theme if one was
    # logged in, for example.
    return 'default'

@app.route('/')
def hello_world():
    return render_template('hello.html')
```

And next to it we have a directory called `themes` with a directory called
`default` inside of it. Our `themes/default/hello.html` looks like this:


```jinja2
Hello world!
```

That's it! By default Flask-Themer will look for a `themes` directory next to
your project and assume all the directories inside of it are themes. You can
change what directory it looks for with `THEMER_DEFAULT_DIRECTORY`, or specify
the template loaders explicitly to overwrite the default:

```python
from flask_themer import Themer, FileSystemThemeLoader

app = Flask(__name__)
themer = Themer(app, loaders=[
    FileSystemThemeLoader(app, os.path.join(
        app.root_path,
        'also_themes'
    ))
])
```

## Using Themes From Templates

Two template globals are added once Flask-Themer is setup, `theme()` and
`theme_static()` (just like flask-themes). These methods look up the currently
active theme and look for the given path in that theme, returning a special
path that Jinja can use to load it.

```jinja2
{% extends theme("base.html") %}

{% block header %}
    {{ super() }}
    <link rel="stylesheet" href="{{ theme_static("bootstrap.css") }}">
{% endblock %}
```

Themes can also extend other themes using the `theme` argument:

```jinja2
{% extends theme("base.html", theme="my_parent_theme") %}
```


## Theme Loaders

_Theme_ loaders are the mechanism by which Flask-Themer discovers what themes
are available. You can create a custom loader to get themes from a ZIP file, or
a database for example. Usually if you create a new `ThemeLoader` you'll also
need to create a new Jinja [_template_ loader][loader] so Jinja knows how to
read individual templates. Lets do a very minimal example that loads just a
single theme from a ZIP file.


```python
from zipfile import ZipFile
from flask_themer import ThemeLoader, Theme
from jinja2.loaders import BaseLoader, TemplateNotFound

class ZipFileTemplateLoader(BaseLoader):
    def __init__(self, *args, archive, **kwargs):
        super().__init__(*args, **kwargs)
        self.archive = archive

    def get_source(self, environment, template):
        try:
            return (self.archive.read(template), None, False)
        except KeyError:
            raise TemplateNotFound(template)


class ZipFileThemeLoader(ThemeLoader):
    def __init__(self, path_to_zip):
        self.archive = ZipFile(path_to_zip)

    @property
    def themes(self):
        yield Theme(
            name='my_dumb_theme',
            theme_loader=self,
            jinja_loader=ZipFileTemplateLoader(archive=self.archive),
        )

    def get_static(self, theme, path):
        return self.archive.read(path)
```

And then to use our new loaders we update our previous example:

```python
...
themer = Themer(app, loaders=[
    ZipFileThemeLoader('my_dumb_theme.zip')
])
...
```

Pretty simple right? You can see how we could easily create a loader to load
multiple themes from an archive, or load a user's customized theme from a
database.

[flask-themes]: https://github.com/maxcountryman/flask-themes
[pypi]: https://pypi.org/
[semver]: https://semver.org/
[loader]: https://jinja.palletsprojects.com/en/latest/api/#loaders



            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/tktech/flask-themer",
    "name": "flask-themer",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": "",
    "keywords": "flask,themes,jinja2",
    "author": "Tyler Kennedy",
    "author_email": "tk@tkte.ch",
    "download_url": "https://files.pythonhosted.org/packages/46/d5/af8aad885ea0b96425adda4c24f2f2e30c12770d478f27b2f3895bacf184/flask-themer-2.0.0.tar.gz",
    "platform": null,
    "description": "![PyPI](https://img.shields.io/pypi/v/flask-themer?style=flat-square)\n![PyPI - License](https://img.shields.io/pypi/l/flask-themer?style=flat-square)\n![GitHub Workflow Status](https://img.shields.io/github/workflow/status/TkTech/flask-themer/Run%20tests?style=flat-square)\n\n\n# Flask-Themer\n\nSimple theme support for flask apps.\n\nFlask-Themer is inspired by the (seemingly) abandoned [flask-themes][] project,\nbut has been written from scratch for Python 3.8+. However, it is _not_ immediately\ncompatible with flask-themes and does not seek to be. Flask-Themer tries to have\nlittle opinion on how you actually structure your project and its themes and does\nnot require a particular metadata format/file.\n\nFlask-Themer releases follow [Semantic Versioning][semver].\nFlask-Themer has 100% test coverage and considers it an error to fall below\n100%.\n\n## Installation\n\nInstall the latest release from [PyPi][]:\n\n```\npip install flask-themer\n```\n\nor get the latest development version from GitHub:\n\n```\ngit clone https://github.com/TkTech/flask-themer.git\ncd flask-themer\npython setup.py develop\n```\n\n## Quickstart\n\n\nFlask-Themer usage is usually very basic, and once setup you likely won't need\nto touch it again. Let's do a quickstart. Notice how we import `render_template`\nfrom `flask_themer` instead of `flask`.\n\n\nOur `app.py` looks like this:\n\n```python\nfrom flask import Flask\nfrom flask_themer import Themer, render_template\n\napp = Flask(__name__)\nthemer = Themer(app)\n\n\n@themer.current_theme_loader\ndef get_current_theme():\n    # This is where you would look up the current user's theme if one was\n    # logged in, for example.\n    return 'default'\n\n@app.route('/')\ndef hello_world():\n    return render_template('hello.html')\n```\n\nAnd next to it we have a directory called `themes` with a directory called\n`default` inside of it. Our `themes/default/hello.html` looks like this:\n\n\n```jinja2\nHello world!\n```\n\nThat's it! By default Flask-Themer will look for a `themes` directory next to\nyour project and assume all the directories inside of it are themes. You can\nchange what directory it looks for with `THEMER_DEFAULT_DIRECTORY`, or specify\nthe template loaders explicitly to overwrite the default:\n\n```python\nfrom flask_themer import Themer, FileSystemThemeLoader\n\napp = Flask(__name__)\nthemer = Themer(app, loaders=[\n    FileSystemThemeLoader(app, os.path.join(\n        app.root_path,\n        'also_themes'\n    ))\n])\n```\n\n## Using Themes From Templates\n\nTwo template globals are added once Flask-Themer is setup, `theme()` and\n`theme_static()` (just like flask-themes). These methods look up the currently\nactive theme and look for the given path in that theme, returning a special\npath that Jinja can use to load it.\n\n```jinja2\n{% extends theme(\"base.html\") %}\n\n{% block header %}\n    {{ super() }}\n    <link rel=\"stylesheet\" href=\"{{ theme_static(\"bootstrap.css\") }}\">\n{% endblock %}\n```\n\nThemes can also extend other themes using the `theme` argument:\n\n```jinja2\n{% extends theme(\"base.html\", theme=\"my_parent_theme\") %}\n```\n\n\n## Theme Loaders\n\n_Theme_ loaders are the mechanism by which Flask-Themer discovers what themes\nare available. You can create a custom loader to get themes from a ZIP file, or\na database for example. Usually if you create a new `ThemeLoader` you'll also\nneed to create a new Jinja [_template_ loader][loader] so Jinja knows how to\nread individual templates. Lets do a very minimal example that loads just a\nsingle theme from a ZIP file.\n\n\n```python\nfrom zipfile import ZipFile\nfrom flask_themer import ThemeLoader, Theme\nfrom jinja2.loaders import BaseLoader, TemplateNotFound\n\nclass ZipFileTemplateLoader(BaseLoader):\n    def __init__(self, *args, archive, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.archive = archive\n\n    def get_source(self, environment, template):\n        try:\n            return (self.archive.read(template), None, False)\n        except KeyError:\n            raise TemplateNotFound(template)\n\n\nclass ZipFileThemeLoader(ThemeLoader):\n    def __init__(self, path_to_zip):\n        self.archive = ZipFile(path_to_zip)\n\n    @property\n    def themes(self):\n        yield Theme(\n            name='my_dumb_theme',\n            theme_loader=self,\n            jinja_loader=ZipFileTemplateLoader(archive=self.archive),\n        )\n\n    def get_static(self, theme, path):\n        return self.archive.read(path)\n```\n\nAnd then to use our new loaders we update our previous example:\n\n```python\n...\nthemer = Themer(app, loaders=[\n    ZipFileThemeLoader('my_dumb_theme.zip')\n])\n...\n```\n\nPretty simple right? You can see how we could easily create a loader to load\nmultiple themes from an archive, or load a user's customized theme from a\ndatabase.\n\n[flask-themes]: https://github.com/maxcountryman/flask-themes\n[pypi]: https://pypi.org/\n[semver]: https://semver.org/\n[loader]: https://jinja.palletsprojects.com/en/latest/api/#loaders\n\n\n",
    "bugtrack_url": null,
    "license": "",
    "summary": "Simple theme mechanism for Flask",
    "version": "2.0.0",
    "project_urls": {
        "Homepage": "https://github.com/tktech/flask-themer"
    },
    "split_keywords": [
        "flask",
        "themes",
        "jinja2"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "4d95e5640f821b07f230423b5ff642f8db76caf8aa0cf59202d4242f60ca173f",
                "md5": "df50cbc27b314db028da9f564a0a856a",
                "sha256": "bea76d0833a7a4db217822327b1d53604e34320123f03fd30686a311beabc9f0"
            },
            "downloads": -1,
            "filename": "flask_themer-2.0.0-py3.8.egg",
            "has_sig": false,
            "md5_digest": "df50cbc27b314db028da9f564a0a856a",
            "packagetype": "bdist_egg",
            "python_version": "2.0.0",
            "requires_python": ">=3.8",
            "size": 10094,
            "upload_time": "2023-06-06T05:05:46",
            "upload_time_iso_8601": "2023-06-06T05:05:46.894860Z",
            "url": "https://files.pythonhosted.org/packages/4d/95/e5640f821b07f230423b5ff642f8db76caf8aa0cf59202d4242f60ca173f/flask_themer-2.0.0-py3.8.egg",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "f3ed806e4e8079ea0a7e9b0df3fb6e45af97c0377924cfbd664c0f1353e88144",
                "md5": "0ac1f484d530e648fab15f840f28436f",
                "sha256": "e34cc5bccf4fafb7d04f5de6e2219cc001f6485f7b0640dc5b728f8de88c2b57"
            },
            "downloads": -1,
            "filename": "flask_themer-2.0.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "0ac1f484d530e648fab15f840f28436f",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 6939,
            "upload_time": "2023-06-06T05:05:44",
            "upload_time_iso_8601": "2023-06-06T05:05:44.346166Z",
            "url": "https://files.pythonhosted.org/packages/f3/ed/806e4e8079ea0a7e9b0df3fb6e45af97c0377924cfbd664c0f1353e88144/flask_themer-2.0.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "46d5af8aad885ea0b96425adda4c24f2f2e30c12770d478f27b2f3895bacf184",
                "md5": "1df2ca7e127536b516add3fb600d22a9",
                "sha256": "c8dbea370ad88d9d13e5d0c360f9678bb219bfbab7a7d0cb4f00ff89ac09a275"
            },
            "downloads": -1,
            "filename": "flask-themer-2.0.0.tar.gz",
            "has_sig": false,
            "md5_digest": "1df2ca7e127536b516add3fb600d22a9",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 7025,
            "upload_time": "2023-06-06T05:05:45",
            "upload_time_iso_8601": "2023-06-06T05:05:45.491943Z",
            "url": "https://files.pythonhosted.org/packages/46/d5/af8aad885ea0b96425adda4c24f2f2e30c12770d478f27b2f3895bacf184/flask-themer-2.0.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-06-06 05:05:45",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "tktech",
    "github_project": "flask-themer",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "flask-themer"
}
        
Elapsed time: 0.08718s