plain 0.28.0__py3-none-any.whl → 0.30.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plain/__main__.py +1 -1
- plain/assets/urls.py +3 -4
- plain/assets/views.py +2 -2
- plain/cli/README.md +0 -16
- plain/cli/__init__.py +2 -2
- plain/cli/{cli.py → core.py} +20 -24
- plain/cli/registry.py +62 -0
- plain/packages/config.py +0 -2
- plain/preflight/urls.py +3 -2
- plain/runtime/global_settings.py +1 -1
- plain/urls/__init__.py +2 -3
- plain/urls/resolvers.py +13 -17
- plain/urls/routers.py +12 -50
- {plain-0.28.0.dist-info → plain-0.30.0.dist-info}/METADATA +1 -1
- {plain-0.28.0.dist-info → plain-0.30.0.dist-info}/RECORD +18 -18
- plain-0.30.0.dist-info/entry_points.txt +2 -0
- plain/cli/packages.py +0 -94
- plain-0.28.0.dist-info/entry_points.txt +0 -2
- {plain-0.28.0.dist-info → plain-0.30.0.dist-info}/WHEEL +0 -0
- {plain-0.28.0.dist-info → plain-0.30.0.dist-info}/licenses/LICENSE +0 -0
plain/__main__.py
CHANGED
plain/assets/urls.py
CHANGED
@@ -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)
|
plain/assets/views.py
CHANGED
@@ -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),
|
plain/cli/README.md
CHANGED
@@ -99,19 +99,3 @@ then any commands you defined.
|
|
99
99
|
$ plain <pkg> hello
|
100
100
|
Hello, world!
|
101
101
|
```
|
102
|
-
|
103
|
-
### Add CLI commands to published packages
|
104
|
-
|
105
|
-
Some packages, like [plain-dev](https://plainframework.com/docs/plain-dev/),
|
106
|
-
never show up in `INSTALLED_PACKAGES` but still have CLI commands.
|
107
|
-
These are detected via Python entry points.
|
108
|
-
|
109
|
-
An example with `pyproject.toml` and UV:
|
110
|
-
|
111
|
-
```toml
|
112
|
-
# pyproject.toml
|
113
|
-
[project.entry-points."plain.cli"]
|
114
|
-
"dev" = "plain.dev:cli"
|
115
|
-
"pre-commit" = "plain.dev.precommit:cli"
|
116
|
-
"contrib" = "plain.dev.contribute:cli"
|
117
|
-
```
|
plain/cli/__init__.py
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
from .
|
1
|
+
from .registry import register_cli
|
2
2
|
|
3
|
-
__all__ = ["
|
3
|
+
__all__ = ["register_cli"]
|
plain/cli/{cli.py → core.py}
RENAMED
@@ -1,4 +1,3 @@
|
|
1
|
-
import importlib
|
2
1
|
import os
|
3
2
|
import shutil
|
4
3
|
import subprocess
|
@@ -6,7 +5,6 @@ import sys
|
|
6
5
|
import tomllib
|
7
6
|
import traceback
|
8
7
|
from importlib.metadata import entry_points
|
9
|
-
from importlib.util import find_spec
|
10
8
|
from pathlib import Path
|
11
9
|
|
12
10
|
import click
|
@@ -20,7 +18,7 @@ from plain.packages import packages_registry
|
|
20
18
|
from plain.utils.crypto import get_random_string
|
21
19
|
|
22
20
|
from .formatting import PlainContext
|
23
|
-
from .
|
21
|
+
from .registry import cli_registry
|
24
22
|
|
25
23
|
|
26
24
|
@click.group()
|
@@ -381,10 +379,10 @@ def create(package_name):
|
|
381
379
|
# Create a urls.py file with a default namespace
|
382
380
|
if not (package_dir / "urls.py").exists():
|
383
381
|
(package_dir / "urls.py").write_text(
|
384
|
-
f"""from plain.urls import path,
|
382
|
+
f"""from plain.urls import path, Router
|
385
383
|
|
386
|
-
|
387
|
-
class Router(
|
384
|
+
|
385
|
+
class {package_name.capitalize()}Router(Router):
|
388
386
|
namespace = f"{package_name}"
|
389
387
|
urls = [
|
390
388
|
# path("", views.IndexView, name="index"),
|
@@ -424,10 +422,15 @@ def generate_secret_key():
|
|
424
422
|
@plain_cli.command()
|
425
423
|
@click.option("--flat", is_flag=True, help="List all URLs in a flat list")
|
426
424
|
def urls(flat):
|
427
|
-
"""Print all URL patterns under settings.
|
425
|
+
"""Print all URL patterns under settings.URLS_ROUTER"""
|
426
|
+
from plain.runtime import settings
|
428
427
|
from plain.urls import URLResolver, get_resolver
|
429
428
|
|
430
|
-
|
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)
|
431
434
|
if flat:
|
432
435
|
|
433
436
|
def flat_list(patterns, prefix="", curr_ns=""):
|
@@ -500,25 +503,21 @@ def urls(flat):
|
|
500
503
|
print_tree(resolver.url_patterns)
|
501
504
|
|
502
505
|
|
503
|
-
class
|
506
|
+
class CLIRegistryGroup(click.Group):
|
504
507
|
"""
|
505
|
-
|
508
|
+
Click Group that exposes commands from the CLI registry.
|
506
509
|
"""
|
507
510
|
|
508
|
-
|
511
|
+
def __init__(self, *args, **kwargs):
|
512
|
+
super().__init__(*args, **kwargs)
|
513
|
+
cli_registry.import_modules()
|
509
514
|
|
510
515
|
def list_commands(self, ctx):
|
511
|
-
|
512
|
-
return ["app"]
|
513
|
-
else:
|
514
|
-
return []
|
516
|
+
return sorted(cli_registry.get_commands().keys())
|
515
517
|
|
516
518
|
def get_command(self, ctx, name):
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
cli = importlib.import_module(self.MODULE_NAME)
|
521
|
-
return cli.cli
|
519
|
+
commands = cli_registry.get_commands()
|
520
|
+
return commands.get(name)
|
522
521
|
|
523
522
|
|
524
523
|
class PlainCommandCollection(click.CommandCollection):
|
@@ -531,9 +530,7 @@ class PlainCommandCollection(click.CommandCollection):
|
|
531
530
|
plain.runtime.setup()
|
532
531
|
|
533
532
|
sources = [
|
534
|
-
|
535
|
-
EntryPointGroup(),
|
536
|
-
AppCLIGroup(),
|
533
|
+
CLIRegistryGroup(),
|
537
534
|
plain_cli,
|
538
535
|
]
|
539
536
|
except plain.runtime.AppPathNotFound:
|
@@ -545,7 +542,6 @@ class PlainCommandCollection(click.CommandCollection):
|
|
545
542
|
)
|
546
543
|
|
547
544
|
sources = [
|
548
|
-
EntryPointGroup(),
|
549
545
|
plain_cli,
|
550
546
|
]
|
551
547
|
except ImproperlyConfigured as e:
|
plain/cli/registry.py
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
from importlib import import_module
|
2
|
+
from importlib.util import find_spec
|
3
|
+
|
4
|
+
from plain.packages import packages_registry
|
5
|
+
|
6
|
+
|
7
|
+
class CLIRegistry:
|
8
|
+
def __init__(self):
|
9
|
+
self._commands = {}
|
10
|
+
|
11
|
+
def register_command(self, cmd, name):
|
12
|
+
"""
|
13
|
+
Register a CLI command or group with the specified name.
|
14
|
+
"""
|
15
|
+
self._commands[name] = cmd
|
16
|
+
|
17
|
+
def import_modules(self):
|
18
|
+
"""
|
19
|
+
Import modules from installed packages and app to trigger registration.
|
20
|
+
"""
|
21
|
+
# Import from installed packages
|
22
|
+
for package_config in packages_registry.get_package_configs():
|
23
|
+
import_name = f"{package_config.name}.cli"
|
24
|
+
try:
|
25
|
+
import_module(import_name)
|
26
|
+
except ModuleNotFoundError:
|
27
|
+
pass
|
28
|
+
|
29
|
+
# Import from app
|
30
|
+
import_name = "app.cli"
|
31
|
+
if find_spec(import_name):
|
32
|
+
try:
|
33
|
+
import_module(import_name)
|
34
|
+
except ModuleNotFoundError:
|
35
|
+
pass
|
36
|
+
|
37
|
+
def get_commands(self):
|
38
|
+
"""
|
39
|
+
Get all registered commands.
|
40
|
+
"""
|
41
|
+
return self._commands
|
42
|
+
|
43
|
+
|
44
|
+
cli_registry = CLIRegistry()
|
45
|
+
|
46
|
+
|
47
|
+
def register_cli(name):
|
48
|
+
"""
|
49
|
+
Register a CLI command or group with the given name.
|
50
|
+
|
51
|
+
Usage:
|
52
|
+
@register_cli("users")
|
53
|
+
@click.group()
|
54
|
+
def users_cli():
|
55
|
+
pass
|
56
|
+
"""
|
57
|
+
|
58
|
+
def wrapper(cmd):
|
59
|
+
cli_registry.register_command(cmd, name)
|
60
|
+
return cmd
|
61
|
+
|
62
|
+
return wrapper
|
plain/packages/config.py
CHANGED
@@ -10,8 +10,6 @@ CONFIG_MODULE_NAME = "config"
|
|
10
10
|
class PackageConfig:
|
11
11
|
"""Class representing a Plain application and its configuration."""
|
12
12
|
|
13
|
-
migrations_module = "migrations"
|
14
|
-
|
15
13
|
def __init__(self, name, *, label=""):
|
16
14
|
# Full Python path to the application e.g. 'plain.admin.admin'.
|
17
15
|
self.name = name
|
plain/preflight/urls.py
CHANGED
@@ -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
|
plain/runtime/global_settings.py
CHANGED
plain/urls/__init__.py
CHANGED
@@ -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
|
]
|
plain/urls/resolvers.py
CHANGED
@@ -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
|
|
plain/urls/routers.py
CHANGED
@@ -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
|
@@ -1,5 +1,5 @@
|
|
1
1
|
plain/README.md,sha256=nW3Ioj3IxPb6aoCGaFMN2n7Cd7LMx0s8Lph6pMkKnh4,8
|
2
|
-
plain/__main__.py,sha256=
|
2
|
+
plain/__main__.py,sha256=GK39854Lc_LO_JP8DzY9Y2MIQ4cQEl7SXFJy244-lC8,110
|
3
3
|
plain/debug.py,sha256=abPkJY4aSbBYGEYSZST_ZY3ohXPGDdz9uWQBYRqfd3M,730
|
4
4
|
plain/exceptions.py,sha256=Z9cbPE5im_Y-bjVq8cqC85gBoqOr80rLFG5wTKixrwE,5894
|
5
5
|
plain/json.py,sha256=McJdsbMT1sYwkGRG--f2NSZz0hVXPMix9x3nKaaak2o,1262
|
@@ -12,14 +12,14 @@ plain/assets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
12
|
plain/assets/compile.py,sha256=Qg-rMWykij_Jheq4THrPFWlmYv07ihHzWiNsD815HYE,3336
|
13
13
|
plain/assets/finders.py,sha256=rhkHG5QW3H3IlBGHB5WJf9J6VTdDWgUC0qEs6u2Z4RQ,1233
|
14
14
|
plain/assets/fingerprints.py,sha256=1NKAnnXVlncY5iimXztr0NL3RIjBKsNlZRIe6nmItJc,931
|
15
|
-
plain/assets/urls.py,sha256=
|
16
|
-
plain/assets/views.py,sha256=
|
17
|
-
plain/cli/README.md,sha256=
|
18
|
-
plain/cli/__init__.py,sha256=
|
19
|
-
plain/cli/
|
15
|
+
plain/assets/urls.py,sha256=3BSvp5B--V6Q03BizmZ6qa2dUamF1r5IdlFbwD401eU,962
|
16
|
+
plain/assets/views.py,sha256=s6ERHfgRyoZ8P6mH9ahGZxU1LyN8WByAiJAjlwBCl-k,9265
|
17
|
+
plain/cli/README.md,sha256=t3k4jmSK0QFALO3bVWTUsJC09KhY4CvauStTvVLLUdI,1922
|
18
|
+
plain/cli/__init__.py,sha256=6w9T7K2WrPwh6DcaMb2oNt_CWU6Bc57nUTO2Bt1p38Y,63
|
19
|
+
plain/cli/core.py,sha256=8UelQ8vif6ahUwYnJc7jWy2t7Ml68oyEkt0MtQMycxE,18411
|
20
20
|
plain/cli/formatting.py,sha256=1hZH13y1qwHcU2K2_Na388nw9uvoeQH8LrWL-O9h8Yc,2207
|
21
|
-
plain/cli/packages.py,sha256=GLvDgQ1o93tSHae_B2i0YNimpt3LGu4QMQpFYrO48d8,2758
|
22
21
|
plain/cli/print.py,sha256=XraUYrgODOJquIiEv78wSCYGRBplHXtXSS9QtFG5hqY,217
|
22
|
+
plain/cli/registry.py,sha256=yKVMSDjW8g10nlV9sPXFGJQmhC_U-k4J4kM7N2OQVLA,1467
|
23
23
|
plain/cli/startup.py,sha256=3LIz9JrIZoF52Sa0j0SCypQwEaBDkhvuGaBdtiQLr5Q,680
|
24
24
|
plain/csrf/README.md,sha256=RXMWMtHmzf30gVVNOfj0kD4xlSqFIPgJh-n7dIciaEM,163
|
25
25
|
plain/csrf/middleware.py,sha256=FYhT7KPJ664Sm0nKjeej1OIXalvVTYiotQX3ytI0dfY,17417
|
@@ -61,7 +61,7 @@ plain/logs/loggers.py,sha256=iz9SYcwP9w5QAuwpULl48SFkVyJuuMoQ_fdLgdCHpNg,2121
|
|
61
61
|
plain/logs/utils.py,sha256=9UzdCCQXJinGDs71Ngw297mlWkhgZStSd67ya4NOW98,1257
|
62
62
|
plain/packages/README.md,sha256=Vq1Nw3mmEmZ2IriQavuVi4BjcQC2nb8k7YIbnm8QjIg,799
|
63
63
|
plain/packages/__init__.py,sha256=OpQny0xLplPdPpozVUUkrW2gB-IIYyDT1b4zMzOcCC4,160
|
64
|
-
plain/packages/config.py,sha256=
|
64
|
+
plain/packages/config.py,sha256=uOO7uE9jajqDhqFBafJQ3ZnfLmQiHikTzOSJ1AlP7ZM,3289
|
65
65
|
plain/packages/registry.py,sha256=Aklno7y7UrBZlidtUR_YO3B5xqF46UbUtalReNcYHm8,7937
|
66
66
|
plain/preflight/README.md,sha256=-PKVd0RBMh4ROiMkegPS2PgvT1Kq9qqN1KfNkmUSdFc,177
|
67
67
|
plain/preflight/__init__.py,sha256=H-TNRvaddPtOGmv4RXoc1fxDV1AOb7_K3u7ECF8mV58,607
|
@@ -69,10 +69,10 @@ plain/preflight/files.py,sha256=wbHCNgps7o1c1zQNBd8FDCaVaqX90UwuvLgEQ_DbUpY,510
|
|
69
69
|
plain/preflight/messages.py,sha256=HwatjA6MRFfzFAnSOa_uAw1Pvk_CLuNfW3IYi71_1Mk,2322
|
70
70
|
plain/preflight/registry.py,sha256=7s7f_iEwURzv-Ye515P5lJWcHltd5Ca2fsX1Wpbf1wQ,2306
|
71
71
|
plain/preflight/security.py,sha256=sNpv5AHobPcaO48cOUGRNe2EjusTducjY8vyShR8EhI,2645
|
72
|
-
plain/preflight/urls.py,sha256=
|
72
|
+
plain/preflight/urls.py,sha256=Wlz6n8nTBPzKhDjcT1helwrsLe4PKRlUPLxuxvjuZz4,2882
|
73
73
|
plain/runtime/README.md,sha256=Q8VVO7JRGuYrDxzuYL6ptoilhclbecxKzpRXKgbWGkU,2061
|
74
74
|
plain/runtime/__init__.py,sha256=o2RVETiL8U0lMFBpbtfnxflhw_4MFllMV6CEpX3RqZs,1965
|
75
|
-
plain/runtime/global_settings.py,sha256=
|
75
|
+
plain/runtime/global_settings.py,sha256=DUx_RPZsyplBV9i8sDy7S1fZdU9LUuxqnXYEBTOMzEI,5490
|
76
76
|
plain/runtime/user_settings.py,sha256=uRHHVfzUvHon91_fOKj7K2WaBYwJ1gCPLfeXqKj5CTs,10902
|
77
77
|
plain/signals/README.md,sha256=cd3tKEgH-xc88CUWyDxl4-qv-HBXx8VT32BXVwA5azA,230
|
78
78
|
plain/signals/__init__.py,sha256=eAs0kLqptuP6I31dWXeAqRNji3svplpAV4Ez6ktjwXM,131
|
@@ -92,12 +92,12 @@ plain/test/README.md,sha256=Zso3Ir7a8vQerzKB6egjROQWkpveLAbscn7VTROPAiU,37
|
|
92
92
|
plain/test/__init__.py,sha256=rXe88Y602NP8DBnReSyXb7dUzKoWweLuT43j-qwOUl4,138
|
93
93
|
plain/test/client.py,sha256=36Cir1KbrNEhza-5FTgdwxe1iZ5zaEDtESrqcnyndnY,31318
|
94
94
|
plain/urls/README.md,sha256=pWnCvgYkWN7rG7hSyBOtX4ZUP3iO7FhqM6lvwwYll6c,33
|
95
|
-
plain/urls/__init__.py,sha256=
|
95
|
+
plain/urls/__init__.py,sha256=DFO2OL1IllHW5USPIb5uYvvzf_G-Bl0Qu1zrRLHmWyM,542
|
96
96
|
plain/urls/converters.py,sha256=s2JZVOdzZC16lgobsI93hygcdH5L0Kj4742WEkXsVcs,1193
|
97
97
|
plain/urls/exceptions.py,sha256=q4iPh3Aa-zHbA-tw8v6WyX1J1n5WdAady2xvxFuyXB0,114
|
98
98
|
plain/urls/patterns.py,sha256=bU_xfhZbKMSgRG9OJ8w_NSuYRm_9zGnqoz_WY44fhUk,9358
|
99
|
-
plain/urls/resolvers.py,sha256=
|
100
|
-
plain/urls/routers.py,sha256=
|
99
|
+
plain/urls/resolvers.py,sha256=PyqbO1JIoJq2ayCSmONW_6O8a3vM7cTVbqQJdCJHIK0,15218
|
100
|
+
plain/urls/routers.py,sha256=iEsQtTpPNDDVn7r_BQX84FESGSjOeD5qgyO_ep5rzaU,2819
|
101
101
|
plain/urls/utils.py,sha256=WiGq6hHI-5DLFOxCQTAZ2qm0J-UdGosLcjuxlfK6_Tg,2137
|
102
102
|
plain/utils/README.md,sha256=Bf5OG-MkOJDz_U8RGVreDfAI4M4nnPaLtk-LdinxHSc,99
|
103
103
|
plain/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -134,8 +134,8 @@ plain/views/forms.py,sha256=RhlaUcZCkeqokY_fvv-NOS-kgZAG4XhDLOPbf9K_Zlc,2691
|
|
134
134
|
plain/views/objects.py,sha256=g5Lzno0Zsv0K449UpcCtxwCoO7WMRAWqKlxxV2V0_qg,8263
|
135
135
|
plain/views/redirect.py,sha256=9zHZgKvtSkdrMX9KmsRM8hJTPmBktxhc4d8OitbuniI,1724
|
136
136
|
plain/views/templates.py,sha256=cBkFNCSXgVi8cMqQbhsqJ4M_rIQYVl8cUvq9qu4YIes,1951
|
137
|
-
plain-0.
|
138
|
-
plain-0.
|
139
|
-
plain-0.
|
140
|
-
plain-0.
|
141
|
-
plain-0.
|
137
|
+
plain-0.30.0.dist-info/METADATA,sha256=9jwyLull3SgsxtScELF6OmST3HcsJIvCyuy5OLA0Bmc,319
|
138
|
+
plain-0.30.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
139
|
+
plain-0.30.0.dist-info/entry_points.txt,sha256=1Ys2lsSeMepD1vz8RSrJopna0RQfUd951vYvCRsvl6A,45
|
140
|
+
plain-0.30.0.dist-info/licenses/LICENSE,sha256=m0D5O7QoH9l5Vz_rrX_9r-C8d9UNr_ciK6Qwac7o6yo,3175
|
141
|
+
plain-0.30.0.dist-info/RECORD,,
|
plain/cli/packages.py
DELETED
@@ -1,94 +0,0 @@
|
|
1
|
-
import importlib
|
2
|
-
from importlib.metadata import entry_points
|
3
|
-
from importlib.util import find_spec
|
4
|
-
|
5
|
-
import click
|
6
|
-
|
7
|
-
from plain.packages import packages_registry
|
8
|
-
|
9
|
-
|
10
|
-
class InstalledPackagesGroup(click.Group):
|
11
|
-
"""
|
12
|
-
Packages in INSTALLED_PACKAGES with a cli.py module
|
13
|
-
will be discovered automatically.
|
14
|
-
"""
|
15
|
-
|
16
|
-
PLAIN_APPS_PREFIX = "plain."
|
17
|
-
APP_PREFIX = "app."
|
18
|
-
MODULE_NAME = "cli"
|
19
|
-
|
20
|
-
def list_commands(self, ctx):
|
21
|
-
command_names = []
|
22
|
-
|
23
|
-
# Get installed packages with a cli.py module
|
24
|
-
for app in packages_registry.get_package_configs():
|
25
|
-
if not find_spec(f"{app.name}.{self.MODULE_NAME}"):
|
26
|
-
continue
|
27
|
-
|
28
|
-
cli_name = app.name
|
29
|
-
|
30
|
-
# Change plain.{pkg} to just {pkg}
|
31
|
-
if cli_name.startswith(self.PLAIN_APPS_PREFIX):
|
32
|
-
cli_name = cli_name[len(self.PLAIN_APPS_PREFIX) :]
|
33
|
-
|
34
|
-
# Change app.{pkg} to just {pkg}
|
35
|
-
if cli_name.startswith(self.APP_PREFIX):
|
36
|
-
cli_name = cli_name[len(self.APP_PREFIX) :]
|
37
|
-
|
38
|
-
if cli_name in command_names:
|
39
|
-
raise ValueError(
|
40
|
-
f"Duplicate command name {cli_name} found in installed packages."
|
41
|
-
)
|
42
|
-
|
43
|
-
command_names.append(cli_name)
|
44
|
-
|
45
|
-
return command_names
|
46
|
-
|
47
|
-
def get_command(self, ctx, name):
|
48
|
-
# Try it as plain.x, app.x, and just x (we don't know ahead of time which it is)
|
49
|
-
for n in [self.PLAIN_APPS_PREFIX + name, self.APP_PREFIX + name, name]:
|
50
|
-
try:
|
51
|
-
if not find_spec(n):
|
52
|
-
# plain.<name> doesn't exist at all
|
53
|
-
continue
|
54
|
-
except ModuleNotFoundError:
|
55
|
-
continue
|
56
|
-
|
57
|
-
try:
|
58
|
-
if not find_spec(f"{n}.{self.MODULE_NAME}"):
|
59
|
-
continue
|
60
|
-
except ModuleNotFoundError:
|
61
|
-
continue
|
62
|
-
|
63
|
-
cli = importlib.import_module(f"{n}.{self.MODULE_NAME}")
|
64
|
-
|
65
|
-
# Get the app's cli.py group
|
66
|
-
try:
|
67
|
-
return cli.cli
|
68
|
-
except AttributeError:
|
69
|
-
continue
|
70
|
-
|
71
|
-
|
72
|
-
class EntryPointGroup(click.Group):
|
73
|
-
"""
|
74
|
-
Python packages can be added to the Plain CLI
|
75
|
-
via the plain_cli entrypoint in their setup.py.
|
76
|
-
|
77
|
-
This is intended for packages that don't go in INSTALLED_PACKAGES.
|
78
|
-
"""
|
79
|
-
|
80
|
-
ENTRYPOINT_NAME = "plain.cli"
|
81
|
-
|
82
|
-
def list_commands(self, ctx):
|
83
|
-
rv = []
|
84
|
-
|
85
|
-
for entry_point in entry_points().select(group=self.ENTRYPOINT_NAME):
|
86
|
-
rv.append(entry_point.name)
|
87
|
-
|
88
|
-
rv.sort()
|
89
|
-
return rv
|
90
|
-
|
91
|
-
def get_command(self, ctx, name):
|
92
|
-
for entry_point in entry_points().select(group=self.ENTRYPOINT_NAME):
|
93
|
-
if entry_point.name == name:
|
94
|
-
return entry_point.load()
|
File without changes
|
File without changes
|