plain 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plain/README.md +33 -0
- plain/__main__.py +5 -0
- plain/assets/README.md +56 -0
- plain/assets/__init__.py +6 -0
- plain/assets/finders.py +233 -0
- plain/assets/preflight.py +14 -0
- plain/assets/storage.py +916 -0
- plain/assets/utils.py +52 -0
- plain/assets/whitenoise/__init__.py +5 -0
- plain/assets/whitenoise/base.py +259 -0
- plain/assets/whitenoise/compress.py +189 -0
- plain/assets/whitenoise/media_types.py +137 -0
- plain/assets/whitenoise/middleware.py +197 -0
- plain/assets/whitenoise/responders.py +286 -0
- plain/assets/whitenoise/storage.py +178 -0
- plain/assets/whitenoise/string_utils.py +13 -0
- plain/cli/README.md +123 -0
- plain/cli/__init__.py +3 -0
- plain/cli/cli.py +439 -0
- plain/cli/formatting.py +61 -0
- plain/cli/packages.py +73 -0
- plain/cli/print.py +9 -0
- plain/cli/startup.py +33 -0
- plain/csrf/README.md +3 -0
- plain/csrf/middleware.py +466 -0
- plain/csrf/views.py +10 -0
- plain/debug.py +23 -0
- plain/exceptions.py +242 -0
- plain/forms/README.md +14 -0
- plain/forms/__init__.py +8 -0
- plain/forms/boundfield.py +58 -0
- plain/forms/exceptions.py +11 -0
- plain/forms/fields.py +1030 -0
- plain/forms/forms.py +297 -0
- plain/http/README.md +1 -0
- plain/http/__init__.py +51 -0
- plain/http/cookie.py +20 -0
- plain/http/multipartparser.py +743 -0
- plain/http/request.py +754 -0
- plain/http/response.py +719 -0
- plain/internal/__init__.py +0 -0
- plain/internal/files/README.md +3 -0
- plain/internal/files/__init__.py +3 -0
- plain/internal/files/base.py +161 -0
- plain/internal/files/locks.py +127 -0
- plain/internal/files/move.py +102 -0
- plain/internal/files/temp.py +79 -0
- plain/internal/files/uploadedfile.py +150 -0
- plain/internal/files/uploadhandler.py +254 -0
- plain/internal/files/utils.py +78 -0
- plain/internal/handlers/__init__.py +0 -0
- plain/internal/handlers/base.py +133 -0
- plain/internal/handlers/exception.py +145 -0
- plain/internal/handlers/wsgi.py +216 -0
- plain/internal/legacy/__init__.py +0 -0
- plain/internal/legacy/__main__.py +12 -0
- plain/internal/legacy/management/__init__.py +414 -0
- plain/internal/legacy/management/base.py +692 -0
- plain/internal/legacy/management/color.py +113 -0
- plain/internal/legacy/management/commands/__init__.py +0 -0
- plain/internal/legacy/management/commands/collectstatic.py +297 -0
- plain/internal/legacy/management/sql.py +67 -0
- plain/internal/legacy/management/utils.py +175 -0
- plain/json.py +40 -0
- plain/logs/README.md +24 -0
- plain/logs/__init__.py +5 -0
- plain/logs/configure.py +39 -0
- plain/logs/loggers.py +74 -0
- plain/logs/utils.py +46 -0
- plain/middleware/README.md +3 -0
- plain/middleware/__init__.py +0 -0
- plain/middleware/clickjacking.py +52 -0
- plain/middleware/common.py +87 -0
- plain/middleware/gzip.py +64 -0
- plain/middleware/security.py +64 -0
- plain/packages/README.md +41 -0
- plain/packages/__init__.py +4 -0
- plain/packages/config.py +259 -0
- plain/packages/registry.py +438 -0
- plain/paginator.py +187 -0
- plain/preflight/README.md +3 -0
- plain/preflight/__init__.py +38 -0
- plain/preflight/compatibility/__init__.py +0 -0
- plain/preflight/compatibility/django_4_0.py +20 -0
- plain/preflight/files.py +19 -0
- plain/preflight/messages.py +88 -0
- plain/preflight/registry.py +72 -0
- plain/preflight/security/__init__.py +0 -0
- plain/preflight/security/base.py +268 -0
- plain/preflight/security/csrf.py +40 -0
- plain/preflight/urls.py +117 -0
- plain/runtime/README.md +75 -0
- plain/runtime/__init__.py +61 -0
- plain/runtime/global_settings.py +199 -0
- plain/runtime/user_settings.py +353 -0
- plain/signals/README.md +14 -0
- plain/signals/__init__.py +5 -0
- plain/signals/dispatch/__init__.py +9 -0
- plain/signals/dispatch/dispatcher.py +320 -0
- plain/signals/dispatch/license.txt +35 -0
- plain/signing.py +299 -0
- plain/templates/README.md +20 -0
- plain/templates/__init__.py +6 -0
- plain/templates/core.py +24 -0
- plain/templates/jinja/README.md +227 -0
- plain/templates/jinja/__init__.py +22 -0
- plain/templates/jinja/defaults.py +119 -0
- plain/templates/jinja/extensions.py +39 -0
- plain/templates/jinja/filters.py +28 -0
- plain/templates/jinja/globals.py +19 -0
- plain/test/README.md +3 -0
- plain/test/__init__.py +16 -0
- plain/test/client.py +985 -0
- plain/test/utils.py +255 -0
- plain/urls/README.md +3 -0
- plain/urls/__init__.py +40 -0
- plain/urls/base.py +118 -0
- plain/urls/conf.py +94 -0
- plain/urls/converters.py +66 -0
- plain/urls/exceptions.py +9 -0
- plain/urls/resolvers.py +731 -0
- plain/utils/README.md +3 -0
- plain/utils/__init__.py +0 -0
- plain/utils/_os.py +52 -0
- plain/utils/cache.py +327 -0
- plain/utils/connection.py +84 -0
- plain/utils/crypto.py +76 -0
- plain/utils/datastructures.py +345 -0
- plain/utils/dateformat.py +329 -0
- plain/utils/dateparse.py +154 -0
- plain/utils/dates.py +76 -0
- plain/utils/deconstruct.py +54 -0
- plain/utils/decorators.py +90 -0
- plain/utils/deprecation.py +6 -0
- plain/utils/duration.py +44 -0
- plain/utils/email.py +12 -0
- plain/utils/encoding.py +235 -0
- plain/utils/functional.py +456 -0
- plain/utils/hashable.py +26 -0
- plain/utils/html.py +401 -0
- plain/utils/http.py +374 -0
- plain/utils/inspect.py +73 -0
- plain/utils/ipv6.py +46 -0
- plain/utils/itercompat.py +8 -0
- plain/utils/module_loading.py +69 -0
- plain/utils/regex_helper.py +353 -0
- plain/utils/safestring.py +72 -0
- plain/utils/termcolors.py +221 -0
- plain/utils/text.py +518 -0
- plain/utils/timesince.py +138 -0
- plain/utils/timezone.py +244 -0
- plain/utils/tree.py +126 -0
- plain/validators.py +603 -0
- plain/views/README.md +268 -0
- plain/views/__init__.py +18 -0
- plain/views/base.py +107 -0
- plain/views/csrf.py +24 -0
- plain/views/errors.py +25 -0
- plain/views/exceptions.py +4 -0
- plain/views/forms.py +76 -0
- plain/views/objects.py +229 -0
- plain/views/redirect.py +72 -0
- plain/views/templates.py +66 -0
- plain/wsgi.py +11 -0
- plain-0.1.0.dist-info/LICENSE +85 -0
- plain-0.1.0.dist-info/METADATA +51 -0
- plain-0.1.0.dist-info/RECORD +169 -0
- plain-0.1.0.dist-info/WHEEL +4 -0
- plain-0.1.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
from importlib import import_module
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from jinja2 import Environment, StrictUndefined
|
|
6
|
+
|
|
7
|
+
from plain.packages import packages
|
|
8
|
+
from plain.runtime import settings
|
|
9
|
+
from plain.utils.module_loading import import_string, module_has_submodule
|
|
10
|
+
|
|
11
|
+
from .filters import default_filters
|
|
12
|
+
from .globals import default_globals
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@functools.lru_cache
|
|
16
|
+
def _get_app_template_dirs():
|
|
17
|
+
"""
|
|
18
|
+
Return an iterable of paths of directories to load app templates from.
|
|
19
|
+
|
|
20
|
+
dirname is the name of the subdirectory containing templates inside
|
|
21
|
+
installed applications.
|
|
22
|
+
"""
|
|
23
|
+
dirname = "templates"
|
|
24
|
+
template_dirs = [
|
|
25
|
+
Path(package_config.path) / dirname
|
|
26
|
+
for package_config in packages.get_package_configs()
|
|
27
|
+
if package_config.path and (Path(package_config.path) / dirname).is_dir()
|
|
28
|
+
]
|
|
29
|
+
# Immutable return value because it will be cached and shared by callers.
|
|
30
|
+
return tuple(template_dirs)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _get_installed_extensions() -> tuple[list, dict, dict]:
|
|
34
|
+
"""Automatically load extensions, globals, filters from INSTALLED_PACKAGES jinja module and root jinja module"""
|
|
35
|
+
extensions = []
|
|
36
|
+
globals = {}
|
|
37
|
+
filters = {}
|
|
38
|
+
|
|
39
|
+
for package_config in packages.get_package_configs():
|
|
40
|
+
if module_has_submodule(package_config.module, "jinja"):
|
|
41
|
+
module = import_module(f"{package_config.name}.jinja")
|
|
42
|
+
else:
|
|
43
|
+
continue
|
|
44
|
+
|
|
45
|
+
if hasattr(module, "extensions"):
|
|
46
|
+
extensions.extend(module.extensions)
|
|
47
|
+
|
|
48
|
+
if hasattr(module, "globals"):
|
|
49
|
+
globals.update(module.globals)
|
|
50
|
+
|
|
51
|
+
if hasattr(module, "filters"):
|
|
52
|
+
filters.update(module.filters)
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
import jinja
|
|
56
|
+
|
|
57
|
+
if hasattr(jinja, "extensions"):
|
|
58
|
+
extensions.extend(jinja.extensions)
|
|
59
|
+
|
|
60
|
+
if hasattr(jinja, "globals"):
|
|
61
|
+
globals.update(jinja.globals)
|
|
62
|
+
|
|
63
|
+
if hasattr(jinja, "filters"):
|
|
64
|
+
filters.update(jinja.filters)
|
|
65
|
+
except ImportError:
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
return extensions, globals, filters
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def finalize_callable_error(obj):
|
|
72
|
+
"""Prevent direct rendering of a callable (likely just forgotten ()) by raising a TypeError"""
|
|
73
|
+
if callable(obj):
|
|
74
|
+
raise TypeError(f"{obj} is callable, did you forget parentheses?")
|
|
75
|
+
|
|
76
|
+
# TODO find a way to prevent <object representation> from being rendered
|
|
77
|
+
# if obj.__class__.__str__ is object.__str__:
|
|
78
|
+
# raise TypeError(f"{obj} does not have a __str__ method")
|
|
79
|
+
|
|
80
|
+
return obj
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def get_template_dirs():
|
|
84
|
+
jinja_templates = Path(__file__).parent / "templates"
|
|
85
|
+
app_templates = settings.path.parent / "templates"
|
|
86
|
+
return (jinja_templates, app_templates) + _get_app_template_dirs()
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def create_default_environment(include_packages=True, **environment_kwargs):
|
|
90
|
+
"""
|
|
91
|
+
This default jinja environment, also used by the error rendering and internal views so
|
|
92
|
+
customization needs to happen by using this function, not settings that hook in internally.
|
|
93
|
+
"""
|
|
94
|
+
loader = import_string(settings.JINJA_LOADER)(get_template_dirs())
|
|
95
|
+
kwargs = {
|
|
96
|
+
"loader": loader,
|
|
97
|
+
"autoescape": True,
|
|
98
|
+
"auto_reload": settings.DEBUG,
|
|
99
|
+
"undefined": StrictUndefined,
|
|
100
|
+
"finalize": finalize_callable_error,
|
|
101
|
+
"extensions": ["jinja2.ext.loopcontrols", "jinja2.ext.debug"],
|
|
102
|
+
}
|
|
103
|
+
kwargs.update(**environment_kwargs)
|
|
104
|
+
env = Environment(**kwargs)
|
|
105
|
+
|
|
106
|
+
# Load the top-level defaults
|
|
107
|
+
env.globals.update(default_globals)
|
|
108
|
+
env.filters.update(default_filters)
|
|
109
|
+
|
|
110
|
+
if include_packages:
|
|
111
|
+
app_extensions, app_globals, app_filters = _get_installed_extensions()
|
|
112
|
+
|
|
113
|
+
for extension in app_extensions:
|
|
114
|
+
env.add_extension(extension)
|
|
115
|
+
|
|
116
|
+
env.globals.update(app_globals)
|
|
117
|
+
env.filters.update(app_filters)
|
|
118
|
+
|
|
119
|
+
return env
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from jinja2 import nodes
|
|
2
|
+
from jinja2.ext import Extension
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class InclusionTagExtension(Extension):
|
|
6
|
+
"""Intended to be subclassed"""
|
|
7
|
+
|
|
8
|
+
# tags = {'inclusion_tag'}
|
|
9
|
+
tags: set[str]
|
|
10
|
+
template_name: str
|
|
11
|
+
|
|
12
|
+
def parse(self, parser):
|
|
13
|
+
lineno = next(parser.stream).lineno
|
|
14
|
+
args = [
|
|
15
|
+
nodes.DerivedContextReference(),
|
|
16
|
+
]
|
|
17
|
+
kwargs = []
|
|
18
|
+
while parser.stream.current.type != "block_end":
|
|
19
|
+
if parser.stream.current.type == "name":
|
|
20
|
+
key = parser.stream.current.value
|
|
21
|
+
parser.stream.skip()
|
|
22
|
+
parser.stream.expect("assign")
|
|
23
|
+
value = parser.parse_expression()
|
|
24
|
+
kwargs.append(nodes.Keyword(key, value))
|
|
25
|
+
else:
|
|
26
|
+
args.append(parser.parse_expression())
|
|
27
|
+
|
|
28
|
+
call = self.call_method("_render", args=args, kwargs=kwargs, lineno=lineno)
|
|
29
|
+
return nodes.CallBlock(call, [], [], []).set_lineno(lineno)
|
|
30
|
+
|
|
31
|
+
def _render(self, context, *args, **kwargs):
|
|
32
|
+
context = self.get_context(context, *args, **kwargs)
|
|
33
|
+
template = self.environment.get_template(self.template_name)
|
|
34
|
+
return template.render(context)
|
|
35
|
+
|
|
36
|
+
def get_context(self, context, *args, **kwargs):
|
|
37
|
+
raise NotImplementedError(
|
|
38
|
+
"You need to implement the `get_context` method in your subclass."
|
|
39
|
+
)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
from itertools import islice
|
|
3
|
+
|
|
4
|
+
from plain.utils.html import json_script
|
|
5
|
+
from plain.utils.timesince import timesince, timeuntil
|
|
6
|
+
from plain.utils.timezone import localtime
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def localtime_filter(value, timezone=None):
|
|
10
|
+
"""Converts a datetime to local time in a template."""
|
|
11
|
+
if not value:
|
|
12
|
+
# Without this, we get the current localtime
|
|
13
|
+
# which doesn't make sense as a filter
|
|
14
|
+
raise ValueError("localtime filter requires a datetime")
|
|
15
|
+
return localtime(value, timezone)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
default_filters = {
|
|
19
|
+
# The standard Python ones
|
|
20
|
+
"strftime": datetime.datetime.strftime,
|
|
21
|
+
"strptime": datetime.datetime.strptime,
|
|
22
|
+
# To convert to user time zone
|
|
23
|
+
"localtime": localtime_filter,
|
|
24
|
+
"timeuntil": timeuntil,
|
|
25
|
+
"timesince": timesince,
|
|
26
|
+
"json_script": json_script,
|
|
27
|
+
"islice": islice, # slice for dict.items()
|
|
28
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from plain.assets import get_asset_url
|
|
2
|
+
from plain.paginator import Paginator
|
|
3
|
+
from plain.utils import timezone
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def url(viewname, *args, **kwargs):
|
|
7
|
+
# A modified reverse that lets you pass args directly, excluding urlconf
|
|
8
|
+
from plain.urls import reverse
|
|
9
|
+
|
|
10
|
+
return reverse(viewname, args=args, kwargs=kwargs)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
default_globals = {
|
|
14
|
+
"asset": get_asset_url,
|
|
15
|
+
"url": url,
|
|
16
|
+
"Paginator": Paginator,
|
|
17
|
+
"now": timezone.now,
|
|
18
|
+
"localtime": timezone.localtime,
|
|
19
|
+
}
|
plain/test/README.md
ADDED
plain/test/__init__.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Plain Unit Test framework."""
|
|
2
|
+
|
|
3
|
+
from plain.test.client import Client, RequestFactory
|
|
4
|
+
from plain.test.utils import (
|
|
5
|
+
ignore_warnings,
|
|
6
|
+
modify_settings,
|
|
7
|
+
override_settings,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"Client",
|
|
12
|
+
"RequestFactory",
|
|
13
|
+
"ignore_warnings",
|
|
14
|
+
"modify_settings",
|
|
15
|
+
"override_settings",
|
|
16
|
+
]
|