plain 0.29.0__tar.gz → 0.31.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 (152) hide show
  1. {plain-0.29.0 → plain-0.31.0}/PKG-INFO +1 -1
  2. {plain-0.29.0 → plain-0.31.0}/plain/assets/urls.py +3 -4
  3. {plain-0.29.0 → plain-0.31.0}/plain/assets/views.py +2 -2
  4. {plain-0.29.0 → plain-0.31.0}/plain/cli/core.py +10 -5
  5. {plain-0.29.0 → plain-0.31.0}/plain/forms/fields.py +0 -11
  6. {plain-0.29.0 → plain-0.31.0}/plain/preflight/urls.py +3 -2
  7. {plain-0.29.0 → plain-0.31.0}/plain/runtime/global_settings.py +1 -1
  8. {plain-0.29.0 → plain-0.31.0}/plain/urls/__init__.py +2 -3
  9. {plain-0.29.0 → plain-0.31.0}/plain/urls/resolvers.py +13 -17
  10. {plain-0.29.0 → plain-0.31.0}/plain/urls/routers.py +12 -50
  11. {plain-0.29.0 → plain-0.31.0}/plain/validators.py +0 -15
  12. {plain-0.29.0 → plain-0.31.0}/pyproject.toml +1 -1
  13. {plain-0.29.0 → plain-0.31.0}/tests/app/settings.py +2 -0
  14. {plain-0.29.0 → plain-0.31.0}/tests/app/urls.py +2 -3
  15. {plain-0.29.0 → plain-0.31.0}/.gitignore +0 -0
  16. {plain-0.29.0 → plain-0.31.0}/LICENSE +0 -0
  17. {plain-0.29.0 → plain-0.31.0}/README.md +0 -0
  18. {plain-0.29.0 → plain-0.31.0}/plain/README.md +0 -0
  19. {plain-0.29.0 → plain-0.31.0}/plain/__main__.py +0 -0
  20. {plain-0.29.0 → plain-0.31.0}/plain/assets/README.md +0 -0
  21. {plain-0.29.0 → plain-0.31.0}/plain/assets/__init__.py +0 -0
  22. {plain-0.29.0 → plain-0.31.0}/plain/assets/compile.py +0 -0
  23. {plain-0.29.0 → plain-0.31.0}/plain/assets/finders.py +0 -0
  24. {plain-0.29.0 → plain-0.31.0}/plain/assets/fingerprints.py +0 -0
  25. {plain-0.29.0 → plain-0.31.0}/plain/cli/README.md +0 -0
  26. {plain-0.29.0 → plain-0.31.0}/plain/cli/__init__.py +0 -0
  27. {plain-0.29.0 → plain-0.31.0}/plain/cli/formatting.py +0 -0
  28. {plain-0.29.0 → plain-0.31.0}/plain/cli/print.py +0 -0
  29. {plain-0.29.0 → plain-0.31.0}/plain/cli/registry.py +0 -0
  30. {plain-0.29.0 → plain-0.31.0}/plain/cli/startup.py +0 -0
  31. {plain-0.29.0 → plain-0.31.0}/plain/csrf/README.md +0 -0
  32. {plain-0.29.0 → plain-0.31.0}/plain/csrf/middleware.py +0 -0
  33. {plain-0.29.0 → plain-0.31.0}/plain/csrf/views.py +0 -0
  34. {plain-0.29.0 → plain-0.31.0}/plain/debug.py +0 -0
  35. {plain-0.29.0 → plain-0.31.0}/plain/exceptions.py +0 -0
  36. {plain-0.29.0 → plain-0.31.0}/plain/forms/README.md +0 -0
  37. {plain-0.29.0 → plain-0.31.0}/plain/forms/__init__.py +0 -0
  38. {plain-0.29.0 → plain-0.31.0}/plain/forms/boundfield.py +0 -0
  39. {plain-0.29.0 → plain-0.31.0}/plain/forms/exceptions.py +0 -0
  40. {plain-0.29.0 → plain-0.31.0}/plain/forms/forms.py +0 -0
  41. {plain-0.29.0 → plain-0.31.0}/plain/http/README.md +0 -0
  42. {plain-0.29.0 → plain-0.31.0}/plain/http/__init__.py +0 -0
  43. {plain-0.29.0 → plain-0.31.0}/plain/http/cookie.py +0 -0
  44. {plain-0.29.0 → plain-0.31.0}/plain/http/multipartparser.py +0 -0
  45. {plain-0.29.0 → plain-0.31.0}/plain/http/request.py +0 -0
  46. {plain-0.29.0 → plain-0.31.0}/plain/http/response.py +0 -0
  47. {plain-0.29.0 → plain-0.31.0}/plain/internal/__init__.py +0 -0
  48. {plain-0.29.0 → plain-0.31.0}/plain/internal/files/README.md +0 -0
  49. {plain-0.29.0 → plain-0.31.0}/plain/internal/files/__init__.py +0 -0
  50. {plain-0.29.0 → plain-0.31.0}/plain/internal/files/base.py +0 -0
  51. {plain-0.29.0 → plain-0.31.0}/plain/internal/files/locks.py +0 -0
  52. {plain-0.29.0 → plain-0.31.0}/plain/internal/files/move.py +0 -0
  53. {plain-0.29.0 → plain-0.31.0}/plain/internal/files/temp.py +0 -0
  54. {plain-0.29.0 → plain-0.31.0}/plain/internal/files/uploadedfile.py +0 -0
  55. {plain-0.29.0 → plain-0.31.0}/plain/internal/files/uploadhandler.py +0 -0
  56. {plain-0.29.0 → plain-0.31.0}/plain/internal/files/utils.py +0 -0
  57. {plain-0.29.0 → plain-0.31.0}/plain/internal/handlers/__init__.py +0 -0
  58. {plain-0.29.0 → plain-0.31.0}/plain/internal/handlers/base.py +0 -0
  59. {plain-0.29.0 → plain-0.31.0}/plain/internal/handlers/exception.py +0 -0
  60. {plain-0.29.0 → plain-0.31.0}/plain/internal/handlers/wsgi.py +0 -0
  61. {plain-0.29.0 → plain-0.31.0}/plain/internal/middleware/__init__.py +0 -0
  62. {plain-0.29.0 → plain-0.31.0}/plain/internal/middleware/headers.py +0 -0
  63. {plain-0.29.0 → plain-0.31.0}/plain/internal/middleware/https.py +0 -0
  64. {plain-0.29.0 → plain-0.31.0}/plain/internal/middleware/slash.py +0 -0
  65. {plain-0.29.0 → plain-0.31.0}/plain/json.py +0 -0
  66. {plain-0.29.0 → plain-0.31.0}/plain/logs/README.md +0 -0
  67. {plain-0.29.0 → plain-0.31.0}/plain/logs/__init__.py +0 -0
  68. {plain-0.29.0 → plain-0.31.0}/plain/logs/configure.py +0 -0
  69. {plain-0.29.0 → plain-0.31.0}/plain/logs/loggers.py +0 -0
  70. {plain-0.29.0 → plain-0.31.0}/plain/logs/utils.py +0 -0
  71. {plain-0.29.0 → plain-0.31.0}/plain/packages/README.md +0 -0
  72. {plain-0.29.0 → plain-0.31.0}/plain/packages/__init__.py +0 -0
  73. {plain-0.29.0 → plain-0.31.0}/plain/packages/config.py +0 -0
  74. {plain-0.29.0 → plain-0.31.0}/plain/packages/registry.py +0 -0
  75. {plain-0.29.0 → plain-0.31.0}/plain/paginator.py +0 -0
  76. {plain-0.29.0 → plain-0.31.0}/plain/preflight/README.md +0 -0
  77. {plain-0.29.0 → plain-0.31.0}/plain/preflight/__init__.py +0 -0
  78. {plain-0.29.0 → plain-0.31.0}/plain/preflight/files.py +0 -0
  79. {plain-0.29.0 → plain-0.31.0}/plain/preflight/messages.py +0 -0
  80. {plain-0.29.0 → plain-0.31.0}/plain/preflight/registry.py +0 -0
  81. {plain-0.29.0 → plain-0.31.0}/plain/preflight/security.py +0 -0
  82. {plain-0.29.0 → plain-0.31.0}/plain/runtime/README.md +0 -0
  83. {plain-0.29.0 → plain-0.31.0}/plain/runtime/__init__.py +0 -0
  84. {plain-0.29.0 → plain-0.31.0}/plain/runtime/user_settings.py +0 -0
  85. {plain-0.29.0 → plain-0.31.0}/plain/signals/README.md +0 -0
  86. {plain-0.29.0 → plain-0.31.0}/plain/signals/__init__.py +0 -0
  87. {plain-0.29.0 → plain-0.31.0}/plain/signals/dispatch/__init__.py +0 -0
  88. {plain-0.29.0 → plain-0.31.0}/plain/signals/dispatch/dispatcher.py +0 -0
  89. {plain-0.29.0 → plain-0.31.0}/plain/signals/dispatch/license.txt +0 -0
  90. {plain-0.29.0 → plain-0.31.0}/plain/signing.py +0 -0
  91. {plain-0.29.0 → plain-0.31.0}/plain/templates/README.md +0 -0
  92. {plain-0.29.0 → plain-0.31.0}/plain/templates/__init__.py +0 -0
  93. {plain-0.29.0 → plain-0.31.0}/plain/templates/core.py +0 -0
  94. {plain-0.29.0 → plain-0.31.0}/plain/templates/jinja/README.md +0 -0
  95. {plain-0.29.0 → plain-0.31.0}/plain/templates/jinja/__init__.py +0 -0
  96. {plain-0.29.0 → plain-0.31.0}/plain/templates/jinja/environments.py +0 -0
  97. {plain-0.29.0 → plain-0.31.0}/plain/templates/jinja/extensions.py +0 -0
  98. {plain-0.29.0 → plain-0.31.0}/plain/templates/jinja/filters.py +0 -0
  99. {plain-0.29.0 → plain-0.31.0}/plain/templates/jinja/globals.py +0 -0
  100. {plain-0.29.0 → plain-0.31.0}/plain/test/README.md +0 -0
  101. {plain-0.29.0 → plain-0.31.0}/plain/test/__init__.py +0 -0
  102. {plain-0.29.0 → plain-0.31.0}/plain/test/client.py +0 -0
  103. {plain-0.29.0 → plain-0.31.0}/plain/urls/README.md +0 -0
  104. {plain-0.29.0 → plain-0.31.0}/plain/urls/converters.py +0 -0
  105. {plain-0.29.0 → plain-0.31.0}/plain/urls/exceptions.py +0 -0
  106. {plain-0.29.0 → plain-0.31.0}/plain/urls/patterns.py +0 -0
  107. {plain-0.29.0 → plain-0.31.0}/plain/urls/utils.py +0 -0
  108. {plain-0.29.0 → plain-0.31.0}/plain/utils/README.md +0 -0
  109. {plain-0.29.0 → plain-0.31.0}/plain/utils/__init__.py +0 -0
  110. {plain-0.29.0 → plain-0.31.0}/plain/utils/cache.py +0 -0
  111. {plain-0.29.0 → plain-0.31.0}/plain/utils/connection.py +0 -0
  112. {plain-0.29.0 → plain-0.31.0}/plain/utils/crypto.py +0 -0
  113. {plain-0.29.0 → plain-0.31.0}/plain/utils/datastructures.py +0 -0
  114. {plain-0.29.0 → plain-0.31.0}/plain/utils/dateparse.py +0 -0
  115. {plain-0.29.0 → plain-0.31.0}/plain/utils/deconstruct.py +0 -0
  116. {plain-0.29.0 → plain-0.31.0}/plain/utils/decorators.py +0 -0
  117. {plain-0.29.0 → plain-0.31.0}/plain/utils/duration.py +0 -0
  118. {plain-0.29.0 → plain-0.31.0}/plain/utils/encoding.py +0 -0
  119. {plain-0.29.0 → plain-0.31.0}/plain/utils/functional.py +0 -0
  120. {plain-0.29.0 → plain-0.31.0}/plain/utils/hashable.py +0 -0
  121. {plain-0.29.0 → plain-0.31.0}/plain/utils/html.py +0 -0
  122. {plain-0.29.0 → plain-0.31.0}/plain/utils/http.py +0 -0
  123. {plain-0.29.0 → plain-0.31.0}/plain/utils/inspect.py +0 -0
  124. {plain-0.29.0 → plain-0.31.0}/plain/utils/ipv6.py +0 -0
  125. {plain-0.29.0 → plain-0.31.0}/plain/utils/itercompat.py +0 -0
  126. {plain-0.29.0 → plain-0.31.0}/plain/utils/module_loading.py +0 -0
  127. {plain-0.29.0 → plain-0.31.0}/plain/utils/regex_helper.py +0 -0
  128. {plain-0.29.0 → plain-0.31.0}/plain/utils/safestring.py +0 -0
  129. {plain-0.29.0 → plain-0.31.0}/plain/utils/text.py +0 -0
  130. {plain-0.29.0 → plain-0.31.0}/plain/utils/timesince.py +0 -0
  131. {plain-0.29.0 → plain-0.31.0}/plain/utils/timezone.py +0 -0
  132. {plain-0.29.0 → plain-0.31.0}/plain/utils/tree.py +0 -0
  133. {plain-0.29.0 → plain-0.31.0}/plain/views/README.md +0 -0
  134. {plain-0.29.0 → plain-0.31.0}/plain/views/__init__.py +0 -0
  135. {plain-0.29.0 → plain-0.31.0}/plain/views/base.py +0 -0
  136. {plain-0.29.0 → plain-0.31.0}/plain/views/csrf.py +0 -0
  137. {plain-0.29.0 → plain-0.31.0}/plain/views/errors.py +0 -0
  138. {plain-0.29.0 → plain-0.31.0}/plain/views/exceptions.py +0 -0
  139. {plain-0.29.0 → plain-0.31.0}/plain/views/forms.py +0 -0
  140. {plain-0.29.0 → plain-0.31.0}/plain/views/objects.py +0 -0
  141. {plain-0.29.0 → plain-0.31.0}/plain/views/redirect.py +0 -0
  142. {plain-0.29.0 → plain-0.31.0}/plain/views/templates.py +0 -0
  143. {plain-0.29.0 → plain-0.31.0}/plain/wsgi.py +0 -0
  144. {plain-0.29.0 → plain-0.31.0}/tests/.bolt/assets_collected/assets.json +0 -0
  145. {plain-0.29.0 → plain-0.31.0}/tests/.gitignore +0 -0
  146. {plain-0.29.0 → plain-0.31.0}/tests/app/.gitignore +0 -0
  147. {plain-0.29.0 → plain-0.31.0}/tests/app/test/__init__.py +0 -0
  148. {plain-0.29.0 → plain-0.31.0}/tests/app/test/default_settings.py +0 -0
  149. {plain-0.29.0 → plain-0.31.0}/tests/conftest.py +0 -0
  150. {plain-0.29.0 → plain-0.31.0}/tests/test_cli.py +0 -0
  151. {plain-0.29.0 → plain-0.31.0}/tests/test_runtime.py +0 -0
  152. {plain-0.29.0 → plain-0.31.0}/tests/test_wsgi.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plain
3
- Version: 0.29.0
3
+ Version: 0.31.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
@@ -1,12 +1,11 @@
1
1
  from plain.runtime import settings
2
- from plain.urls import RouterBase, path, register_router, reverse
2
+ from plain.urls import Router, path, reverse
3
3
 
4
4
  from .fingerprints import get_fingerprinted_url_path
5
5
  from .views import AssetView
6
6
 
7
7
 
8
- @register_router
9
- class Router(RouterBase):
8
+ class AssetsRouter(Router):
10
9
  namespace = "assets"
11
10
  urls = [
12
11
  path("<path:path>", AssetView, name="asset"),
@@ -29,4 +28,4 @@ def get_asset_url(url_path):
29
28
  if settings.ASSETS_BASE_URL:
30
29
  return settings.ASSETS_BASE_URL + resolved_url_path
31
30
 
32
- return reverse(Router.namespace + ":asset", path=resolved_url_path)
31
+ return reverse(AssetsRouter.namespace + ":asset", path=resolved_url_path)
@@ -206,9 +206,9 @@ class AssetView(View):
206
206
  # or we're already looking at it.
207
207
  return
208
208
 
209
- from .urls import Router
209
+ from .urls import AssetsRouter
210
210
 
211
- namespace = Router.namespace
211
+ namespace = AssetsRouter.namespace
212
212
 
213
213
  return ResponseRedirect(
214
214
  redirect_to=reverse(f"{namespace}:asset", fingerprinted_url_path),
@@ -379,10 +379,10 @@ def create(package_name):
379
379
  # Create a urls.py file with a default namespace
380
380
  if not (package_dir / "urls.py").exists():
381
381
  (package_dir / "urls.py").write_text(
382
- f"""from plain.urls import path, RouterBase, register_router
382
+ f"""from plain.urls import path, Router
383
383
 
384
- @register_router
385
- class Router(RouterBase):
384
+
385
+ class {package_name.capitalize()}Router(Router):
386
386
  namespace = f"{package_name}"
387
387
  urls = [
388
388
  # path("", views.IndexView, name="index"),
@@ -422,10 +422,15 @@ def generate_secret_key():
422
422
  @plain_cli.command()
423
423
  @click.option("--flat", is_flag=True, help="List all URLs in a flat list")
424
424
  def urls(flat):
425
- """Print all URL patterns under settings.URLS_MODULE"""
425
+ """Print all URL patterns under settings.URLS_ROUTER"""
426
+ from plain.runtime import settings
426
427
  from plain.urls import URLResolver, get_resolver
427
428
 
428
- resolver = get_resolver()
429
+ if not settings.URLS_ROUTER:
430
+ click.secho("URLS_ROUTER is not set", fg="red")
431
+ sys.exit(1)
432
+
433
+ resolver = get_resolver(settings.URLS_ROUTER)
429
434
  if flat:
430
435
 
431
436
  def flat_list(patterns, prefix="", curr_ns=""):
@@ -43,7 +43,6 @@ __all__ = (
43
43
  "FloatField",
44
44
  "DecimalField",
45
45
  "JSONField",
46
- "SlugField",
47
46
  "TypedChoiceField",
48
47
  "UUIDField",
49
48
  )
@@ -900,16 +899,6 @@ class MultipleChoiceField(ChoiceField):
900
899
  return data.getlist(html_name)
901
900
 
902
901
 
903
- class SlugField(CharField):
904
- default_validators = [validators.validate_slug]
905
-
906
- def __init__(self, *, allow_unicode=False, **kwargs):
907
- self.allow_unicode = allow_unicode
908
- if self.allow_unicode:
909
- self.default_validators = [validators.validate_unicode_slug]
910
- super().__init__(**kwargs)
911
-
912
-
913
902
  class UUIDField(CharField):
914
903
  default_error_messages = {
915
904
  "invalid": "Enter a valid UUID.",
@@ -7,11 +7,12 @@ from . import Error, Warning, register
7
7
 
8
8
  @register
9
9
  def check_url_config(package_configs, **kwargs):
10
- if getattr(settings, "URLS_MODULE", None):
10
+ if getattr(settings, "URLS_ROUTER", None):
11
11
  from plain.urls import get_resolver
12
12
 
13
13
  resolver = get_resolver()
14
14
  return check_resolver(resolver)
15
+
15
16
  return []
16
17
 
17
18
 
@@ -33,7 +34,7 @@ def check_url_namespaces_unique(package_configs, **kwargs):
33
34
  """
34
35
  Warn if URL namespaces used in applications aren't unique.
35
36
  """
36
- if not getattr(settings, "URLS_MODULE", None):
37
+ if not getattr(settings, "URLS_ROUTER", None):
37
38
  return []
38
39
 
39
40
  from plain.urls import get_resolver
@@ -74,7 +74,7 @@ SECRET_KEY: str
74
74
  # secret key rotation.
75
75
  SECRET_KEY_FALLBACKS: list[str] = []
76
76
 
77
- URLS_MODULE = "app.urls"
77
+ URLS_ROUTER: str
78
78
 
79
79
  # List of upload handler classes to be applied in order.
80
80
  FILE_UPLOAD_HANDLERS = [
@@ -6,7 +6,7 @@ from .resolvers import (
6
6
  URLResolver,
7
7
  get_resolver,
8
8
  )
9
- from .routers import RouterBase, include, path, register_router
9
+ from .routers import Router, include, path
10
10
  from .utils import (
11
11
  reverse,
12
12
  reverse_lazy,
@@ -24,6 +24,5 @@ __all__ = [
24
24
  "register_converter",
25
25
  "reverse",
26
26
  "reverse_lazy",
27
- "RouterBase",
28
- "register_router",
27
+ "Router",
29
28
  ]
@@ -8,7 +8,6 @@ attributes of the resolved URL match.
8
8
 
9
9
  import functools
10
10
  import re
11
- from importlib import import_module
12
11
  from pickle import PicklingError
13
12
  from threading import local
14
13
  from urllib.parse import quote
@@ -17,6 +16,7 @@ from plain.preflight.urls import check_resolver
17
16
  from plain.runtime import settings
18
17
  from plain.utils.datastructures import MultiValueDict
19
18
  from plain.utils.http import RFC3986_SUBDELIMS, escape_leading_slashes
19
+ from plain.utils.module_loading import import_string
20
20
  from plain.utils.regex_helper import normalize
21
21
 
22
22
  from .exceptions import NoReverseMatch, Resolver404
@@ -87,30 +87,26 @@ class ResolverMatch:
87
87
  raise PicklingError(f"Cannot pickle {self.__class__.__qualname__}.")
88
88
 
89
89
 
90
- def get_resolver(urls_module=None):
91
- if urls_module is None:
92
- urls_module = settings.URLS_MODULE
90
+ def get_resolver(router=None):
91
+ if router is None:
92
+ router = settings.URLS_ROUTER
93
93
 
94
- return _get_cached_resolver(urls_module)
94
+ return _get_cached_resolver(router)
95
95
 
96
96
 
97
97
  @functools.cache
98
- def _get_cached_resolver(urls_module):
99
- from .routers import routers_registry
98
+ def _get_cached_resolver(router):
99
+ if isinstance(router, str):
100
+ # Do this inside the cached call, primarily for the URLS_ROUTER
101
+ router_class = import_string(router)
102
+ router = router_class()
100
103
 
101
- if isinstance(urls_module, str):
102
- # Need to trigger an import in order for the @register_router
103
- # decorators to run. So this is a sensible entrypoint to do that,
104
- # usually just for the root URLS_MODULE but could be for anything.
105
- urls_module = import_module(urls_module)
106
-
107
- router = routers_registry.get_module_router(urls_module)
108
104
  return URLResolver(pattern=RegexPattern(r"^/"), router=router)
109
105
 
110
106
 
111
107
  @functools.cache
112
108
  def get_ns_resolver(ns_pattern, resolver, converters):
113
- from .routers import RouterBase
109
+ from .routers import Router
114
110
 
115
111
  # Build a namespaced resolver for the given parent urls_module pattern.
116
112
  # This makes it possible to have captured parameters in the parent
@@ -118,13 +114,13 @@ def get_ns_resolver(ns_pattern, resolver, converters):
118
114
  pattern = RegexPattern(ns_pattern)
119
115
  pattern.converters = dict(converters)
120
116
 
121
- class _NestedRouter(RouterBase):
117
+ class _NestedRouter(Router):
122
118
  namespace = ""
123
119
  urls = resolver.url_patterns
124
120
 
125
121
  ns_resolver = URLResolver(pattern=pattern, router=_NestedRouter())
126
122
 
127
- class _NamespacedRouter(RouterBase):
123
+ class _NamespacedRouter(Router):
128
124
  namespace = ""
129
125
  urls = [ns_resolver]
130
126
 
@@ -1,9 +1,6 @@
1
1
  import re
2
- from types import ModuleType
3
2
  from typing import TYPE_CHECKING
4
3
 
5
- from plain.exceptions import ImproperlyConfigured
6
-
7
4
  from .patterns import RegexPattern, RoutePattern, URLPattern
8
5
  from .resolvers import (
9
6
  URLResolver,
@@ -13,7 +10,7 @@ if TYPE_CHECKING:
13
10
  from plain.views import View
14
11
 
15
12
 
16
- class RouterBase:
13
+ class Router:
17
14
  """
18
15
  Base class for defining url patterns.
19
16
 
@@ -25,37 +22,8 @@ class RouterBase:
25
22
  urls: list
26
23
 
27
24
 
28
- class RoutersRegistry:
29
- """Keep track of all the Routers that are explicitly registered in packages."""
30
-
31
- def __init__(self):
32
- self._routers = {}
33
-
34
- def register_router(self, router_class):
35
- router = (
36
- router_class()
37
- ) # Don't necessarily need to instantiate it yet, but will likely add methods.
38
- router_module_name = router_class.__module__
39
- self._routers[router_module_name] = router
40
- return router
41
-
42
- def get_module_router(self, module):
43
- if isinstance(module, str):
44
- module_name = module
45
- else:
46
- module_name = module.__name__
47
-
48
- try:
49
- return self._routers[module_name]
50
- except KeyError as e:
51
- registered_routers = ", ".join(self._routers.keys()) or "None"
52
- raise ImproperlyConfigured(
53
- f"Router {module_name} is not registered with the resolver. Use @register_router on the Router class in urls.py.\n\nRegistered routers: {registered_routers}"
54
- ) from e
55
-
56
-
57
25
  def include(
58
- route: str | re.Pattern, module_or_urls: list | tuple | str | ModuleType
26
+ route: str | re.Pattern, router_or_urls: list | tuple | str | Router
59
27
  ) -> URLResolver:
60
28
  """
61
29
  Include URLs from another module or a nested list of URL patterns.
@@ -67,23 +35,26 @@ def include(
67
35
  else:
68
36
  raise TypeError("include() route must be a string or regex")
69
37
 
70
- if isinstance(module_or_urls, list | tuple):
38
+ if isinstance(router_or_urls, list | tuple):
71
39
  # We were given an explicit list of sub-patterns,
72
40
  # so we generate a router for it
73
- class _IncludeRouter(RouterBase):
41
+ class _IncludeRouter(Router):
74
42
  namespace = ""
75
- urls = module_or_urls
43
+ urls = router_or_urls
76
44
 
77
45
  return URLResolver(pattern=pattern, router=_IncludeRouter())
78
- else:
79
- # We were given a module, so we need to look up the router for that module
80
- module = module_or_urls
81
- router = routers_registry.get_module_router(module)
46
+ elif isinstance(router_or_urls, type) and issubclass(router_or_urls, Router):
47
+ router_class = router_or_urls
48
+ router = router_class()
82
49
 
83
50
  return URLResolver(
84
51
  pattern=pattern,
85
52
  router=router,
86
53
  )
54
+ else:
55
+ raise TypeError(
56
+ f"include() urls must be a list, tuple, or Router class (not a Router() instance): {router_or_urls}"
57
+ )
87
58
 
88
59
 
89
60
  def path(route: str | re.Pattern, view: "View", *, name: str = "") -> URLPattern:
@@ -116,12 +87,3 @@ def path(route: str | re.Pattern, view: "View", *, name: str = "") -> URLPattern
116
87
  return URLPattern(pattern=pattern, view=view, name=name)
117
88
 
118
89
  raise TypeError("view must be a View class or View.as_view()")
119
-
120
-
121
- routers_registry = RoutersRegistry()
122
-
123
-
124
- def register_router(router_class):
125
- """Decorator to register a router class"""
126
- routers_registry.register_router(router_class)
127
- return router_class # Return the class, not the instance
@@ -237,21 +237,6 @@ class EmailValidator:
237
237
 
238
238
  validate_email = EmailValidator()
239
239
 
240
- slug_re = _lazy_re_compile(r"^[-a-zA-Z0-9_]+\Z")
241
- validate_slug = RegexValidator(
242
- slug_re,
243
- # Translators: "letters" means latin letters: a-z and A-Z.
244
- "Enter a valid “slug” consisting of letters, numbers, underscores or hyphens.",
245
- "invalid",
246
- )
247
-
248
- slug_unicode_re = _lazy_re_compile(r"^[-\w]+\Z")
249
- validate_unicode_slug = RegexValidator(
250
- slug_unicode_re,
251
- "Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or hyphens."
252
- "invalid",
253
- )
254
-
255
240
 
256
241
  def validate_ipv4_address(value):
257
242
  try:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "plain"
3
- version = "0.29.0"
3
+ version = "0.31.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"
@@ -1,6 +1,8 @@
1
1
  SECRET_KEY = "secret"
2
2
  DEBUG = True
3
3
 
4
+ URLS_ROUTER = "app.urls.AppRouter"
5
+
4
6
  INSTALLED_PACKAGES = [
5
7
  "app.test",
6
8
  ]
@@ -1,4 +1,4 @@
1
- from plain.urls import RouterBase, path, register_router
1
+ from plain.urls import Router, path
2
2
  from plain.views import View
3
3
 
4
4
 
@@ -7,8 +7,7 @@ class TestView(View):
7
7
  return "Hello, world!"
8
8
 
9
9
 
10
- @register_router
11
- class Router(RouterBase):
10
+ class AppRouter(Router):
12
11
  namespace = ""
13
12
  urls = [
14
13
  path("", TestView),
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