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.
- {plain-0.29.0 → plain-0.31.0}/PKG-INFO +1 -1
- {plain-0.29.0 → plain-0.31.0}/plain/assets/urls.py +3 -4
- {plain-0.29.0 → plain-0.31.0}/plain/assets/views.py +2 -2
- {plain-0.29.0 → plain-0.31.0}/plain/cli/core.py +10 -5
- {plain-0.29.0 → plain-0.31.0}/plain/forms/fields.py +0 -11
- {plain-0.29.0 → plain-0.31.0}/plain/preflight/urls.py +3 -2
- {plain-0.29.0 → plain-0.31.0}/plain/runtime/global_settings.py +1 -1
- {plain-0.29.0 → plain-0.31.0}/plain/urls/__init__.py +2 -3
- {plain-0.29.0 → plain-0.31.0}/plain/urls/resolvers.py +13 -17
- {plain-0.29.0 → plain-0.31.0}/plain/urls/routers.py +12 -50
- {plain-0.29.0 → plain-0.31.0}/plain/validators.py +0 -15
- {plain-0.29.0 → plain-0.31.0}/pyproject.toml +1 -1
- {plain-0.29.0 → plain-0.31.0}/tests/app/settings.py +2 -0
- {plain-0.29.0 → plain-0.31.0}/tests/app/urls.py +2 -3
- {plain-0.29.0 → plain-0.31.0}/.gitignore +0 -0
- {plain-0.29.0 → plain-0.31.0}/LICENSE +0 -0
- {plain-0.29.0 → plain-0.31.0}/README.md +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/README.md +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/__main__.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/assets/README.md +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/assets/__init__.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/assets/compile.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/assets/finders.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/assets/fingerprints.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/cli/README.md +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/cli/__init__.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/cli/formatting.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/cli/print.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/cli/registry.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/cli/startup.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/csrf/README.md +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/csrf/middleware.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/csrf/views.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/debug.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/exceptions.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/forms/README.md +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/forms/__init__.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/forms/boundfield.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/forms/exceptions.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/forms/forms.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/http/README.md +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/http/__init__.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/http/cookie.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/http/multipartparser.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/http/request.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/http/response.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/internal/__init__.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/internal/files/README.md +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/internal/files/__init__.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/internal/files/base.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/internal/files/locks.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/internal/files/move.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/internal/files/temp.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/internal/files/uploadedfile.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/internal/files/uploadhandler.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/internal/files/utils.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/internal/handlers/__init__.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/internal/handlers/base.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/internal/handlers/exception.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/internal/handlers/wsgi.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/internal/middleware/__init__.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/internal/middleware/headers.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/internal/middleware/https.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/internal/middleware/slash.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/json.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/logs/README.md +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/logs/__init__.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/logs/configure.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/logs/loggers.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/logs/utils.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/packages/README.md +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/packages/__init__.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/packages/config.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/packages/registry.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/paginator.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/preflight/README.md +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/preflight/__init__.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/preflight/files.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/preflight/messages.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/preflight/registry.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/preflight/security.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/runtime/README.md +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/runtime/__init__.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/runtime/user_settings.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/signals/README.md +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/signals/__init__.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/signals/dispatch/__init__.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/signals/dispatch/dispatcher.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/signals/dispatch/license.txt +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/signing.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/templates/README.md +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/templates/__init__.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/templates/core.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/templates/jinja/README.md +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/templates/jinja/__init__.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/templates/jinja/environments.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/templates/jinja/extensions.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/templates/jinja/filters.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/templates/jinja/globals.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/test/README.md +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/test/__init__.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/test/client.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/urls/README.md +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/urls/converters.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/urls/exceptions.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/urls/patterns.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/urls/utils.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/utils/README.md +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/utils/__init__.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/utils/cache.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/utils/connection.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/utils/crypto.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/utils/datastructures.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/utils/dateparse.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/utils/deconstruct.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/utils/decorators.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/utils/duration.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/utils/encoding.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/utils/functional.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/utils/hashable.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/utils/html.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/utils/http.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/utils/inspect.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/utils/ipv6.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/utils/itercompat.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/utils/module_loading.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/utils/regex_helper.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/utils/safestring.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/utils/text.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/utils/timesince.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/utils/timezone.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/utils/tree.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/views/README.md +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/views/__init__.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/views/base.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/views/csrf.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/views/errors.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/views/exceptions.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/views/forms.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/views/objects.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/views/redirect.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/views/templates.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/plain/wsgi.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/tests/.bolt/assets_collected/assets.json +0 -0
- {plain-0.29.0 → plain-0.31.0}/tests/.gitignore +0 -0
- {plain-0.29.0 → plain-0.31.0}/tests/app/.gitignore +0 -0
- {plain-0.29.0 → plain-0.31.0}/tests/app/test/__init__.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/tests/app/test/default_settings.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/tests/conftest.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/tests/test_cli.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/tests/test_runtime.py +0 -0
- {plain-0.29.0 → plain-0.31.0}/tests/test_wsgi.py +0 -0
@@ -1,12 +1,11 @@
|
|
1
1
|
from plain.runtime import settings
|
2
|
-
from plain.urls import
|
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
|
-
|
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(
|
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
|
209
|
+
from .urls import AssetsRouter
|
210
210
|
|
211
|
-
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,
|
382
|
+
f"""from plain.urls import path, Router
|
383
383
|
|
384
|
-
|
385
|
-
class Router(
|
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.
|
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
|
-
|
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, "
|
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, "
|
37
|
+
if not getattr(settings, "URLS_ROUTER", None):
|
37
38
|
return []
|
38
39
|
|
39
40
|
from plain.urls import get_resolver
|
@@ -6,7 +6,7 @@ from .resolvers import (
|
|
6
6
|
URLResolver,
|
7
7
|
get_resolver,
|
8
8
|
)
|
9
|
-
from .routers import
|
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
|
-
"
|
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(
|
91
|
-
if
|
92
|
-
|
90
|
+
def get_resolver(router=None):
|
91
|
+
if router is None:
|
92
|
+
router = settings.URLS_ROUTER
|
93
93
|
|
94
|
-
return _get_cached_resolver(
|
94
|
+
return _get_cached_resolver(router)
|
95
95
|
|
96
96
|
|
97
97
|
@functools.cache
|
98
|
-
def _get_cached_resolver(
|
99
|
-
|
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
|
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(
|
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(
|
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
|
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,
|
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(
|
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(
|
41
|
+
class _IncludeRouter(Router):
|
74
42
|
namespace = ""
|
75
|
-
urls =
|
43
|
+
urls = router_or_urls
|
76
44
|
|
77
45
|
return URLResolver(pattern=pattern, router=_IncludeRouter())
|
78
|
-
|
79
|
-
|
80
|
-
|
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,4 +1,4 @@
|
|
1
|
-
from plain.urls import
|
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
|
-
|
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
|
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
|