mirror of
https://github.com/mikeckennedy/fastapi-chameleon.git
synced 2026-03-13 10:32:00 +08:00
Sync with upstream branch 'main'.
Retained my changes. May need to rerun any linting that may have been reverted in exceptions.py, but functionality should now be retained. Signed-off-by: Ayzee Patton <vlpatton@vlpatton.gay>
This commit is contained in:
2
.idea/fastapi-chameleon.iml
generated
2
.idea/fastapi-chameleon.iml
generated
@@ -4,7 +4,7 @@
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.11 (fastapi-chameleon)" jdkType="Python SDK" />
|
||||
<orderEntry type="jdk" jdkName="Python 3.13 (fastapi-chameleon)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PyDocumentationSettings">
|
||||
|
||||
5
.idea/misc.xml
generated
5
.idea/misc.xml
generated
@@ -1,4 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11 (fastapi-chameleon)" project-jdk-type="Python SDK" />
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.13 (fastapi-chameleon)" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13 (fastapi-chameleon)" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
@@ -4,8 +4,8 @@ __version__ = '0.1.16'
|
||||
__author__ = 'Michael Kennedy <michael@talkpython.fm>'
|
||||
__all__ = ['template', 'global_init', 'not_found', 'response', 'generic_error', ]
|
||||
|
||||
from .engine import global_init
|
||||
from .engine import template
|
||||
from .engine import response
|
||||
from .engine import not_found
|
||||
from .engine import generic_error
|
||||
from .engine import global_init
|
||||
from .engine import not_found
|
||||
from .engine import response
|
||||
from .engine import template
|
||||
|
||||
@@ -6,8 +6,11 @@ from typing import Optional, Union, Callable
|
||||
import fastapi
|
||||
from chameleon import PageTemplateLoader, PageTemplate
|
||||
|
||||
from fastapi_chameleon.exceptions import FastAPIChameleonException, FastAPIChameleonGenericException, \
|
||||
FastAPIChameleonNotFoundException
|
||||
from fastapi_chameleon.exceptions import (
|
||||
FastAPIChameleonException,
|
||||
FastAPIChameleonGenericException,
|
||||
FastAPIChameleonNotFoundException,
|
||||
)
|
||||
|
||||
__templates: Optional[PageTemplateLoader] = None
|
||||
template_path: Optional[str] = None
|
||||
@@ -20,7 +23,7 @@ def global_init(template_folder: str, auto_reload=False, cache_init=True):
|
||||
return
|
||||
|
||||
if not template_folder:
|
||||
msg = f'The template_folder must be specified.'
|
||||
msg = 'The template_folder must be specified.'
|
||||
raise FastAPIChameleonException(msg)
|
||||
|
||||
if not os.path.isdir(template_folder):
|
||||
@@ -39,7 +42,7 @@ def clear():
|
||||
|
||||
def render(template_file: str, **template_data: dict) -> str:
|
||||
if not __templates:
|
||||
raise FastAPIChameleonException("You must call global_init() before rendering templates.")
|
||||
raise FastAPIChameleonException('You must call global_init() before rendering templates.')
|
||||
|
||||
page: PageTemplate = __templates[template_file]
|
||||
return page.render(encoding='utf-8', **template_data)
|
||||
@@ -119,7 +122,7 @@ def __render_response(template_file, response_val, mimetype, status_code: int =
|
||||
return response_val
|
||||
|
||||
if template_file and not isinstance(response_val, dict):
|
||||
msg = f"Invalid return type {type(response_val)}, we expected a dict or fastapi.Response as the return value."
|
||||
msg = f'Invalid return type {type(response_val)}, we expected a dict or fastapi.Response as the return value.'
|
||||
raise FastAPIChameleonException(msg)
|
||||
|
||||
model = response_val
|
||||
|
||||
42
ruff.toml
Normal file
42
ruff.toml
Normal file
@@ -0,0 +1,42 @@
|
||||
# [ruff]
|
||||
line-length = 120
|
||||
format.quote-style = "single"
|
||||
|
||||
# Enable Pyflakes `E` and `F` codes by default.
|
||||
select = ["E", "F"]
|
||||
ignore = []
|
||||
|
||||
# Exclude a variety of commonly ignored directories.
|
||||
exclude = [
|
||||
".bzr",
|
||||
".direnv",
|
||||
".eggs",
|
||||
".git",
|
||||
".hg",
|
||||
".mypy_cache",
|
||||
".nox",
|
||||
".pants.d",
|
||||
".ruff_cache",
|
||||
".svn",
|
||||
".tox",
|
||||
"__pypackages__",
|
||||
"_build",
|
||||
"buck-out",
|
||||
"build",
|
||||
"dist",
|
||||
"node_modules",
|
||||
".env",
|
||||
".venv",
|
||||
"venv",
|
||||
]
|
||||
per-file-ignores = {}
|
||||
|
||||
# Allow unused variables when underscore-prefixed.
|
||||
# dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||
|
||||
# Assume Python 3.13.
|
||||
target-version = "py313"
|
||||
|
||||
#[tool.ruff.mccabe]
|
||||
## Unlike Flake8, default to a complexity level of 10.
|
||||
mccabe.max-complexity = 10
|
||||
7
setup.py
7
setup.py
@@ -55,14 +55,13 @@ setup(
|
||||
install_requires=requires,
|
||||
|
||||
classifiers=[
|
||||
'Development Status :: 2 - Pre-Alpha',
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'License :: OSI Approved :: MIT License',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Programming Language :: Python :: 3.8',
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Programming Language :: Python :: 3.13',
|
||||
],
|
||||
)
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
import fastapi_chameleon as fc
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def test_templates_path(pytestconfig):
|
||||
return Path(pytestconfig.rootdir, "tests", "templates")
|
||||
return Path(pytestconfig.rootdir, 'tests', 'templates')
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
@@ -5,6 +5,6 @@
|
||||
<title>Page Not Found</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>This is a pretty 404 page.</h1>
|
||||
<h1>This is a pretty 404 page.</h1>
|
||||
</body>
|
||||
</html>
|
||||
@@ -5,6 +5,6 @@
|
||||
<title>Page Not Found</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Another pretty 404 page.</h1>
|
||||
<h1>Another pretty 404 page.</h1>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,12 +1,12 @@
|
||||
<html>
|
||||
<body>
|
||||
<h1>Hello, ${'world'}!</h1>
|
||||
<table>
|
||||
<tr tal:repeat="row 'apple', 'banana', 'pineapple'">
|
||||
<body>
|
||||
<h1>Hello, ${'world'}!</h1>
|
||||
<table>
|
||||
<tr tal:repeat="row 'apple', 'banana', 'pineapple'">
|
||||
<td tal:repeat="col 'juice', 'muffin', 'pie'">
|
||||
${row.capitalize()} ${col}
|
||||
${row.capitalize()} ${col}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,12 +1,12 @@
|
||||
<html>
|
||||
<body>
|
||||
<h1>Hello default ${world}!</h1>
|
||||
<table>
|
||||
<tr tal:repeat="row 'apple', 'banana', 'pineapple'">
|
||||
<body>
|
||||
<h1>Hello default ${world}!</h1>
|
||||
<table>
|
||||
<tr tal:repeat="row 'apple', 'banana', 'pineapple'">
|
||||
<td tal:repeat="col 'juice', 'muffin', 'pie'">
|
||||
${row.capitalize()} ${col}
|
||||
${row.capitalize()} ${col}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,12 +1,12 @@
|
||||
<html>
|
||||
<body>
|
||||
<h1>Hello default ${world}!</h1>
|
||||
<table>
|
||||
<tr tal:repeat="row 'apple', 'banana', 'pineapple'">
|
||||
<body>
|
||||
<h1>Hello default ${world}!</h1>
|
||||
<table>
|
||||
<tr tal:repeat="row 'apple', 'banana', 'pineapple'">
|
||||
<td tal:repeat="col 'juice', 'muffin', 'pie'">
|
||||
${row.capitalize()} ${col}
|
||||
${row.capitalize()} ${col}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
@@ -10,17 +10,20 @@ import fastapi_chameleon as fc
|
||||
# setup_global_template - needed as pytest mix-in.
|
||||
# noinspection PyUnusedLocal
|
||||
@pytest.mark.parametrize(
|
||||
("status_code", "template_file", "expected_h1_in_body"),
|
||||
('status_code', 'template_file', 'expected_h1_in_body'),
|
||||
[
|
||||
(fastapi.status.HTTP_400_BAD_REQUEST, "errors/404.pt", b'<h1>This is a pretty 404 page.</h1>'),
|
||||
(fastapi.status.HTTP_401_UNAUTHORIZED, "errors/other_error_page.pt", b'<h1>Another pretty 404 page.</h1>'),
|
||||
(fastapi.status.HTTP_403_FORBIDDEN, "errors/404.pt", b'<h1>This is a pretty 404 page.</h1>'),
|
||||
(fastapi.status.HTTP_404_NOT_FOUND, "errors/other_error_page.pt", b'<h1>Another pretty 404 page.</h1>'),
|
||||
(fastapi.status.HTTP_405_METHOD_NOT_ALLOWED, "errors/404.pt", b'<h1>This is a pretty 404 page.</h1>'),
|
||||
(fastapi.status.HTTP_406_NOT_ACCEPTABLE, "errors/other_error_page.pt", b'<h1>Another pretty 404 page.</h1>'),
|
||||
(fastapi.status.HTTP_407_PROXY_AUTHENTICATION_REQUIRED, "errors/404.pt",
|
||||
b'<h1>This is a pretty 404 page.</h1>'),
|
||||
(fastapi.status.HTTP_408_REQUEST_TIMEOUT, "errors/other_error_page.pt", b'<h1>Another pretty 404 page.</h1>'),
|
||||
(fastapi.status.HTTP_400_BAD_REQUEST, 'errors/404.pt', b'<h1>This is a pretty 404 page.</h1>'),
|
||||
(fastapi.status.HTTP_401_UNAUTHORIZED, 'errors/other_error_page.pt', b'<h1>Another pretty 404 page.</h1>'),
|
||||
(fastapi.status.HTTP_403_FORBIDDEN, 'errors/404.pt', b'<h1>This is a pretty 404 page.</h1>'),
|
||||
(fastapi.status.HTTP_404_NOT_FOUND, 'errors/other_error_page.pt', b'<h1>Another pretty 404 page.</h1>'),
|
||||
(fastapi.status.HTTP_405_METHOD_NOT_ALLOWED, 'errors/404.pt', b'<h1>This is a pretty 404 page.</h1>'),
|
||||
(fastapi.status.HTTP_406_NOT_ACCEPTABLE, 'errors/other_error_page.pt', b'<h1>Another pretty 404 page.</h1>'),
|
||||
(
|
||||
fastapi.status.HTTP_407_PROXY_AUTHENTICATION_REQUIRED,
|
||||
'errors/404.pt',
|
||||
b'<h1>This is a pretty 404 page.</h1>',
|
||||
),
|
||||
(fastapi.status.HTTP_408_REQUEST_TIMEOUT, 'errors/other_error_page.pt', b'<h1>Another pretty 404 page.</h1>'),
|
||||
],
|
||||
)
|
||||
def test_friendly_403_sync_method(setup_global_template, status_code, template_file, expected_h1_in_body):
|
||||
@@ -38,17 +41,20 @@ def test_friendly_403_sync_method(setup_global_template, status_code, template_f
|
||||
# setup_global_template - needed as pytest mix-in.
|
||||
# noinspection PyUnusedLocal
|
||||
@pytest.mark.parametrize(
|
||||
("status_code", "template_file", "expected_h1_in_body"),
|
||||
('status_code', 'template_file', 'expected_h1_in_body'),
|
||||
[
|
||||
(fastapi.status.HTTP_400_BAD_REQUEST, "errors/404.pt", b'<h1>This is a pretty 404 page.</h1>'),
|
||||
(fastapi.status.HTTP_401_UNAUTHORIZED, "errors/other_error_page.pt", b'<h1>Another pretty 404 page.</h1>'),
|
||||
(fastapi.status.HTTP_403_FORBIDDEN, "errors/404.pt", b'<h1>This is a pretty 404 page.</h1>'),
|
||||
(fastapi.status.HTTP_404_NOT_FOUND, "errors/other_error_page.pt", b'<h1>Another pretty 404 page.</h1>'),
|
||||
(fastapi.status.HTTP_405_METHOD_NOT_ALLOWED, "errors/404.pt", b'<h1>This is a pretty 404 page.</h1>'),
|
||||
(fastapi.status.HTTP_406_NOT_ACCEPTABLE, "errors/other_error_page.pt", b'<h1>Another pretty 404 page.</h1>'),
|
||||
(fastapi.status.HTTP_407_PROXY_AUTHENTICATION_REQUIRED, "errors/404.pt",
|
||||
b'<h1>This is a pretty 404 page.</h1>'),
|
||||
(fastapi.status.HTTP_408_REQUEST_TIMEOUT, "errors/other_error_page.pt", b'<h1>Another pretty 404 page.</h1>'),
|
||||
(fastapi.status.HTTP_400_BAD_REQUEST, 'errors/404.pt', b'<h1>This is a pretty 404 page.</h1>'),
|
||||
(fastapi.status.HTTP_401_UNAUTHORIZED, 'errors/other_error_page.pt', b'<h1>Another pretty 404 page.</h1>'),
|
||||
(fastapi.status.HTTP_403_FORBIDDEN, 'errors/404.pt', b'<h1>This is a pretty 404 page.</h1>'),
|
||||
(fastapi.status.HTTP_404_NOT_FOUND, 'errors/other_error_page.pt', b'<h1>Another pretty 404 page.</h1>'),
|
||||
(fastapi.status.HTTP_405_METHOD_NOT_ALLOWED, 'errors/404.pt', b'<h1>This is a pretty 404 page.</h1>'),
|
||||
(fastapi.status.HTTP_406_NOT_ACCEPTABLE, 'errors/other_error_page.pt', b'<h1>Another pretty 404 page.</h1>'),
|
||||
(
|
||||
fastapi.status.HTTP_407_PROXY_AUTHENTICATION_REQUIRED,
|
||||
'errors/404.pt',
|
||||
b'<h1>This is a pretty 404 page.</h1>',
|
||||
),
|
||||
(fastapi.status.HTTP_408_REQUEST_TIMEOUT, 'errors/other_error_page.pt', b'<h1>Another pretty 404 page.</h1>'),
|
||||
],
|
||||
)
|
||||
def test_friendly_403_async_method(setup_global_template, status_code, template_file, expected_h1_in_body):
|
||||
|
||||
@@ -8,9 +8,10 @@ def test_cannot_decorate_with_missing_init():
|
||||
fc.engine.clear()
|
||||
|
||||
with pytest.raises(FastAPIChameleonException):
|
||||
|
||||
@fc.template('home/index.pt')
|
||||
def view_method(a, b, c):
|
||||
return {"a": a, "b": b, "c": c}
|
||||
return {'a': a, 'b': b, 'c': c}
|
||||
|
||||
view_method(1, 2, 3)
|
||||
|
||||
@@ -23,6 +24,6 @@ def test_can_call_init_with_good_path(test_templates_path):
|
||||
|
||||
|
||||
def test_cannot_call_init_with_bad_path(test_templates_path):
|
||||
bad_path = test_templates_path / "missing"
|
||||
bad_path = test_templates_path / 'missing'
|
||||
with pytest.raises(Exception):
|
||||
fc.global_init(str(bad_path), cache_init=False)
|
||||
|
||||
@@ -11,6 +11,7 @@ import fastapi_chameleon as fc
|
||||
# noinspection PyUnusedLocal
|
||||
def test_cannot_decorate_missing_template(setup_global_template):
|
||||
with pytest.raises(ValueError):
|
||||
|
||||
@fc.template('home/missing.pt')
|
||||
def view_method():
|
||||
return {}
|
||||
@@ -22,6 +23,7 @@ def test_cannot_decorate_missing_template(setup_global_template):
|
||||
# noinspection PyUnusedLocal
|
||||
def test_requires_template_for_default_name(setup_global_template):
|
||||
with pytest.raises(ValueError):
|
||||
|
||||
@fc.template(None)
|
||||
def view_method():
|
||||
return {}
|
||||
@@ -99,4 +101,4 @@ def test_direct_response_pass_through():
|
||||
resp = view_method(1, 2, 3)
|
||||
assert isinstance(resp, fastapi.Response)
|
||||
assert resp.status_code == 418
|
||||
assert resp.body == b"abc"
|
||||
assert resp.body == b'abc'
|
||||
|
||||
Reference in New Issue
Block a user