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.
Files changed (169) hide show
  1. plain/README.md +33 -0
  2. plain/__main__.py +5 -0
  3. plain/assets/README.md +56 -0
  4. plain/assets/__init__.py +6 -0
  5. plain/assets/finders.py +233 -0
  6. plain/assets/preflight.py +14 -0
  7. plain/assets/storage.py +916 -0
  8. plain/assets/utils.py +52 -0
  9. plain/assets/whitenoise/__init__.py +5 -0
  10. plain/assets/whitenoise/base.py +259 -0
  11. plain/assets/whitenoise/compress.py +189 -0
  12. plain/assets/whitenoise/media_types.py +137 -0
  13. plain/assets/whitenoise/middleware.py +197 -0
  14. plain/assets/whitenoise/responders.py +286 -0
  15. plain/assets/whitenoise/storage.py +178 -0
  16. plain/assets/whitenoise/string_utils.py +13 -0
  17. plain/cli/README.md +123 -0
  18. plain/cli/__init__.py +3 -0
  19. plain/cli/cli.py +439 -0
  20. plain/cli/formatting.py +61 -0
  21. plain/cli/packages.py +73 -0
  22. plain/cli/print.py +9 -0
  23. plain/cli/startup.py +33 -0
  24. plain/csrf/README.md +3 -0
  25. plain/csrf/middleware.py +466 -0
  26. plain/csrf/views.py +10 -0
  27. plain/debug.py +23 -0
  28. plain/exceptions.py +242 -0
  29. plain/forms/README.md +14 -0
  30. plain/forms/__init__.py +8 -0
  31. plain/forms/boundfield.py +58 -0
  32. plain/forms/exceptions.py +11 -0
  33. plain/forms/fields.py +1030 -0
  34. plain/forms/forms.py +297 -0
  35. plain/http/README.md +1 -0
  36. plain/http/__init__.py +51 -0
  37. plain/http/cookie.py +20 -0
  38. plain/http/multipartparser.py +743 -0
  39. plain/http/request.py +754 -0
  40. plain/http/response.py +719 -0
  41. plain/internal/__init__.py +0 -0
  42. plain/internal/files/README.md +3 -0
  43. plain/internal/files/__init__.py +3 -0
  44. plain/internal/files/base.py +161 -0
  45. plain/internal/files/locks.py +127 -0
  46. plain/internal/files/move.py +102 -0
  47. plain/internal/files/temp.py +79 -0
  48. plain/internal/files/uploadedfile.py +150 -0
  49. plain/internal/files/uploadhandler.py +254 -0
  50. plain/internal/files/utils.py +78 -0
  51. plain/internal/handlers/__init__.py +0 -0
  52. plain/internal/handlers/base.py +133 -0
  53. plain/internal/handlers/exception.py +145 -0
  54. plain/internal/handlers/wsgi.py +216 -0
  55. plain/internal/legacy/__init__.py +0 -0
  56. plain/internal/legacy/__main__.py +12 -0
  57. plain/internal/legacy/management/__init__.py +414 -0
  58. plain/internal/legacy/management/base.py +692 -0
  59. plain/internal/legacy/management/color.py +113 -0
  60. plain/internal/legacy/management/commands/__init__.py +0 -0
  61. plain/internal/legacy/management/commands/collectstatic.py +297 -0
  62. plain/internal/legacy/management/sql.py +67 -0
  63. plain/internal/legacy/management/utils.py +175 -0
  64. plain/json.py +40 -0
  65. plain/logs/README.md +24 -0
  66. plain/logs/__init__.py +5 -0
  67. plain/logs/configure.py +39 -0
  68. plain/logs/loggers.py +74 -0
  69. plain/logs/utils.py +46 -0
  70. plain/middleware/README.md +3 -0
  71. plain/middleware/__init__.py +0 -0
  72. plain/middleware/clickjacking.py +52 -0
  73. plain/middleware/common.py +87 -0
  74. plain/middleware/gzip.py +64 -0
  75. plain/middleware/security.py +64 -0
  76. plain/packages/README.md +41 -0
  77. plain/packages/__init__.py +4 -0
  78. plain/packages/config.py +259 -0
  79. plain/packages/registry.py +438 -0
  80. plain/paginator.py +187 -0
  81. plain/preflight/README.md +3 -0
  82. plain/preflight/__init__.py +38 -0
  83. plain/preflight/compatibility/__init__.py +0 -0
  84. plain/preflight/compatibility/django_4_0.py +20 -0
  85. plain/preflight/files.py +19 -0
  86. plain/preflight/messages.py +88 -0
  87. plain/preflight/registry.py +72 -0
  88. plain/preflight/security/__init__.py +0 -0
  89. plain/preflight/security/base.py +268 -0
  90. plain/preflight/security/csrf.py +40 -0
  91. plain/preflight/urls.py +117 -0
  92. plain/runtime/README.md +75 -0
  93. plain/runtime/__init__.py +61 -0
  94. plain/runtime/global_settings.py +199 -0
  95. plain/runtime/user_settings.py +353 -0
  96. plain/signals/README.md +14 -0
  97. plain/signals/__init__.py +5 -0
  98. plain/signals/dispatch/__init__.py +9 -0
  99. plain/signals/dispatch/dispatcher.py +320 -0
  100. plain/signals/dispatch/license.txt +35 -0
  101. plain/signing.py +299 -0
  102. plain/templates/README.md +20 -0
  103. plain/templates/__init__.py +6 -0
  104. plain/templates/core.py +24 -0
  105. plain/templates/jinja/README.md +227 -0
  106. plain/templates/jinja/__init__.py +22 -0
  107. plain/templates/jinja/defaults.py +119 -0
  108. plain/templates/jinja/extensions.py +39 -0
  109. plain/templates/jinja/filters.py +28 -0
  110. plain/templates/jinja/globals.py +19 -0
  111. plain/test/README.md +3 -0
  112. plain/test/__init__.py +16 -0
  113. plain/test/client.py +985 -0
  114. plain/test/utils.py +255 -0
  115. plain/urls/README.md +3 -0
  116. plain/urls/__init__.py +40 -0
  117. plain/urls/base.py +118 -0
  118. plain/urls/conf.py +94 -0
  119. plain/urls/converters.py +66 -0
  120. plain/urls/exceptions.py +9 -0
  121. plain/urls/resolvers.py +731 -0
  122. plain/utils/README.md +3 -0
  123. plain/utils/__init__.py +0 -0
  124. plain/utils/_os.py +52 -0
  125. plain/utils/cache.py +327 -0
  126. plain/utils/connection.py +84 -0
  127. plain/utils/crypto.py +76 -0
  128. plain/utils/datastructures.py +345 -0
  129. plain/utils/dateformat.py +329 -0
  130. plain/utils/dateparse.py +154 -0
  131. plain/utils/dates.py +76 -0
  132. plain/utils/deconstruct.py +54 -0
  133. plain/utils/decorators.py +90 -0
  134. plain/utils/deprecation.py +6 -0
  135. plain/utils/duration.py +44 -0
  136. plain/utils/email.py +12 -0
  137. plain/utils/encoding.py +235 -0
  138. plain/utils/functional.py +456 -0
  139. plain/utils/hashable.py +26 -0
  140. plain/utils/html.py +401 -0
  141. plain/utils/http.py +374 -0
  142. plain/utils/inspect.py +73 -0
  143. plain/utils/ipv6.py +46 -0
  144. plain/utils/itercompat.py +8 -0
  145. plain/utils/module_loading.py +69 -0
  146. plain/utils/regex_helper.py +353 -0
  147. plain/utils/safestring.py +72 -0
  148. plain/utils/termcolors.py +221 -0
  149. plain/utils/text.py +518 -0
  150. plain/utils/timesince.py +138 -0
  151. plain/utils/timezone.py +244 -0
  152. plain/utils/tree.py +126 -0
  153. plain/validators.py +603 -0
  154. plain/views/README.md +268 -0
  155. plain/views/__init__.py +18 -0
  156. plain/views/base.py +107 -0
  157. plain/views/csrf.py +24 -0
  158. plain/views/errors.py +25 -0
  159. plain/views/exceptions.py +4 -0
  160. plain/views/forms.py +76 -0
  161. plain/views/objects.py +229 -0
  162. plain/views/redirect.py +72 -0
  163. plain/views/templates.py +66 -0
  164. plain/wsgi.py +11 -0
  165. plain-0.1.0.dist-info/LICENSE +85 -0
  166. plain-0.1.0.dist-info/METADATA +51 -0
  167. plain-0.1.0.dist-info/RECORD +169 -0
  168. plain-0.1.0.dist-info/WHEEL +4 -0
  169. 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
@@ -0,0 +1,3 @@
1
+ # test
2
+
3
+ Testing utilities for Plain.
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
+ ]