plain 0.13.2__tar.gz → 0.14.0__tar.gz

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 (158) hide show
  1. {plain-0.13.2 → plain-0.14.0}/PKG-INFO +1 -1
  2. {plain-0.13.2 → plain-0.14.0}/plain/cli/cli.py +4 -8
  3. {plain-0.13.2 → plain-0.14.0}/plain/cli/packages.py +7 -3
  4. {plain-0.13.2 → plain-0.14.0}/plain/http/response.py +5 -0
  5. {plain-0.13.2 → plain-0.14.0}/plain/runtime/user_settings.py +6 -6
  6. {plain-0.13.2 → plain-0.14.0}/plain/test/client.py +8 -8
  7. {plain-0.13.2 → plain-0.14.0}/plain/views/forms.py +2 -2
  8. {plain-0.13.2 → plain-0.14.0}/plain/views/objects.py +2 -2
  9. {plain-0.13.2 → plain-0.14.0}/pyproject.toml +1 -1
  10. {plain-0.13.2 → plain-0.14.0}/uv.lock +1 -1
  11. plain-0.13.2/plain/utils/termcolors.py +0 -221
  12. {plain-0.13.2 → plain-0.14.0}/.gitignore +0 -0
  13. {plain-0.13.2 → plain-0.14.0}/LICENSE +0 -0
  14. {plain-0.13.2 → plain-0.14.0}/README.md +0 -0
  15. {plain-0.13.2 → plain-0.14.0}/plain/README.md +0 -0
  16. {plain-0.13.2 → plain-0.14.0}/plain/__main__.py +0 -0
  17. {plain-0.13.2 → plain-0.14.0}/plain/assets/README.md +0 -0
  18. {plain-0.13.2 → plain-0.14.0}/plain/assets/__init__.py +0 -0
  19. {plain-0.13.2 → plain-0.14.0}/plain/assets/compile.py +0 -0
  20. {plain-0.13.2 → plain-0.14.0}/plain/assets/finders.py +0 -0
  21. {plain-0.13.2 → plain-0.14.0}/plain/assets/fingerprints.py +0 -0
  22. {plain-0.13.2 → plain-0.14.0}/plain/assets/urls.py +0 -0
  23. {plain-0.13.2 → plain-0.14.0}/plain/assets/views.py +0 -0
  24. {plain-0.13.2 → plain-0.14.0}/plain/cli/README.md +0 -0
  25. {plain-0.13.2 → plain-0.14.0}/plain/cli/__init__.py +0 -0
  26. {plain-0.13.2 → plain-0.14.0}/plain/cli/formatting.py +0 -0
  27. {plain-0.13.2 → plain-0.14.0}/plain/cli/print.py +0 -0
  28. {plain-0.13.2 → plain-0.14.0}/plain/cli/startup.py +0 -0
  29. {plain-0.13.2 → plain-0.14.0}/plain/csrf/README.md +0 -0
  30. {plain-0.13.2 → plain-0.14.0}/plain/csrf/middleware.py +0 -0
  31. {plain-0.13.2 → plain-0.14.0}/plain/csrf/views.py +0 -0
  32. {plain-0.13.2 → plain-0.14.0}/plain/debug.py +0 -0
  33. {plain-0.13.2 → plain-0.14.0}/plain/exceptions.py +0 -0
  34. {plain-0.13.2 → plain-0.14.0}/plain/forms/README.md +0 -0
  35. {plain-0.13.2 → plain-0.14.0}/plain/forms/__init__.py +0 -0
  36. {plain-0.13.2 → plain-0.14.0}/plain/forms/boundfield.py +0 -0
  37. {plain-0.13.2 → plain-0.14.0}/plain/forms/exceptions.py +0 -0
  38. {plain-0.13.2 → plain-0.14.0}/plain/forms/fields.py +0 -0
  39. {plain-0.13.2 → plain-0.14.0}/plain/forms/forms.py +0 -0
  40. {plain-0.13.2 → plain-0.14.0}/plain/http/README.md +0 -0
  41. {plain-0.13.2 → plain-0.14.0}/plain/http/__init__.py +0 -0
  42. {plain-0.13.2 → plain-0.14.0}/plain/http/cookie.py +0 -0
  43. {plain-0.13.2 → plain-0.14.0}/plain/http/multipartparser.py +0 -0
  44. {plain-0.13.2 → plain-0.14.0}/plain/http/request.py +0 -0
  45. {plain-0.13.2 → plain-0.14.0}/plain/internal/__init__.py +0 -0
  46. {plain-0.13.2 → plain-0.14.0}/plain/internal/files/README.md +0 -0
  47. {plain-0.13.2 → plain-0.14.0}/plain/internal/files/__init__.py +0 -0
  48. {plain-0.13.2 → plain-0.14.0}/plain/internal/files/base.py +0 -0
  49. {plain-0.13.2 → plain-0.14.0}/plain/internal/files/locks.py +0 -0
  50. {plain-0.13.2 → plain-0.14.0}/plain/internal/files/move.py +0 -0
  51. {plain-0.13.2 → plain-0.14.0}/plain/internal/files/temp.py +0 -0
  52. {plain-0.13.2 → plain-0.14.0}/plain/internal/files/uploadedfile.py +0 -0
  53. {plain-0.13.2 → plain-0.14.0}/plain/internal/files/uploadhandler.py +0 -0
  54. {plain-0.13.2 → plain-0.14.0}/plain/internal/files/utils.py +0 -0
  55. {plain-0.13.2 → plain-0.14.0}/plain/internal/handlers/__init__.py +0 -0
  56. {plain-0.13.2 → plain-0.14.0}/plain/internal/handlers/base.py +0 -0
  57. {plain-0.13.2 → plain-0.14.0}/plain/internal/handlers/exception.py +0 -0
  58. {plain-0.13.2 → plain-0.14.0}/plain/internal/handlers/wsgi.py +0 -0
  59. {plain-0.13.2 → plain-0.14.0}/plain/internal/middleware/__init__.py +0 -0
  60. {plain-0.13.2 → plain-0.14.0}/plain/internal/middleware/headers.py +0 -0
  61. {plain-0.13.2 → plain-0.14.0}/plain/internal/middleware/https.py +0 -0
  62. {plain-0.13.2 → plain-0.14.0}/plain/internal/middleware/slash.py +0 -0
  63. {plain-0.13.2 → plain-0.14.0}/plain/json.py +0 -0
  64. {plain-0.13.2 → plain-0.14.0}/plain/logs/README.md +0 -0
  65. {plain-0.13.2 → plain-0.14.0}/plain/logs/__init__.py +0 -0
  66. {plain-0.13.2 → plain-0.14.0}/plain/logs/configure.py +0 -0
  67. {plain-0.13.2 → plain-0.14.0}/plain/logs/loggers.py +0 -0
  68. {plain-0.13.2 → plain-0.14.0}/plain/logs/utils.py +0 -0
  69. {plain-0.13.2 → plain-0.14.0}/plain/packages/README.md +0 -0
  70. {plain-0.13.2 → plain-0.14.0}/plain/packages/__init__.py +0 -0
  71. {plain-0.13.2 → plain-0.14.0}/plain/packages/config.py +0 -0
  72. {plain-0.13.2 → plain-0.14.0}/plain/packages/registry.py +0 -0
  73. {plain-0.13.2 → plain-0.14.0}/plain/paginator.py +0 -0
  74. {plain-0.13.2 → plain-0.14.0}/plain/preflight/README.md +0 -0
  75. {plain-0.13.2 → plain-0.14.0}/plain/preflight/__init__.py +0 -0
  76. {plain-0.13.2 → plain-0.14.0}/plain/preflight/files.py +0 -0
  77. {plain-0.13.2 → plain-0.14.0}/plain/preflight/messages.py +0 -0
  78. {plain-0.13.2 → plain-0.14.0}/plain/preflight/registry.py +0 -0
  79. {plain-0.13.2 → plain-0.14.0}/plain/preflight/security.py +0 -0
  80. {plain-0.13.2 → plain-0.14.0}/plain/preflight/urls.py +0 -0
  81. {plain-0.13.2 → plain-0.14.0}/plain/runtime/README.md +0 -0
  82. {plain-0.13.2 → plain-0.14.0}/plain/runtime/__init__.py +0 -0
  83. {plain-0.13.2 → plain-0.14.0}/plain/runtime/global_settings.py +0 -0
  84. {plain-0.13.2 → plain-0.14.0}/plain/signals/README.md +0 -0
  85. {plain-0.13.2 → plain-0.14.0}/plain/signals/__init__.py +0 -0
  86. {plain-0.13.2 → plain-0.14.0}/plain/signals/dispatch/__init__.py +0 -0
  87. {plain-0.13.2 → plain-0.14.0}/plain/signals/dispatch/dispatcher.py +0 -0
  88. {plain-0.13.2 → plain-0.14.0}/plain/signals/dispatch/license.txt +0 -0
  89. {plain-0.13.2 → plain-0.14.0}/plain/signing.py +0 -0
  90. {plain-0.13.2 → plain-0.14.0}/plain/templates/README.md +0 -0
  91. {plain-0.13.2 → plain-0.14.0}/plain/templates/__init__.py +0 -0
  92. {plain-0.13.2 → plain-0.14.0}/plain/templates/core.py +0 -0
  93. {plain-0.13.2 → plain-0.14.0}/plain/templates/jinja/README.md +0 -0
  94. {plain-0.13.2 → plain-0.14.0}/plain/templates/jinja/__init__.py +0 -0
  95. {plain-0.13.2 → plain-0.14.0}/plain/templates/jinja/environments.py +0 -0
  96. {plain-0.13.2 → plain-0.14.0}/plain/templates/jinja/extensions.py +0 -0
  97. {plain-0.13.2 → plain-0.14.0}/plain/templates/jinja/filters.py +0 -0
  98. {plain-0.13.2 → plain-0.14.0}/plain/templates/jinja/globals.py +0 -0
  99. {plain-0.13.2 → plain-0.14.0}/plain/test/README.md +0 -0
  100. {plain-0.13.2 → plain-0.14.0}/plain/test/__init__.py +0 -0
  101. {plain-0.13.2 → plain-0.14.0}/plain/urls/README.md +0 -0
  102. {plain-0.13.2 → plain-0.14.0}/plain/urls/__init__.py +0 -0
  103. {plain-0.13.2 → plain-0.14.0}/plain/urls/base.py +0 -0
  104. {plain-0.13.2 → plain-0.14.0}/plain/urls/conf.py +0 -0
  105. {plain-0.13.2 → plain-0.14.0}/plain/urls/converters.py +0 -0
  106. {plain-0.13.2 → plain-0.14.0}/plain/urls/exceptions.py +0 -0
  107. {plain-0.13.2 → plain-0.14.0}/plain/urls/resolvers.py +0 -0
  108. {plain-0.13.2 → plain-0.14.0}/plain/utils/README.md +0 -0
  109. {plain-0.13.2 → plain-0.14.0}/plain/utils/__init__.py +0 -0
  110. {plain-0.13.2 → plain-0.14.0}/plain/utils/_os.py +0 -0
  111. {plain-0.13.2 → plain-0.14.0}/plain/utils/cache.py +0 -0
  112. {plain-0.13.2 → plain-0.14.0}/plain/utils/connection.py +0 -0
  113. {plain-0.13.2 → plain-0.14.0}/plain/utils/crypto.py +0 -0
  114. {plain-0.13.2 → plain-0.14.0}/plain/utils/datastructures.py +0 -0
  115. {plain-0.13.2 → plain-0.14.0}/plain/utils/dateformat.py +0 -0
  116. {plain-0.13.2 → plain-0.14.0}/plain/utils/dateparse.py +0 -0
  117. {plain-0.13.2 → plain-0.14.0}/plain/utils/dates.py +0 -0
  118. {plain-0.13.2 → plain-0.14.0}/plain/utils/deconstruct.py +0 -0
  119. {plain-0.13.2 → plain-0.14.0}/plain/utils/decorators.py +0 -0
  120. {plain-0.13.2 → plain-0.14.0}/plain/utils/deprecation.py +0 -0
  121. {plain-0.13.2 → plain-0.14.0}/plain/utils/duration.py +0 -0
  122. {plain-0.13.2 → plain-0.14.0}/plain/utils/email.py +0 -0
  123. {plain-0.13.2 → plain-0.14.0}/plain/utils/encoding.py +0 -0
  124. {plain-0.13.2 → plain-0.14.0}/plain/utils/functional.py +0 -0
  125. {plain-0.13.2 → plain-0.14.0}/plain/utils/hashable.py +0 -0
  126. {plain-0.13.2 → plain-0.14.0}/plain/utils/html.py +0 -0
  127. {plain-0.13.2 → plain-0.14.0}/plain/utils/http.py +0 -0
  128. {plain-0.13.2 → plain-0.14.0}/plain/utils/inspect.py +0 -0
  129. {plain-0.13.2 → plain-0.14.0}/plain/utils/ipv6.py +0 -0
  130. {plain-0.13.2 → plain-0.14.0}/plain/utils/itercompat.py +0 -0
  131. {plain-0.13.2 → plain-0.14.0}/plain/utils/module_loading.py +0 -0
  132. {plain-0.13.2 → plain-0.14.0}/plain/utils/regex_helper.py +0 -0
  133. {plain-0.13.2 → plain-0.14.0}/plain/utils/safestring.py +0 -0
  134. {plain-0.13.2 → plain-0.14.0}/plain/utils/text.py +0 -0
  135. {plain-0.13.2 → plain-0.14.0}/plain/utils/timesince.py +0 -0
  136. {plain-0.13.2 → plain-0.14.0}/plain/utils/timezone.py +0 -0
  137. {plain-0.13.2 → plain-0.14.0}/plain/utils/tree.py +0 -0
  138. {plain-0.13.2 → plain-0.14.0}/plain/validators.py +0 -0
  139. {plain-0.13.2 → plain-0.14.0}/plain/views/README.md +0 -0
  140. {plain-0.13.2 → plain-0.14.0}/plain/views/__init__.py +0 -0
  141. {plain-0.13.2 → plain-0.14.0}/plain/views/base.py +0 -0
  142. {plain-0.13.2 → plain-0.14.0}/plain/views/csrf.py +0 -0
  143. {plain-0.13.2 → plain-0.14.0}/plain/views/errors.py +0 -0
  144. {plain-0.13.2 → plain-0.14.0}/plain/views/exceptions.py +0 -0
  145. {plain-0.13.2 → plain-0.14.0}/plain/views/redirect.py +0 -0
  146. {plain-0.13.2 → plain-0.14.0}/plain/views/templates.py +0 -0
  147. {plain-0.13.2 → plain-0.14.0}/plain/wsgi.py +0 -0
  148. {plain-0.13.2 → plain-0.14.0}/tests/.bolt/assets_collected/assets.json +0 -0
  149. {plain-0.13.2 → plain-0.14.0}/tests/.gitignore +0 -0
  150. {plain-0.13.2 → plain-0.14.0}/tests/app/.gitignore +0 -0
  151. {plain-0.13.2 → plain-0.14.0}/tests/app/settings.py +0 -0
  152. {plain-0.13.2 → plain-0.14.0}/tests/app/test/__init__.py +0 -0
  153. {plain-0.13.2 → plain-0.14.0}/tests/app/test/default_settings.py +0 -0
  154. {plain-0.13.2 → plain-0.14.0}/tests/app/urls.py +0 -0
  155. {plain-0.13.2 → plain-0.14.0}/tests/conftest.py +0 -0
  156. {plain-0.13.2 → plain-0.14.0}/tests/test_cli.py +0 -0
  157. {plain-0.13.2 → plain-0.14.0}/tests/test_runtime.py +0 -0
  158. {plain-0.13.2 → plain-0.14.0}/tests/test_wsgi.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plain
3
- Version: 0.13.2
3
+ Version: 0.14.0
4
4
  Summary: A web framework for building products with Python.
5
5
  Author-email: Dave Gaeddert <dave.gaeddert@dropseed.dev>
6
6
  License-File: LICENSE
@@ -422,21 +422,17 @@ class AppCLIGroup(click.Group):
422
422
  MODULE_NAME = "app.cli"
423
423
 
424
424
  def list_commands(self, ctx):
425
- try:
426
- find_spec(self.MODULE_NAME)
425
+ if find_spec(self.MODULE_NAME):
427
426
  return ["app"]
428
- except ModuleNotFoundError:
427
+ else:
429
428
  return []
430
429
 
431
430
  def get_command(self, ctx, name):
432
431
  if name != "app":
433
432
  return
434
433
 
435
- try:
436
- cli = importlib.import_module(self.MODULE_NAME)
437
- return cli.cli
438
- except ModuleNotFoundError:
439
- return
434
+ cli = importlib.import_module(self.MODULE_NAME)
435
+ return cli.cli
440
436
 
441
437
 
442
438
  class PlainCommandCollection(click.CommandCollection):
@@ -36,11 +36,15 @@ class InstalledPackagesGroup(click.Group):
36
36
  def get_command(self, ctx, name):
37
37
  # Try it as plain.x and just x (we don't know ahead of time which it is, but prefer plain.x)
38
38
  for n in [self.PLAIN_APPS_PREFIX + name, name]:
39
- try:
40
- cli = importlib.import_module(f"{n}.{self.MODULE_NAME}")
41
- except ModuleNotFoundError:
39
+ if not find_spec(n):
40
+ # plain.<name> doesn't exist at all
41
+ continue
42
+
43
+ if not find_spec(f"{n}.{self.MODULE_NAME}"):
42
44
  continue
43
45
 
46
+ cli = importlib.import_module(f"{n}.{self.MODULE_NAME}")
47
+
44
48
  # Get the app's cli.py group
45
49
  try:
46
50
  return cli.cli
@@ -7,6 +7,7 @@ import re
7
7
  import sys
8
8
  import time
9
9
  from email.header import Header
10
+ from functools import cached_property
10
11
  from http.client import responses
11
12
  from http.cookies import SimpleCookie
12
13
  from urllib.parse import urlparse
@@ -423,6 +424,10 @@ class Response(ResponseBase):
423
424
  # Create a list of properly encoded bytestrings to support write().
424
425
  self._container = [content]
425
426
 
427
+ @cached_property
428
+ def text(self):
429
+ return self.content.decode(self.charset or "utf-8")
430
+
426
431
  def __iter__(self):
427
432
  return iter(self._container)
428
433
 
@@ -4,6 +4,7 @@ import os
4
4
  import time
5
5
  import types
6
6
  import typing
7
+ from importlib.util import find_spec
7
8
  from pathlib import Path
8
9
 
9
10
  from plain.exceptions import ImproperlyConfigured
@@ -101,12 +102,11 @@ class Settings:
101
102
 
102
103
  def _load_default_settings(self, settings_module):
103
104
  for entry in getattr(settings_module, "INSTALLED_PACKAGES", []):
104
- try:
105
- if isinstance(entry, PackageConfig):
106
- app_settings = entry.module.default_settings
107
- else:
108
- app_settings = importlib.import_module(f"{entry}.default_settings")
109
- except ModuleNotFoundError:
105
+ if isinstance(entry, PackageConfig):
106
+ app_settings = entry.module.default_settings
107
+ elif find_spec(f"{entry}.default_settings"):
108
+ app_settings = importlib.import_module(f"{entry}.default_settings")
109
+ else:
110
110
  continue
111
111
 
112
112
  self._load_module_settings(app_settings)
@@ -5,7 +5,6 @@ import sys
5
5
  from functools import partial
6
6
  from http import HTTPStatus
7
7
  from http.cookies import SimpleCookie
8
- from importlib import import_module
9
8
  from io import BytesIO, IOBase
10
9
  from itertools import chain
11
10
  from urllib.parse import unquote_to_bytes, urljoin, urlparse, urlsplit
@@ -21,6 +20,7 @@ from plain.utils.encoding import force_bytes
21
20
  from plain.utils.functional import SimpleLazyObject
22
21
  from plain.utils.http import urlencode
23
22
  from plain.utils.itercompat import is_iterable
23
+ from plain.utils.module_loading import import_string
24
24
  from plain.utils.regex_helper import _lazy_re_compile
25
25
 
26
26
  __all__ = (
@@ -562,11 +562,11 @@ class ClientMixin:
562
562
  @property
563
563
  def session(self):
564
564
  """Return the current session variables."""
565
- engine = import_module(settings.SESSION_ENGINE)
565
+ Session = import_string(settings.SESSION_CLASS)
566
566
  cookie = self.cookies.get(settings.SESSION_COOKIE_NAME)
567
567
  if cookie:
568
- return engine.SessionStore(cookie.value)
569
- session = engine.SessionStore()
568
+ return Session(cookie.value)
569
+ session = Session()
570
570
  session.save()
571
571
  self.cookies[settings.SESSION_COOKIE_NAME] = session.session_key
572
572
  return session
@@ -582,8 +582,8 @@ class ClientMixin:
582
582
  if self.session:
583
583
  request.session = self.session
584
584
  else:
585
- engine = import_module(settings.SESSION_ENGINE)
586
- request.session = engine.SessionStore()
585
+ Session = import_string(settings.SESSION_CLASS)
586
+ request.session = Session()
587
587
  login(request, user)
588
588
  # Save the session values.
589
589
  request.session.save()
@@ -608,8 +608,8 @@ class ClientMixin:
608
608
  request.session = self.session
609
609
  request.user = get_user(request)
610
610
  else:
611
- engine = import_module(settings.SESSION_ENGINE)
612
- request.session = engine.SessionStore()
611
+ Session = import_string(settings.SESSION_CLASS)
612
+ request.session = Session()
613
613
  logout(request)
614
614
  self.cookies = SimpleCookie()
615
615
 
@@ -40,7 +40,7 @@ class FormView(TemplateView):
40
40
  )
41
41
  return kwargs
42
42
 
43
- def get_success_url(self) -> str:
43
+ def get_success_url(self, form: "BaseForm") -> str:
44
44
  """Return the URL to redirect to after processing a valid form."""
45
45
  if not self.success_url:
46
46
  raise ImproperlyConfigured("No URL to redirect to. Provide a success_url.")
@@ -48,7 +48,7 @@ class FormView(TemplateView):
48
48
 
49
49
  def form_valid(self, form: "BaseForm") -> Response:
50
50
  """If the form is valid, redirect to the supplied URL."""
51
- return ResponseRedirect(self.get_success_url())
51
+ return ResponseRedirect(self.get_success_url(form))
52
52
 
53
53
  def form_invalid(self, form: "BaseForm") -> Response:
54
54
  """If the form is invalid, render the invalid form."""
@@ -93,7 +93,7 @@ class CreateView(ObjectTemplateViewMixin, FormView):
93
93
  self.object = None
94
94
 
95
95
  # TODO? would rather you have to specify this...
96
- def get_success_url(self):
96
+ def get_success_url(self, form):
97
97
  """Return the URL to redirect to after processing a valid form."""
98
98
  if self.success_url:
99
99
  url = self.success_url.format(**self.object.__dict__)
@@ -126,7 +126,7 @@ class UpdateView(ObjectTemplateViewMixin, FormView):
126
126
  self.load_object()
127
127
  return super().post()
128
128
 
129
- def get_success_url(self):
129
+ def get_success_url(self, form):
130
130
  """Return the URL to redirect to after processing a valid form."""
131
131
  if self.success_url:
132
132
  url = self.success_url.format(**self.object.__dict__)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "plain"
3
- version = "0.13.2"
3
+ version = "0.14.0"
4
4
  description = "A web framework for building products with Python."
5
5
  authors = [{name = "Dave Gaeddert", email = "dave.gaeddert@dropseed.dev"}]
6
6
  readme = "README.md"
@@ -102,7 +102,7 @@ wheels = [
102
102
 
103
103
  [[package]]
104
104
  name = "plain"
105
- version = "0.13.1"
105
+ version = "0.14.0"
106
106
  source = { editable = "." }
107
107
  dependencies = [
108
108
  { name = "click" },
@@ -1,221 +0,0 @@
1
- """
2
- termcolors.py
3
- """
4
-
5
- color_names = ("black", "red", "green", "yellow", "blue", "magenta", "cyan", "white")
6
- foreground = {color_names[x]: f"3{x}" for x in range(8)}
7
- background = {color_names[x]: f"4{x}" for x in range(8)}
8
-
9
- RESET = "0"
10
- opt_dict = {
11
- "bold": "1",
12
- "underscore": "4",
13
- "blink": "5",
14
- "reverse": "7",
15
- "conceal": "8",
16
- }
17
-
18
-
19
- def colorize(text="", opts=(), **kwargs):
20
- """
21
- Return your text, enclosed in ANSI graphics codes.
22
-
23
- Depends on the keyword arguments 'fg' and 'bg', and the contents of
24
- the opts tuple/list.
25
-
26
- Return the RESET code if no parameters are given.
27
-
28
- Valid colors:
29
- 'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'
30
-
31
- Valid options:
32
- 'bold'
33
- 'underscore'
34
- 'blink'
35
- 'reverse'
36
- 'conceal'
37
- 'noreset' - string will not be auto-terminated with the RESET code
38
-
39
- Examples:
40
- colorize('hello', fg='red', bg='blue', opts=('blink',))
41
- colorize()
42
- colorize('goodbye', opts=('underscore',))
43
- print(colorize('first line', fg='red', opts=('noreset',)))
44
- print('this should be red too')
45
- print(colorize('and so should this'))
46
- print('this should not be red')
47
- """
48
- code_list = []
49
- if text == "" and len(opts) == 1 and opts[0] == "reset":
50
- return f"\x1b[{RESET}m"
51
- for k, v in kwargs.items():
52
- if k == "fg":
53
- code_list.append(foreground[v])
54
- elif k == "bg":
55
- code_list.append(background[v])
56
- for o in opts:
57
- if o in opt_dict:
58
- code_list.append(opt_dict[o])
59
- if "noreset" not in opts:
60
- text = "{}\x1b[{}m".format(text or "", RESET)
61
- return "{}{}".format(("\x1b[{}m".format(";".join(code_list))), text or "")
62
-
63
-
64
- def make_style(opts=(), **kwargs):
65
- """
66
- Return a function with default parameters for colorize()
67
-
68
- Example:
69
- bold_red = make_style(opts=('bold',), fg='red')
70
- print(bold_red('hello'))
71
- KEYWORD = make_style(fg='yellow')
72
- COMMENT = make_style(fg='blue', opts=('bold',))
73
- """
74
- return lambda text: colorize(text, opts, **kwargs)
75
-
76
-
77
- NOCOLOR_PALETTE = "nocolor"
78
- DARK_PALETTE = "dark"
79
- LIGHT_PALETTE = "light"
80
-
81
- PALETTES = {
82
- NOCOLOR_PALETTE: {
83
- "ERROR": {},
84
- "SUCCESS": {},
85
- "WARNING": {},
86
- "NOTICE": {},
87
- "SQL_FIELD": {},
88
- "SQL_COLTYPE": {},
89
- "SQL_KEYWORD": {},
90
- "SQL_TABLE": {},
91
- "HTTP_INFO": {},
92
- "HTTP_SUCCESS": {},
93
- "HTTP_REDIRECT": {},
94
- "HTTP_NOT_MODIFIED": {},
95
- "HTTP_BAD_REQUEST": {},
96
- "HTTP_NOT_FOUND": {},
97
- "HTTP_SERVER_ERROR": {},
98
- "MIGRATE_HEADING": {},
99
- "MIGRATE_LABEL": {},
100
- },
101
- DARK_PALETTE: {
102
- "ERROR": {"fg": "red", "opts": ("bold",)},
103
- "SUCCESS": {"fg": "green", "opts": ("bold",)},
104
- "WARNING": {"fg": "yellow", "opts": ("bold",)},
105
- "NOTICE": {"fg": "red"},
106
- "SQL_FIELD": {"fg": "green", "opts": ("bold",)},
107
- "SQL_COLTYPE": {"fg": "green"},
108
- "SQL_KEYWORD": {"fg": "yellow"},
109
- "SQL_TABLE": {"opts": ("bold",)},
110
- "HTTP_INFO": {"opts": ("bold",)},
111
- "HTTP_SUCCESS": {},
112
- "HTTP_REDIRECT": {"fg": "green"},
113
- "HTTP_NOT_MODIFIED": {"fg": "cyan"},
114
- "HTTP_BAD_REQUEST": {"fg": "red", "opts": ("bold",)},
115
- "HTTP_NOT_FOUND": {"fg": "yellow"},
116
- "HTTP_SERVER_ERROR": {"fg": "magenta", "opts": ("bold",)},
117
- "MIGRATE_HEADING": {"fg": "cyan", "opts": ("bold",)},
118
- "MIGRATE_LABEL": {"opts": ("bold",)},
119
- },
120
- LIGHT_PALETTE: {
121
- "ERROR": {"fg": "red", "opts": ("bold",)},
122
- "SUCCESS": {"fg": "green", "opts": ("bold",)},
123
- "WARNING": {"fg": "yellow", "opts": ("bold",)},
124
- "NOTICE": {"fg": "red"},
125
- "SQL_FIELD": {"fg": "green", "opts": ("bold",)},
126
- "SQL_COLTYPE": {"fg": "green"},
127
- "SQL_KEYWORD": {"fg": "blue"},
128
- "SQL_TABLE": {"opts": ("bold",)},
129
- "HTTP_INFO": {"opts": ("bold",)},
130
- "HTTP_SUCCESS": {},
131
- "HTTP_REDIRECT": {"fg": "green", "opts": ("bold",)},
132
- "HTTP_NOT_MODIFIED": {"fg": "green"},
133
- "HTTP_BAD_REQUEST": {"fg": "red", "opts": ("bold",)},
134
- "HTTP_NOT_FOUND": {"fg": "red"},
135
- "HTTP_SERVER_ERROR": {"fg": "magenta", "opts": ("bold",)},
136
- "MIGRATE_HEADING": {"fg": "cyan", "opts": ("bold",)},
137
- "MIGRATE_LABEL": {"opts": ("bold",)},
138
- },
139
- }
140
- DEFAULT_PALETTE = DARK_PALETTE
141
-
142
-
143
- def parse_color_setting(config_string):
144
- """Parse a DJANGO_COLORS environment variable to produce the system palette
145
-
146
- The general form of a palette definition is:
147
-
148
- "palette;role=fg;role=fg/bg;role=fg,option,option;role=fg/bg,option,option"
149
-
150
- where:
151
- palette is a named palette; one of 'light', 'dark', or 'nocolor'.
152
- role is a named style used by Django
153
- fg is a foreground color.
154
- bg is a background color.
155
- option is a display options.
156
-
157
- Specifying a named palette is the same as manually specifying the individual
158
- definitions for each role. Any individual definitions following the palette
159
- definition will augment the base palette definition.
160
-
161
- Valid roles:
162
- 'error', 'success', 'warning', 'notice', 'sql_field', 'sql_coltype',
163
- 'sql_keyword', 'sql_table', 'http_info', 'http_success',
164
- 'http_redirect', 'http_not_modified', 'http_bad_request',
165
- 'http_not_found', 'http_server_error', 'migrate_heading',
166
- 'migrate_label'
167
-
168
- Valid colors:
169
- 'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'
170
-
171
- Valid options:
172
- 'bold', 'underscore', 'blink', 'reverse', 'conceal', 'noreset'
173
- """
174
- if not config_string:
175
- return PALETTES[DEFAULT_PALETTE]
176
-
177
- # Split the color configuration into parts
178
- parts = config_string.lower().split(";")
179
- palette = PALETTES[NOCOLOR_PALETTE].copy()
180
- for part in parts:
181
- if part in PALETTES:
182
- # A default palette has been specified
183
- palette.update(PALETTES[part])
184
- elif "=" in part:
185
- # Process a palette defining string
186
- definition = {}
187
-
188
- # Break the definition into the role,
189
- # plus the list of specific instructions.
190
- # The role must be in upper case
191
- role, instructions = part.split("=")
192
- role = role.upper()
193
-
194
- styles = instructions.split(",")
195
- styles.reverse()
196
-
197
- # The first instruction can contain a slash
198
- # to break apart fg/bg.
199
- colors = styles.pop().split("/")
200
- colors.reverse()
201
- fg = colors.pop()
202
- if fg in color_names:
203
- definition["fg"] = fg
204
- if colors and colors[-1] in color_names:
205
- definition["bg"] = colors[-1]
206
-
207
- # All remaining instructions are options
208
- opts = tuple(s for s in styles if s in opt_dict)
209
- if opts:
210
- definition["opts"] = opts
211
-
212
- # The nocolor palette has all available roles.
213
- # Use that palette as the basis for determining
214
- # if the role is valid.
215
- if role in PALETTES[NOCOLOR_PALETTE] and definition:
216
- palette[role] = definition
217
-
218
- # If there are no colors specified, return the empty palette.
219
- if palette == PALETTES[NOCOLOR_PALETTE]:
220
- return None
221
- return palette
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes