![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"
}