plain 0.47.0__py3-none-any.whl → 0.49.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/cli/README.md CHANGED
@@ -30,6 +30,12 @@ Run a Python script in the context of your app.
30
30
 
31
31
  View the runtime value of a named setting.
32
32
 
33
+ ### `plain help`
34
+
35
+ Print help for all available commands and subcommands.
36
+ Each command's help output is prefixed with the full command name for
37
+ readability.
38
+
33
39
  ### `plain shell`
34
40
 
35
41
  Open a Python shell with the Plain loaded.
plain/cli/core.py CHANGED
@@ -10,6 +10,7 @@ from .build import build
10
10
  from .chores import chores
11
11
  from .docs import docs
12
12
  from .formatting import PlainContext
13
+ from .help import help_cmd
13
14
  from .preflight import preflight_checks
14
15
  from .registry import cli_registry
15
16
  from .scaffold import create
@@ -34,6 +35,7 @@ plain_cli.add_command(urls)
34
35
  plain_cli.add_command(setting)
35
36
  plain_cli.add_command(shell)
36
37
  plain_cli.add_command(run)
38
+ plain_cli.add_command(help_cmd)
37
39
 
38
40
 
39
41
  class CLIRegistryGroup(click.Group):
plain/cli/help.py ADDED
@@ -0,0 +1,27 @@
1
+ import click
2
+ from click.core import MultiCommand
3
+
4
+
5
+ @click.command("help")
6
+ @click.pass_context
7
+ def help_cmd(ctx):
8
+ """Show help for all commands and subcommands."""
9
+
10
+ root = ctx.parent.command
11
+ info_name = ctx.parent.info_name or "plain"
12
+
13
+ def print_help(cmd, prog, parent=None):
14
+ sub_ctx = click.Context(cmd, info_name=prog, parent=parent)
15
+
16
+ title = sub_ctx.command_path
17
+ click.secho(title, fg="green", bold=True)
18
+ click.secho("-" * len(title), fg="green")
19
+ click.echo(sub_ctx.get_help())
20
+
21
+ if isinstance(cmd, MultiCommand):
22
+ for name in cmd.list_commands(sub_ctx):
23
+ click.echo()
24
+ sub_cmd = cmd.get_command(sub_ctx, name)
25
+ print_help(sub_cmd, name, sub_ctx)
26
+
27
+ print_help(root, info_name)
plain/runtime/README.md CHANGED
@@ -85,7 +85,7 @@ DATABASES: dict
85
85
  # Automatically configure DATABASES if a DATABASE_URL was given in the environment
86
86
  if "DATABASE_URL" in environ:
87
87
  DATABASES = {
88
- "default": database_url.parse(
88
+ "default": database_url.parse_database_url(
89
89
  environ["DATABASE_URL"],
90
90
  # Enable persistent connections by default
91
91
  conn_max_age=int(environ.get("DATABASE_CONN_MAX_AGE", 600)),
@@ -1,3 +1,4 @@
1
+ import importlib.util
1
2
  from importlib import import_module
2
3
 
3
4
  from plain.packages import packages_registry
@@ -25,24 +26,21 @@ class JinjaEnvironment(LazyObject):
25
26
  self._wrapped = env
26
27
 
27
28
  for package_config in packages_registry.get_package_configs():
28
- # Allow this to fail in case there are import errors inside of their file
29
+ # Autoload template helpers if the package provides a ``templates`` module
29
30
  import_name = f"{package_config.name}.templates"
30
31
  if import_name in self._imported_modules:
31
32
  continue
32
- try:
33
- import_module(import_name)
34
- self._imported_modules.add(import_name)
35
- except ModuleNotFoundError:
36
- pass
33
+ if importlib.util.find_spec(import_name) is None:
34
+ continue
35
+ import_module(import_name)
36
+ self._imported_modules.add(import_name)
37
37
 
38
- # Allow this to fail in case there are import errors inside of their file
38
+ # Autoload template helpers from the local ``app`` package if present
39
39
  import_name = "app.templates"
40
40
  if import_name not in self._imported_modules:
41
- try:
41
+ if importlib.util.find_spec(import_name) is not None:
42
42
  import_module(import_name)
43
43
  self._imported_modules.add(import_name)
44
- except ModuleNotFoundError:
45
- pass
46
44
 
47
45
 
48
46
  environment = JinjaEnvironment()
plain/views/templates.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from plain.csrf.middleware import get_token
2
2
  from plain.exceptions import ImproperlyConfigured
3
+ from plain.http import Response
3
4
  from plain.runtime import settings
4
5
  from plain.templates import Template, TemplateFileMissing
5
6
  from plain.utils.functional import lazy
@@ -69,5 +70,5 @@ class TemplateView(View):
69
70
  def render_template(self) -> str:
70
71
  return self.get_template().render(self.get_template_context())
71
72
 
72
- def get(self):
73
- return self.render_template()
73
+ def get(self) -> Response:
74
+ return Response(self.render_template())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plain
3
- Version: 0.47.0
3
+ Version: 0.49.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
@@ -17,13 +17,14 @@ plain/assets/views.py,sha256=T_0Qh6v9qBerEBYbhToigwOzsij-x1z_R-1zETQcIh0,9447
17
17
  plain/chores/README.md,sha256=Da8Nw8ZF7PlAE_iVb0AqJGbLOu0F6HC0cj1K451KBak,1946
18
18
  plain/chores/__init__.py,sha256=r9TXtQCH-VbvfnIJ5F8FxgQC35GRWFOfmMZN3q9niLg,67
19
19
  plain/chores/registry.py,sha256=V3WjuekRI22LFvJbqSkUXQtiOtuE2ZK8gKV1TRvxRUI,1866
20
- plain/cli/README.md,sha256=ompPcgzY2Fdpm579ITmCpFIaZIsiXYbfD61mqkq312M,1860
20
+ plain/cli/README.md,sha256=nkcpS_mWPZ6JbnlKh4fq-b7AlmItAQsT78XAqRlwqNg,2017
21
21
  plain/cli/__init__.py,sha256=6w9T7K2WrPwh6DcaMb2oNt_CWU6Bc57nUTO2Bt1p38Y,63
22
22
  plain/cli/build.py,sha256=dKUYBNegvb6QNckR7XZ7CJJtINwZcmDvbUdv2dWwjf8,3226
23
23
  plain/cli/chores.py,sha256=xXSSFvr8T5jWfLWqe6E8YVMw1BkQxyOHHVuY0x9RH0A,2412
24
- plain/cli/core.py,sha256=sy5otyN5jNOHkcy1EXISB7iMr1itp0rzmBSnEXYGe2k,2866
24
+ plain/cli/core.py,sha256=h8gjeBYQzYTzpmeDMAJTdVDOEoNCNcvnxml-KIsiRPo,2925
25
25
  plain/cli/docs.py,sha256=2CpTv5k-TNWf593tPiglvUVXWBGdfPmbGf8vl5AfJwU,8995
26
26
  plain/cli/formatting.py,sha256=1hZH13y1qwHcU2K2_Na388nw9uvoeQH8LrWL-O9h8Yc,2207
27
+ plain/cli/help.py,sha256=otRSGxOJ5V8JMjpdZ8XYqUbdlYdJvxOMzQroLOWw-l0,801
27
28
  plain/cli/preflight.py,sha256=NKyYjcoDjigzfJIDhf7A7degYadaUI05Bw7U9OQ73vs,4170
28
29
  plain/cli/print.py,sha256=XraUYrgODOJquIiEv78wSCYGRBplHXtXSS9QtFG5hqY,217
29
30
  plain/cli/registry.py,sha256=yKVMSDjW8g10nlV9sPXFGJQmhC_U-k4J4kM7N2OQVLA,1467
@@ -81,7 +82,7 @@ plain/preflight/messages.py,sha256=B6VyXzu7HTJHaPVK4G1L_1HVHG87CT7JPtcDk8QYSeE,2
81
82
  plain/preflight/registry.py,sha256=wHLq6LSMkKunkxElBmNwzMzQx3tc6OGYeKyOOi0tuyQ,2333
82
83
  plain/preflight/security.py,sha256=oxUZBp2M0bpBfUoLYepIxoex2Y90nyjlrL8XU8UTHYY,2438
83
84
  plain/preflight/urls.py,sha256=cQ-WnFa_5oztpKdtwhuIGb7pXEml__bHsjs1SWO2YNI,1468
84
- plain/runtime/README.md,sha256=S_FIOmSq8LkVQHh9Xm6s3EJWKTVdlSr5A_bNXgh02X8,4740
85
+ plain/runtime/README.md,sha256=yPcrJKnFEqkrb0KlhLZi2VInV-LCl6ENOZs7mOHlhi0,4753
85
86
  plain/runtime/__init__.py,sha256=8GtvKROf3HUCtneDYXTbEioPcCtwnV76dP57n2PnjuE,2343
86
87
  plain/runtime/global_settings.py,sha256=MpJ6vtBMO2EYALaKXrtgcGoxLh9Y549zwwErC5D5K7I,5343
87
88
  plain/runtime/user_settings.py,sha256=OzMiEkE6ZQ50nxd1WIqirXPiNuMAQULklYHEzgzLWgA,11027
@@ -93,7 +94,7 @@ plain/signals/dispatch/license.txt,sha256=o9EhDhsC4Q5HbmD-IfNGVTEkXtNE33r5rIt3ll
93
94
  plain/templates/README.md,sha256=G43IdTRJX6mfxsExUTQf9QAwPeHLsDgY7PvKXdx4l6g,2432
94
95
  plain/templates/__init__.py,sha256=bX76FakE9T7mfK3N0deN85HlwHNQpeigytSC9Z8LcOs,451
95
96
  plain/templates/core.py,sha256=iw58EAmyyv8N5HDA-Sq4-fLgz_qx8v8WJfurgR116jw,625
96
- plain/templates/jinja/__init__.py,sha256=qBESSL8XfwdxtwujjR5mZvk4VddlMn1-jOsSxGQy0oE,2768
97
+ plain/templates/jinja/__init__.py,sha256=xvYif0feMYR9pWjN0czthq2dk3qI4D5UQjgj9yp4dNA,2776
97
98
  plain/templates/jinja/environments.py,sha256=9plifzvQj--aTN1cCpJ2WdzQxZJpzB8S_4hghgQRQT0,2064
98
99
  plain/templates/jinja/extensions.py,sha256=AEmmmHDbdRW8fhjYDzq9eSSNbp9WHsXenD8tPthjc0s,1351
99
100
  plain/templates/jinja/filters.py,sha256=t_u8BkWtEpJFLbLywONxWKrenSzOvDJhfOLwlZiXHDU,968
@@ -114,7 +115,6 @@ plain/urls/utils.py,sha256=lKxTX_A3XJpIH7FjlNYju108stY6-8Sw2uVdiSsxOKQ,1249
114
115
  plain/utils/README.md,sha256=hRRkcg4CxMX-zz8d4Bn6V2uJr_VKgTLurc1jY7QlEx8,198
115
116
  plain/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
116
117
  plain/utils/cache.py,sha256=iuvOTIfI1s857iVOAPNLK5lkzlrl0fIiBYaiUXWQu40,5303
117
- plain/utils/connection.py,sha256=KF57CXt4tVO0EXMRwvIQX0eSruvpNQwGkK4_YGT9nXo,2488
118
118
  plain/utils/crypto.py,sha256=zFDydnaqNMGYFHUc-CAn8f93685a17BhGusAcITH1lI,2662
119
119
  plain/utils/datastructures.py,sha256=g4UYTbxIb_n8F9JWMP4dHPwUz71591fHreGATPO4qEc,10240
120
120
  plain/utils/dateparse.py,sha256=u9_tF85YteXSjW9KQzNg_pcCEFDZS3EGorCddcWU0vE,5351
@@ -145,9 +145,9 @@ plain/views/exceptions.py,sha256=b4euI49ZUKS9O8AGAcFfiDpstzkRAuuj_uYQXzWNHME,138
145
145
  plain/views/forms.py,sha256=ESZOXuo6IeYixp1RZvPb94KplkowRiwO2eGJCM6zJI0,2400
146
146
  plain/views/objects.py,sha256=GGbcfg_9fPZ-PiaBwIHG2e__8GfWDR7JQtQ15wTyiHg,5970
147
147
  plain/views/redirect.py,sha256=daq2cQIkdDF78bt43sjuZxRAyJm_t_SKw6tyPmiXPIc,1985
148
- plain/views/templates.py,sha256=SU1fO9gVMp-gEQHYeFplxvmgeMyrLgT8MJ12WNVmQC8,2085
149
- plain-0.47.0.dist-info/METADATA,sha256=1pZQsM230re8YuNsP-4sMkVswn-9OrSRzm0A7QxhVA8,4297
150
- plain-0.47.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
151
- plain-0.47.0.dist-info/entry_points.txt,sha256=1Ys2lsSeMepD1vz8RSrJopna0RQfUd951vYvCRsvl6A,45
152
- plain-0.47.0.dist-info/licenses/LICENSE,sha256=m0D5O7QoH9l5Vz_rrX_9r-C8d9UNr_ciK6Qwac7o6yo,3175
153
- plain-0.47.0.dist-info/RECORD,,
148
+ plain/views/templates.py,sha256=ivkI7LU7BXDQ0d4Geab96Is4-Cp03KbIntXRT1J8e6I,2139
149
+ plain-0.49.0.dist-info/METADATA,sha256=h5KgVsVtWFjd4CveudNI0w7_TsFeroqviAiWssBGNwE,4297
150
+ plain-0.49.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
151
+ plain-0.49.0.dist-info/entry_points.txt,sha256=1Ys2lsSeMepD1vz8RSrJopna0RQfUd951vYvCRsvl6A,45
152
+ plain-0.49.0.dist-info/licenses/LICENSE,sha256=m0D5O7QoH9l5Vz_rrX_9r-C8d9UNr_ciK6Qwac7o6yo,3175
153
+ plain-0.49.0.dist-info/RECORD,,
plain/utils/connection.py DELETED
@@ -1,84 +0,0 @@
1
- from functools import cached_property
2
- from threading import local
3
-
4
- from plain.runtime import settings as plain_settings
5
-
6
-
7
- class ConnectionProxy:
8
- """Proxy for accessing a connection object's attributes."""
9
-
10
- def __init__(self, connections, alias):
11
- self.__dict__["_connections"] = connections
12
- self.__dict__["_alias"] = alias
13
-
14
- def __getattr__(self, item):
15
- return getattr(self._connections[self._alias], item)
16
-
17
- def __setattr__(self, name, value):
18
- return setattr(self._connections[self._alias], name, value)
19
-
20
- def __delattr__(self, name):
21
- return delattr(self._connections[self._alias], name)
22
-
23
- def __contains__(self, key):
24
- return key in self._connections[self._alias]
25
-
26
- def __eq__(self, other):
27
- return self._connections[self._alias] == other
28
-
29
-
30
- class ConnectionDoesNotExist(Exception):
31
- pass
32
-
33
-
34
- class BaseConnectionHandler:
35
- settings_name = None
36
- exception_class = ConnectionDoesNotExist
37
-
38
- def __init__(self, settings=None):
39
- self._settings = settings
40
- self._connections = local()
41
-
42
- @cached_property
43
- def settings(self):
44
- self._settings = self.configure_settings(self._settings)
45
- return self._settings
46
-
47
- def configure_settings(self, settings):
48
- if settings is None:
49
- settings = getattr(plain_settings, self.settings_name)
50
- return settings
51
-
52
- def create_connection(self, alias):
53
- raise NotImplementedError("Subclasses must implement create_connection().")
54
-
55
- def __getitem__(self, alias):
56
- try:
57
- return getattr(self._connections, alias)
58
- except AttributeError:
59
- if alias not in self.settings:
60
- raise self.exception_class(f"The connection '{alias}' doesn't exist.")
61
- conn = self.create_connection(alias)
62
- setattr(self._connections, alias, conn)
63
- return conn
64
-
65
- def __setitem__(self, key, value):
66
- setattr(self._connections, key, value)
67
-
68
- def __delitem__(self, key):
69
- delattr(self._connections, key)
70
-
71
- def __iter__(self):
72
- return iter(self.settings)
73
-
74
- def all(self, initialized_only=False):
75
- return [
76
- self[alias]
77
- for alias in self
78
- # If initialized_only is True, return only initialized connections.
79
- if not initialized_only or hasattr(self._connections, alias)
80
- ]
81
-
82
- def close_all(self):
83
- for conn in self.all(initialized_only=True):
84
- conn.close()
File without changes