muffin 0.102.3__py3-none-any.whl → 1.0.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.
muffin/app.py CHANGED
@@ -1,11 +1,12 @@
1
1
  """Implement Muffin Application."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  import logging
5
6
  from contextvars import ContextVar
6
- from inspect import isawaitable, stack
7
+ from inspect import currentframe, isawaitable
7
8
  from logging.config import dictConfig
8
- from typing import TYPE_CHECKING, Any, Final, Mapping, Union
9
+ from typing import TYPE_CHECKING, Any, Final, Mapping
9
10
 
10
11
  from asgi_tools import App as BaseApp
11
12
  from asgi_tools._compat import aio_wait
@@ -22,7 +23,7 @@ if TYPE_CHECKING:
22
23
 
23
24
  from muffin.plugins import BasePlugin
24
25
 
25
- BACKGROUND_TASK: Final["ContextVar[set[Awaitable] | None]"] = ContextVar(
26
+ BACKGROUND_TASKS: Final[ContextVar[set[Awaitable[Any]] | None]] = ContextVar(
26
27
  "background_tasks", default=None
27
28
  )
28
29
 
@@ -50,7 +51,7 @@ class Application(BaseApp):
50
51
  "LOG_CONFIG": None,
51
52
  }
52
53
 
53
- def __init__(self, *cfg_mods: Union[str, ModuleType], **options):
54
+ def __init__(self, *cfg_mods: str | ModuleType, **options):
54
55
  """Initialize the application.
55
56
 
56
57
  :param *cfg_mods: modules to import application's config
@@ -103,10 +104,10 @@ class Application(BaseApp):
103
104
  async def __call__(self, scope: TASGIScope, receive: TASGIReceive, send: TASGISend):
104
105
  """Support background tasks."""
105
106
  await self.lifespan(scope, receive, send)
106
- bgtasks = BACKGROUND_TASK.get()
107
+ bgtasks = BACKGROUND_TASKS.get()
107
108
  if bgtasks is not None:
108
109
  await aio_wait(*bgtasks)
109
- BACKGROUND_TASK.set(None)
110
+ BACKGROUND_TASKS.set(None)
110
111
 
111
112
  def import_submodules(self, *submodules: str, silent: bool = False, **kwargs):
112
113
  """Automatically import submodules.
@@ -125,9 +126,12 @@ class Application(BaseApp):
125
126
  app.import_submodules(silent=True)
126
127
 
127
128
  """
128
- parent_frame = stack()[1][0]
129
- package_name = parent_frame.f_locals["__name__"]
130
- return import_submodules(package_name, *submodules, silent=silent, **kwargs)
129
+ curframe = currentframe()
130
+ if curframe:
131
+ parent_frame = curframe.f_back
132
+ if parent_frame:
133
+ package_name = parent_frame.f_locals["__name__"]
134
+ return import_submodules(package_name, *submodules, silent=silent, **kwargs)
131
135
 
132
136
  def run_after_response(self, *tasks: Awaitable):
133
137
  """Await the given awaitable after the response is completed.
@@ -154,11 +158,11 @@ class Application(BaseApp):
154
158
  return "OK"
155
159
 
156
160
  """
157
- scheduled = BACKGROUND_TASK.get() or set()
161
+ scheduled = set(BACKGROUND_TASKS.get() or [])
158
162
  for task in tasks:
159
163
  if not isawaitable(task):
160
164
  raise TypeError(f"Task must be awaitable: {task!r}") # noqa: TRY003
161
165
 
162
166
  scheduled.add(task)
163
167
 
164
- BACKGROUND_TASK.set(scheduled)
168
+ BACKGROUND_TASKS.set(scheduled)
muffin/handler.py CHANGED
@@ -3,9 +3,8 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import inspect
6
- from typing import TYPE_CHECKING, Any, Awaitable, Callable, Optional, cast
6
+ from typing import TYPE_CHECKING, Any, Awaitable, Callable, cast
7
7
 
8
- from asgi_tools.utils import is_awaitable
9
8
  from asgi_tools.view import HTTP_METHODS, HTTPView
10
9
 
11
10
  from muffin.errors import AsyncRequiredError
@@ -34,7 +33,7 @@ class HandlerMeta(type):
34
33
  cls.methods = {method.upper() for method in cls.methods}
35
34
  for m in cls.methods:
36
35
  method = getattr(cls, m.lower(), None)
37
- if method and not is_awaitable(method):
36
+ if method and not inspect.iscoroutinefunction(method):
38
37
  raise AsyncRequiredError(method)
39
38
 
40
39
  return cls
@@ -50,7 +49,7 @@ class Handler(HTTPView, metaclass=HandlerMeta):
50
49
 
51
50
  async def get(self, request):
52
51
  name = request.patch_params.get('name') or 'all'
53
- return "GET: Hello f{name}"
52
+ return f"GET: Hello {name}"
54
53
 
55
54
  async def post(self, request):
56
55
  name = request.patch_params.get('name') or 'all'
@@ -79,12 +78,10 @@ class Handler(HTTPView, metaclass=HandlerMeta):
79
78
 
80
79
  """
81
80
 
82
- methods: Optional[TMethods] = None
81
+ methods: TMethods | None = None
83
82
 
84
83
  @classmethod
85
- def __route__(
86
- cls, router: Router, *paths: str, methods: Optional[TMethodsArg] = None, **params
87
- ):
84
+ def __route__(cls, router: Router, *paths: str, methods: TMethodsArg | None = None, **params):
88
85
  """Check for registered methods."""
89
86
  router.bind(cls, *paths, methods=methods or cls.methods, **params)
90
87
  for _, method in inspect.getmembers(cls, lambda m: hasattr(m, "__route__")):
@@ -93,14 +90,14 @@ class Handler(HTTPView, metaclass=HandlerMeta):
93
90
 
94
91
  return cls
95
92
 
96
- def __call__(self, request: Request, *, method_name: Optional[str] = None, **_) -> Awaitable:
93
+ def __call__(self, request: Request, *, method_name: str | None = None, **_) -> Awaitable:
97
94
  """Dispatch the given request by HTTP method."""
98
95
  method = getattr(self, method_name or request.method.lower())
99
96
  return method(request)
100
97
 
101
98
  @staticmethod
102
99
  def route(
103
- *paths: str, methods: Optional[TMethodsArg] = None
100
+ *paths: str, methods: TMethodsArg | None = None
104
101
  ) -> Callable[[TVCallable], TVCallable]:
105
102
  """Mark a method as a route."""
106
103
 
muffin/manage.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """CLI Support is here."""
2
+
2
3
  import argparse
3
4
  import code
4
5
  import inspect
@@ -9,7 +10,7 @@ import sys
9
10
  from contextlib import AsyncExitStack, suppress
10
11
  from importlib import metadata
11
12
  from pathlib import Path
12
- from typing import TYPE_CHECKING, AsyncContextManager, Callable, Optional, overload
13
+ from typing import TYPE_CHECKING, AsyncContextManager, Callable, overload
13
14
 
14
15
  from muffin.constants import CONFIG_ENV_VARIABLE
15
16
  from muffin.errors import AsyncRequiredError
@@ -132,17 +133,15 @@ class Manager:
132
133
  return self(*args, **kwargs)
133
134
 
134
135
  @overload
135
- def __call__(self, fn: "TVCallable") -> "TVCallable":
136
- ...
136
+ def __call__(self, fn: "TVCallable") -> "TVCallable": ...
137
137
 
138
138
  @overload
139
- def __call__(self, *, lifespan: bool = False) -> Callable[["TVCallable"], "TVCallable"]:
140
- ...
139
+ def __call__(self, *, lifespan: bool = False) -> Callable[["TVCallable"], "TVCallable"]: ...
141
140
 
142
141
  def __call__(self, fn=None, *, lifespan=False): # noqa: C901
143
142
  """Register a command."""
144
143
 
145
- def wrapper(fn): # noqa: C901
144
+ def wrapper(fn): # noqa: PLR0912, C901
146
145
  if not inspect.iscoroutinefunction(fn) and lifespan:
147
146
  raise AsyncRequiredError(fn)
148
147
 
@@ -157,57 +156,70 @@ class Manager:
157
156
  return fn
158
157
 
159
158
  parser = self.subparsers.add_parser(command_name, description=description)
160
- args, vargs, _, defs, __, kwdefs, anns = inspect.getfullargspec(fn)
161
- defs = defs or []
162
- kwargs_ = dict(zip(args[-len(defs) :], defs))
159
+ sig = inspect.signature(fn)
163
160
  docs = dict(PARAM_RE.findall(fn.__doc__ or ""))
164
161
 
165
- def process_arg(name, *, value=..., **opts):
166
- argname = name.lower()
162
+ for name, param in sig.parameters.items():
167
163
  arghelp = docs.get(name, "")
168
- if value is ...:
169
- return parser.add_argument(argname, help=arghelp, **opts)
170
-
171
- argname = argname.replace("_", "-")
172
- if isinstance(value, bool):
173
- if value:
174
- return parser.add_argument(
175
- "--no-" + argname,
176
- dest=name,
177
- action="store_false",
178
- help=arghelp or f"Disable { name }",
179
- )
180
-
181
- return parser.add_argument(
182
- "--" + argname,
183
- dest=name,
184
- action="store_true",
185
- help=arghelp or f"Enable { name }",
186
- )
164
+ argname = name.replace("_", "-")
187
165
 
188
- if isinstance(value, list):
189
- return parser.add_argument(
190
- "--" + argname,
191
- action="append",
192
- default=value,
193
- help=arghelp,
194
- )
195
-
196
- return parser.add_argument(
197
- "--" + argname,
198
- type=anns.get(name, type(value)),
199
- default=value,
200
- help=arghelp + " [%s]" % repr(value),
166
+ type_func = (
167
+ param.annotation
168
+ if param.annotation is not param.empty
169
+ else type(param.default) if param.default is not param.empty else str
201
170
  )
171
+ if not isinstance(type_func, type):
172
+ type_func = str
202
173
 
203
- if vargs:
204
- process_arg("*", nargs="*", metavar=vargs)
174
+ if param.kind == param.VAR_POSITIONAL:
175
+ parser.add_argument(name, nargs="*", metavar=name, help=arghelp)
176
+ continue
205
177
 
206
- for name, value in (kwdefs or {}).items():
207
- process_arg(name, value=value)
178
+ if param.kind == param.VAR_KEYWORD:
179
+ # **kwargs not supported in CLI parser
180
+ continue
208
181
 
209
- for name in args:
210
- process_arg(name, value=kwargs_.get(name, ...))
182
+ if param.default is param.empty:
183
+ parser.add_argument(
184
+ name,
185
+ help=arghelp,
186
+ type=type_func,
187
+ )
188
+ else:
189
+ default = param.default
190
+ if isinstance(default, bool):
191
+ if default:
192
+ parser.add_argument(
193
+ f"--no-{argname}",
194
+ dest=name,
195
+ action="store_false",
196
+ help=arghelp or f"Disable {name}",
197
+ )
198
+ else:
199
+ parser.add_argument(
200
+ f"--{argname}",
201
+ dest=name,
202
+ action="store_true",
203
+ help=arghelp or f"Enable {name}",
204
+ )
205
+ elif isinstance(default, list):
206
+ parser.add_argument(
207
+ f"--{argname}",
208
+ action="append",
209
+ default=default,
210
+ help=arghelp,
211
+ )
212
+ else:
213
+ parser.add_argument(
214
+ f"--{argname}",
215
+ type=(
216
+ param.annotation
217
+ if param.annotation is not param.empty
218
+ else type(default)
219
+ ),
220
+ default=default,
221
+ help=f"{arghelp} [{default!r}]",
222
+ )
211
223
 
212
224
  self.commands[command_name] = fn
213
225
  fn.parser = parser
@@ -218,7 +230,7 @@ class Manager:
218
230
 
219
231
  return wrapper
220
232
 
221
- def run(self, *args: str, prog: Optional[str] = None): # noqa: FA100
233
+ def run(self, *args: str, prog: str | None = None):
222
234
  """Parse the arguments and run a command."""
223
235
  if prog:
224
236
  self.parser.prog = prog
muffin/plugins.py CHANGED
@@ -3,14 +3,15 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from abc import ABC
6
- from typing import TYPE_CHECKING, Any, Awaitable, Callable, ClassVar, Mapping, Optional
6
+ from asyncio import iscoroutinefunction
7
+ from typing import TYPE_CHECKING, Any, Awaitable, Callable, ClassVar, Mapping
7
8
 
8
9
  from modconfig import Config
9
10
 
10
11
  from muffin.errors import MuffinError
11
12
 
12
13
  if TYPE_CHECKING:
13
- from contextlib import _AsyncGeneratorContextManager
14
+ from contextlib import AbstractAsyncContextManager
14
15
 
15
16
  from muffin.app import Application
16
17
 
@@ -32,18 +33,18 @@ class BasePlugin(ABC):
32
33
  defaults: ClassVar[Mapping[str, Any]] = {"disabled": False}
33
34
 
34
35
  # Optional middleware method
35
- middleware: Callable[..., Awaitable]
36
+ middleware: Callable[..., Awaitable] | None = None
36
37
 
37
38
  # Optional startup method
38
- startup: Callable[..., Awaitable]
39
+ startup: Callable[..., Awaitable] | None = None
39
40
 
40
41
  # Optional shutdown method
41
- shutdown: Callable[..., Awaitable]
42
+ shutdown: Callable[..., Awaitable] | None = None
42
43
 
43
44
  # Optional conftest method
44
- conftest: Optional[Callable[[], _AsyncGeneratorContextManager]] = None
45
+ conftest: Callable[[], AbstractAsyncContextManager] | None = None
45
46
 
46
- def __init__(self, app: Optional[Application] = None, **options):
47
+ def __init__(self, app: Application | None = None, **options):
47
48
  """Save application and create he plugin's configuration."""
48
49
  if getattr(self, "name", None) is None:
49
50
  msg = "Plugin.name is required"
@@ -64,11 +65,11 @@ class BasePlugin(ABC):
64
65
  return f"<muffin.Plugin: { self.name }>"
65
66
 
66
67
  async def __aenter__(self):
67
- if hasattr(self, "startup"):
68
+ if iscoroutinefunction(self.startup):
68
69
  await self.startup()
69
70
 
70
71
  async def __aexit__(self, exc_type, exc, tb):
71
- if hasattr(self, "shutdown"):
72
+ if iscoroutinefunction(self.shutdown):
72
73
  await self.shutdown()
73
74
 
74
75
  @property
@@ -79,7 +80,7 @@ class BasePlugin(ABC):
79
80
 
80
81
  return self.__app__
81
82
 
82
- def setup(self, app: Application, *, name: Optional[str] = None, **options) -> Any:
83
+ def setup(self, app: Application, *, name: str | None = None, **options) -> Any:
83
84
  """Bind app and update the plugin's configuration."""
84
85
  # allow to redefine the name for multi plugins with same type
85
86
  self.name = name or self.name
@@ -99,15 +100,22 @@ class BasePlugin(ABC):
99
100
  self.__app__ = app
100
101
 
101
102
  # Register a middleware
102
- if hasattr(self, "middleware"):
103
+ if callable(self.middleware):
103
104
  app.middleware(self.middleware)
104
105
 
105
106
  # Bind startup
106
- if hasattr(self, "startup"):
107
+ if callable(self.startup):
107
108
  app.on_startup(self.startup)
108
109
 
109
110
  # Bind shutdown
110
- if hasattr(self, "shutdown"):
111
+ if callable(self.shutdown):
111
112
  app.on_shutdown(self.shutdown)
112
113
 
113
114
  return True
115
+
116
+ async def restart(self):
117
+ if callable(self.shutdown):
118
+ await self.shutdown()
119
+
120
+ if callable(self.startup):
121
+ await self.startup()
muffin/utils.py CHANGED
@@ -56,7 +56,7 @@ logger = getLogger("muffin")
56
56
 
57
57
  def aio_lib() -> str:
58
58
  """Return first available async library."""
59
- aiolib = os.environ.get("MUFFIN_AIOLIB", "asyncio")
59
+ aiolib = os.environ.get("MUFFIN_AIOLIB")
60
60
  if aiolib:
61
61
  return aiolib
62
62
 
@@ -89,11 +89,11 @@ def import_submodules(
89
89
  for module_name in to_import:
90
90
  try:
91
91
  res[module_name] = importlib.import_module(f"{package_name}.{module_name}")
92
- except ImportError: # noqa: PERF203
92
+ except ImportError as exc: # noqa: PERF203
93
93
  if not silent:
94
94
  raise
95
95
 
96
- logger.debug("Failed to import module: %s", f"{package_name}.{module_name}")
96
+ logger.debug("Failed to import %s: %s", f"{package_name}.{module_name}", exc)
97
97
 
98
98
  return res
99
99
 
@@ -0,0 +1,187 @@
1
+ Metadata-Version: 2.3
2
+ Name: muffin
3
+ Version: 1.0.0
4
+ Summary: Muffin is a fast, simple and asyncronous web-framework for Python 3 (asyncio, trio, curio)
5
+ License: MIT
6
+ Keywords: asgi,web,web framework,asyncio,trio,curio
7
+ Author: Kirill Klenov
8
+ Author-email: horneds@gmail.com
9
+ Requires-Python: >=3.10,<4.0
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Framework :: AsyncIO
12
+ Classifier: Framework :: Trio
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
22
+ Classifier: Topic :: Internet :: WWW/HTTP
23
+ Provides-Extra: standard
24
+ Requires-Dist: asgi-tools (>=1.3.2,<2.0.0)
25
+ Requires-Dist: gunicorn (>=20.1.0,<21.0.0) ; extra == "standard"
26
+ Requires-Dist: modconfig (>=1,<2)
27
+ Requires-Dist: ujson
28
+ Requires-Dist: uvicorn[standard] (>=0.21.1,<0.22.0) ; extra == "standard"
29
+ Project-URL: Documentation, https://klen.github.io/muffin
30
+ Project-URL: Homepage, https://github.com/klen/muffin
31
+ Project-URL: Repository, https://github.com/klen/muffin
32
+ Project-URL: changelog, https://raw.githubusercontent.com/klen/muffin/master/CHANGELOG.md
33
+ Description-Content-Type: text/x-rst
34
+
35
+ .. image:: https://raw.github.com/klen/muffin/develop/docs/static/logo-h200.png
36
+ :height: 100px
37
+
38
+ **Muffin** – fast, lightweight, and asynchronous ASGI_ web framework for Python 3.10+.
39
+
40
+ .. image:: https://github.com/klen/muffin/workflows/tests/badge.svg
41
+ :target: https://github.com/klen/muffin/actions
42
+ :alt: Tests Status
43
+
44
+ .. image:: https://github.com/klen/muffin/workflows/docs/badge.svg
45
+ :target: https://klen.github.io/muffin
46
+ :alt: Documentation Status
47
+
48
+ .. image:: https://img.shields.io/pypi/v/muffin
49
+ :target: https://pypi.org/project/muffin/
50
+ :alt: PYPI Version
51
+
52
+ .. image:: https://img.shields.io/pypi/pyversions/muffin
53
+ :target: https://pypi.org/project/muffin/
54
+ :alt: Python Versions
55
+
56
+ ----------
57
+
58
+ Why Muffin?
59
+ -----------
60
+
61
+ Muffin combines the simplicity of microframeworks with native ASGI_ performance, supporting multiple async libraries (Asyncio_, Trio_, Curio_) out of the box. Its rich plugin ecosystem makes building modern web applications pleasant and efficient.
62
+
63
+ Key Features
64
+ ------------
65
+
66
+ - ASGI_ compatible
67
+ - Competitive performance ([Benchmarks](http://klen.github.io/py-frameworks-bench/))
68
+ - Supports Asyncio_, Trio_, and Curio_
69
+ - Multiple response types: text, HTML, JSON, streams, files, SSE, WebSockets
70
+ - First-class plugin system for templating, databases, auth, and more
71
+
72
+ Installation
73
+ ------------
74
+
75
+ Muffin requires **Python 3.10 or newer**. We recommend using the latest stable Python.
76
+
77
+ Install via pip:
78
+
79
+ .. code-block:: console
80
+
81
+ $ pip install muffin
82
+
83
+ For the standard installation with `gunicorn`, `uvicorn`, `uvloop`, `httptools`:
84
+
85
+ .. code-block:: console
86
+
87
+ $ pip install muffin[standard]
88
+
89
+ Dependencies
90
+ ~~~~~~~~~~~~
91
+
92
+ These packages will be installed automatically:
93
+
94
+ * `ASGI-Tools`_ – ASGI toolkit
95
+ * `Modconfig`_ – hierarchical configuration manager
96
+
97
+ .. _ASGI-Tools: https://klen.github.io/asgi-tools/
98
+ .. _Modconfig: https://pypi.org/project/modconfig/
99
+
100
+ Quickstart
101
+ ----------
102
+
103
+ Create a simple "Hello User" app:
104
+
105
+ .. code-block:: python
106
+
107
+ import muffin
108
+
109
+ app = muffin.Application()
110
+
111
+ @app.route('/', '/hello/{name}')
112
+ async def hello(request):
113
+ name = request.path_params.get('name', 'world')
114
+ return f'Hello, {name.title()}!'
115
+
116
+ Save this as `example.py` and run:
117
+
118
+ .. code-block:: console
119
+
120
+ $ uvicorn example:app
121
+
122
+ Visit http://localhost:8000 or http://localhost:8000/hello/username in your browser.
123
+
124
+ Plugins
125
+ -------
126
+
127
+ Muffin has a rich ecosystem of plugins:
128
+
129
+ - [`muffin-jinja2`](https://github.com/klen/muffin-jinja2) – Jinja2 templates (asyncio/trio/curio)
130
+ - [`muffin-session`](https://github.com/klen/muffin-session) – Signed cookie-based HTTP sessions
131
+ - [`muffin-oauth`](https://github.com/klen/muffin-oauth) – OAuth integration
132
+ - [`muffin-sentry`](https://github.com/klen/muffin-sentry) – Sentry error tracking
133
+ - [`muffin-peewee`](https://github.com/klen/muffin-peewee-aio) – Peewee ORM integration
134
+ - [`muffin-babel`](https://github.com/klen/muffin-babel) – i18n support
135
+ - [`muffin-databases`](https://github.com/klen/muffin-databases) – SQL database support
136
+ - [`muffin-mongo`](https://github.com/klen/muffin-mongo) – MongoDB integration
137
+ - [`muffin-rest`](https://github.com/klen/muffin-rest) – REST API utilities
138
+ - [`muffin-redis`](https://github.com/klen/muffin-redis) – Redis integration
139
+ - [`muffin-admin`](https://github.com/klen/muffin-admin) – Auto-generated admin UI
140
+ - [`muffin-prometheus`](https://github.com/klen/muffin-prometheus) – Prometheus metrics exporter
141
+
142
+ See each repo for usage and installation instructions.
143
+
144
+ Benchmarks
145
+ ----------
146
+
147
+ Performance comparisons are available at: http://klen.github.io/py-frameworks-bench/
148
+
149
+ Bug tracker
150
+ -----------
151
+
152
+ Found a bug or have a feature request? Please open an issue at:
153
+ https://github.com/klen/muffin/issues
154
+
155
+ Contributing
156
+ ------------
157
+
158
+ Contributions are welcome! Please see [CONTRIBUTING.md](https://github.com/klen/muffin/blob/develop/CONTRIBUTING.md) for guidelines.
159
+
160
+ License
161
+ -------
162
+
163
+ Muffin is licensed under the MIT license.
164
+
165
+ ----------
166
+
167
+ Credits
168
+ -------
169
+
170
+ **Muffin > 0.40 (completely rewritten on ASGI)**
171
+
172
+ * `Kirill Klenov <https://github.com/klen>`_
173
+
174
+ **Muffin < 0.40 (based on AIOHTTP_)**
175
+
176
+ * `Kirill Klenov <https://github.com/klen>`_
177
+ * `Andrew Grigorev <https://github.com/ei-grad>`_
178
+ * `Diego Garcia <https://github.com/drgarcia1986>`_
179
+
180
+ .. _AIOHTTP: https://docs.aiohttp.org/en/stable/
181
+ .. _ASGI: https://asgi.readthedocs.io/en/latest/
182
+ .. _Asyncio: https://docs.python.org/3/library/asyncio.html
183
+ .. _Curio: https://curio.readthedocs.io/en/latest/
184
+ .. _Python: http://python.org
185
+ .. _Trio: https://trio.readthedocs.io/en/stable/index.html
186
+ .. _MIT license: http://opensource.org/licenses/MIT
187
+
@@ -0,0 +1,15 @@
1
+ muffin/__init__.py,sha256=8ldUFl0xhKQ7efnpU-TlB0BjXwkJ8lCOip0Uqcn-G4k,1016
2
+ muffin/app.py,sha256=6gPJUhWn9QSdzSwIl_8eP1uwOGaFIkHI-aXQrYwU1gY,5318
3
+ muffin/constants.py,sha256=Ga1UJiEdXUk6dIEH_IEVYkFSZxQFPhxie7fCJwQY8V0,71
4
+ muffin/errors.py,sha256=I-vKbMMBiMU07zPdKvoJKqA7s4xYAUA-4oZXrRMRzcM,701
5
+ muffin/handler.py,sha256=_cvF0TV87YH5_ZDnwgsks-h_e0yI14P27j4HG5tWanc,3673
6
+ muffin/manage.py,sha256=g4DilPjOyXyUcAiNs0m31BSikkvsmGkMZCZcOb0cnuQ,9511
7
+ muffin/plugins.py,sha256=fGIocty5hHGTTY3zQrYBvkXjETlLndqRwAe5jmpyi3I,3417
8
+ muffin/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ muffin/pytest.py,sha256=51pJ-JJ2vwqiPhR9TPqXvT78X35l3v3oEkN5LBnfY8E,2679
10
+ muffin/types.py,sha256=wsUj5oAfqSZMoEf-wyFJLBlWa8Mc-eJGqKLr02HxuXE,153
11
+ muffin/utils.py,sha256=O3JXRkFyDlZ3kJZeaHz2WHMTY6-lvHGDu7qFy0x8Zls,2896
12
+ muffin-1.0.0.dist-info/METADATA,sha256=sAsKPxbQILbEoCXRsm2aN7g1dizfaK89D0FJdRb9y5U,6146
13
+ muffin-1.0.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
14
+ muffin-1.0.0.dist-info/entry_points.txt,sha256=GvPS3M-tNVPzhUS5jnUpOmWw2NAqedY34VRCYgPYzlM,84
15
+ muffin-1.0.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.9.0
2
+ Generator: poetry-core 2.1.3
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,381 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: muffin
3
- Version: 0.102.3
4
- Summary: Muffin is a fast, simple and asyncronous web-framework for Python 3 (asyncio, trio, curio)
5
- Home-page: https://github.com/klen/muffin
6
- License: MIT
7
- Keywords: asgi,web,web framework,asyncio,trio,curio
8
- Author: Kirill Klenov
9
- Author-email: horneds@gmail.com
10
- Requires-Python: >=3.9,<4.0
11
- Classifier: Development Status :: 5 - Production/Stable
12
- Classifier: Framework :: AsyncIO
13
- Classifier: Framework :: Trio
14
- Classifier: Intended Audience :: Developers
15
- Classifier: License :: OSI Approved :: MIT License
16
- Classifier: Programming Language :: Python
17
- Classifier: Programming Language :: Python :: 3
18
- Classifier: Programming Language :: Python :: 3.9
19
- Classifier: Programming Language :: Python :: 3.10
20
- Classifier: Programming Language :: Python :: 3.11
21
- Classifier: Programming Language :: Python :: 3.12
22
- Classifier: Programming Language :: Python :: Implementation :: PyPy
23
- Classifier: Topic :: Internet :: WWW/HTTP
24
- Provides-Extra: standard
25
- Requires-Dist: asgi-tools (>=1,<2)
26
- Requires-Dist: gunicorn (>=20.1.0,<21.0.0) ; extra == "standard"
27
- Requires-Dist: modconfig (>=1,<2)
28
- Requires-Dist: ujson
29
- Requires-Dist: uvicorn[standard] (>=0.21.1,<0.22.0) ; extra == "standard"
30
- Project-URL: Documentation, https://klen.github.io/muffin
31
- Project-URL: Repository, https://github.com/klen/muffin
32
- Project-URL: changelog, https://raw.githubusercontent.com/klen/muffin/master/CHANGELOG.md
33
- Description-Content-Type: text/x-rst
34
-
35
- .. image:: https://raw.github.com/klen/muffin/develop/docs/static/logo-h200.png
36
- :height: 100px
37
-
38
- .. _description:
39
-
40
- **Muffin** -- is a fast, lightweight and asyncronous ASGI_ web-framework for Python_ 3.
41
-
42
- .. _badges:
43
-
44
- .. image:: https://github.com/klen/muffin/workflows/tests/badge.svg
45
- :target: https://github.com/klen/muffin/actions
46
- :alt: Tests Status
47
-
48
- .. image:: https://github.com/klen/muffin/workflows/docs/badge.svg
49
- :target: https://klen.github.io/muffin
50
- :alt: Documentation Status
51
-
52
- .. image:: https://img.shields.io/pypi/v/muffin
53
- :target: https://pypi.org/project/muffin/
54
- :alt: PYPI Version
55
-
56
- .. image:: https://img.shields.io/pypi/pyversions/muffin
57
- :target: https://pypi.org/project/muffin/
58
- :alt: Python Versions
59
-
60
- ----------
61
-
62
- .. _features:
63
-
64
- Features
65
- --------
66
-
67
- - ASGI_ compatible;
68
- - `Competitive Performance <http://klen.github.io/py-frameworks-bench/>`_;
69
- - All async python libraries are supported (Asyncio_, Trio_, Curio_);
70
- - Send HTTP (text, html, json, stream, file, http errors) responses
71
- - Support WebSockets, Server Side Events
72
-
73
- .. _documentation:
74
-
75
- **Docs are available at https://klen.github.io/muffin/. Pull requests
76
- with documentation enhancements and/or fixes are awesome and most welcome.**
77
-
78
- .. _contents:
79
-
80
- .. contents::
81
-
82
- .. _requirements:
83
-
84
- .. _installation:
85
-
86
- Installation
87
- ------------
88
-
89
- We recommend using the latest version of Python. The library supports Python
90
- 3.8 and newer (PyPy-3.9+ are supported too).
91
-
92
- Muffin should be installed using pip: ::
93
-
94
- pip install muffin
95
-
96
- The command will install minimal configuration.
97
-
98
- To install Muffin with `gunicorn`, `uvicorn`, `uvloop`, `httptools` use the
99
- command:
100
-
101
- .. code-block:: console
102
-
103
- $ pip install muffin[standard]
104
-
105
- Dependencies
106
- ````````````
107
-
108
- These distributions will be installed automatically when installing **Muffin**.
109
-
110
- * `ASGI-Tools`_ - ASGI_ Toolkit
111
- * `Modconfig`_ - Simple hierarchic configuration manager
112
-
113
- .. _ASGI-Tools: https://klen.github.io/asgi-tools/
114
- .. _Modconfig: https://pypi.org/project/modconfig/
115
-
116
- .. _quickstart:
117
-
118
- Quickstart
119
- ----------
120
-
121
- Example "Hello User" with the Muffin:
122
-
123
- .. code-block:: python
124
-
125
- import muffin
126
-
127
-
128
- app = muffin.Application()
129
-
130
-
131
- @app.route('/', '/hello/{name}')
132
- async def hello(request):
133
- name = request.path_params.get('name', 'world')
134
- return f'Hello {name.title()}!'
135
-
136
-
137
- What did that code do?
138
-
139
- 1. First we imported the ``muffin.Application`` class. An instance of
140
- this class will be our application.
141
- 2. Next we create an instance of this class.
142
- 3. We then use the ``muffin.Application.route`` decorator to tell Muffin
143
- what URLs should trigger our handler function.
144
- 4. The function returns the message we want to display in the user's browser.
145
-
146
-
147
- Save the script as `example.py` and run it using Uvicorn (or another ASGI_ server): ::
148
-
149
- $ uvicorn example:app
150
-
151
- Open http://localhost:8000, http://localhost:8000/hello/username in your browser. Enjoy!
152
-
153
- .. TODO: Finish the general example
154
- .. For a more complete example, see https://github.com/klen/muffin-example
155
-
156
- .. _plugins:
157
-
158
- Plugins overview
159
- ----------------
160
-
161
- The list of some Muffin plugins (please make PR if you want to provide more):
162
-
163
- `Muffin-Jinja2 <https://github.com/klen/muffin-jinja2>`_
164
- ``````````````````````````````````````````````````````````
165
-
166
- `Jinja2 <https://jinja.palletsprojects.com/en/2.11.x/>`_ templates (asyncio/trio/curio)
167
-
168
- .. image:: https://github.com/klen/muffin-jinja2/workflows/tests/badge.svg
169
- :target: https://github.com/klen/muffin-jinja2/actions
170
- :alt: Tests Status
171
-
172
- .. image:: https://img.shields.io/pypi/v/muffin-jinja2
173
- :target: https://pypi.org/project/muffin-jinja2/
174
- :alt: PYPI Version
175
-
176
-
177
-
178
- `Muffin-Session <https://github.com/klen/muffin-session>`_
179
- ```````````````````````````````````````````````````````````
180
-
181
- Signed Cookie-Based HTTP sessions (asyncio/trio/curio)
182
-
183
- .. image:: https://github.com/klen/muffin-session/workflows/tests/badge.svg
184
- :target: https://github.com/klen/muffin-session/actions
185
- :alt: Tests Status
186
-
187
- .. image:: https://img.shields.io/pypi/v/muffin-session
188
- :target: https://pypi.org/project/muffin-session/
189
- :alt: PYPI Version
190
-
191
-
192
- `Muffin-OAuth <https://github.com/klen/muffin-oauth>`_
193
- ```````````````````````````````````````````````````````
194
-
195
- Work with OAuth (authorization, resources loading) (asyncio/trio/curio)
196
-
197
- .. image:: https://github.com/klen/muffin-oauth/workflows/tests/badge.svg
198
- :target: https://github.com/klen/muffin-oauth/actions
199
- :alt: Tests Status
200
-
201
- .. image:: https://img.shields.io/pypi/v/muffin-oauth
202
- :target: https://pypi.org/project/muffin-oauth/
203
- :alt: PYPI Version
204
-
205
-
206
- `Muffin-Sentry <https://github.com/klen/muffin-sentry>`_
207
- `````````````````````````````````````````````````````````
208
-
209
- Sentry integration (asyncio/trio/curio)
210
-
211
- .. image:: https://github.com/klen/muffin-sentry/workflows/tests/badge.svg
212
- :target: https://github.com/klen/muffin-sentry/actions
213
- :alt: Tests Status
214
-
215
- .. image:: https://img.shields.io/pypi/v/muffin-sentry
216
- :target: https://pypi.org/project/muffin-sentry/
217
- :alt: PYPI Version
218
-
219
-
220
- `Muffin-Peewee <https://github.com/klen/muffin-peewee-aio>`_
221
- `````````````````````````````````````````````````````````````
222
-
223
- Peewee support (SQL, ORM) (asyncio/trio/curio)
224
-
225
- .. image:: https://github.com/klen/muffin-peewee-aio/workflows/tests/badge.svg
226
- :target: https://github.com/klen/muffin-peewee/actions
227
- :alt: Tests Status
228
-
229
- .. image:: https://img.shields.io/pypi/v/muffin-peewee-aio
230
- :target: https://pypi.org/project/muffin-peewee-aio/
231
- :alt: PYPI Version
232
-
233
-
234
- `Muffin-Babel <https://github.com/klen/muffin-babel>`_
235
- ````````````````````````````````````````````````````````
236
-
237
- Localization support (asyncio/trio/curio)
238
-
239
- .. image:: https://github.com/klen/muffin-babel/workflows/tests/badge.svg
240
- :target: https://github.com/klen/muffin-babel/actions
241
- :alt: Tests Status
242
-
243
- .. image:: https://img.shields.io/pypi/v/muffin-babel
244
- :target: https://pypi.org/project/muffin-babel/
245
- :alt: PYPI Version
246
-
247
-
248
- `Muffin-Databases <https://github.com/klen/muffin-databases>`_
249
- `````````````````````````````````````````````````````````````````
250
-
251
- Work with SQL databases (asyncio only)
252
-
253
- .. image:: https://github.com/klen/muffin-databases/workflows/tests/badge.svg
254
- :target: https://github.com/klen/muffin-databases/actions
255
- :alt: Tests Status
256
-
257
- .. image:: https://img.shields.io/pypi/v/muffin-databases
258
- :target: https://pypi.org/project/muffin-databases/
259
- :alt: PYPI Version
260
-
261
-
262
- `Muffin-Mongo <https://github.com/klen/muffin-mongo>`_
263
- `````````````````````````````````````````````````````````
264
-
265
- Work with Mongo DB (asyncio only)
266
-
267
- .. image:: https://github.com/klen/muffin-mongo/workflows/tests/badge.svg
268
- :target: https://github.com/klen/muffin-mongo/actions
269
- :alt: Tests Status
270
-
271
- .. image:: https://img.shields.io/pypi/v/muffin-mongo
272
- :target: https://pypi.org/project/muffin-mongo/
273
- :alt: PYPI Version
274
-
275
- `Muffin-REST <https://github.com/klen/muffin-rest>`_
276
- ````````````````````````````````````````````````````````
277
-
278
- The package provides enhanced support for writing REST APIs (asyncio/trio/curio)
279
-
280
- .. image:: https://github.com/klen/muffin-rest/workflows/tests/badge.svg
281
- :target: https://github.com/klen/muffin-rest/actions
282
- :alt: Tests Status
283
-
284
- .. image:: https://img.shields.io/pypi/v/muffin-rest
285
- :target: https://pypi.org/project/muffin-rest/
286
- :alt: PYPI Version
287
-
288
- `Muffin-Redis <https://github.com/klen/muffin-redis>`_
289
- `````````````````````````````````````````````````````````
290
-
291
- Redis support
292
-
293
- .. image:: https://github.com/klen/muffin-redis/workflows/tests/badge.svg
294
- :target: https://github.com/klen/muffin-redis/actions
295
- :alt: Tests Status
296
-
297
- .. image:: https://img.shields.io/pypi/v/muffin-redis
298
- :target: https://pypi.org/project/muffin-redis/
299
- :alt: PYPI Version
300
-
301
- `Muffin-Admin <https://github.com/klen/muffin-admin>`_
302
- `````````````````````````````````````````````````````````
303
-
304
- Automatically build Admin UI
305
-
306
- .. image:: https://github.com/klen/muffin-admin/workflows/tests/badge.svg
307
- :target: https://github.com/klen/muffin-admin/actions
308
- :alt: Tests Status
309
-
310
- .. image:: https://img.shields.io/pypi/v/muffin-admin
311
- :target: https://pypi.org/project/muffin-admin/
312
- :alt: PYPI Version
313
-
314
- `Muffin-Prometheus <https://github.com/klen/muffin-prometheus>`_
315
- ```````````````````````````````````````````````````````````````````
316
-
317
- Prometheus metrics exporter
318
-
319
- .. image:: https://github.com/klen/muffin-prometheus/workflows/tests/badge.svg
320
- :target: https://github.com/klen/muffin-prometheus/actions
321
- :alt: Tests Status
322
-
323
- .. image:: https://img.shields.io/pypi/v/muffin-prometheus
324
- :target: https://pypi.org/project/muffin-prometheus/
325
- :alt: PYPI Version
326
-
327
- .. _benchmarks:
328
-
329
- Benchmarks
330
- -----------
331
-
332
- You could find some tests here: http://klen.github.io/py-frameworks-bench/
333
-
334
- .. _bugtracker:
335
-
336
- Bug tracker
337
- -----------
338
-
339
- If you have any suggestions, bug reports or
340
- annoyances please report them to the issue tracker
341
- at https://github.com/klen/muffin/issues
342
-
343
- .. _contributing:
344
-
345
- Contributing
346
- ------------
347
-
348
- Development of The Muffin happens at: https://github.com/klen/muffin
349
-
350
-
351
- Contributors
352
- -------------
353
-
354
- Muffin > 0.40 (completelly rewriten from scratch)
355
-
356
- * `Kirill Klenov <https://github.com/klen>`_
357
-
358
- Muffin < 0.40 (based on AIOHTTP_)
359
-
360
- * `Kirill Klenov <https://github.com/klen>`_
361
- * `Andrew Grigorev <https://github.com/ei-grad>`_
362
- * `Diego Garcia <https://github.com/drgarcia1986>`_
363
-
364
- .. _license:
365
-
366
- License
367
- -------
368
-
369
- Licensed under a `MIT license`_.
370
-
371
- .. _links:
372
-
373
- .. _AIOHTTP: https://docs.aiohttp.org/en/stable/
374
- .. _ASGI: https://asgi.readthedocs.io/en/latest/
375
- .. _Asyncio: https://docs.python.org/3/library/asyncio.html
376
- .. _Curio: https://curio.readthedocs.io/en/latest/
377
- .. _MIT license: http://opensource.org/licenses/MIT
378
- .. _Python: http://python.org
379
- .. _Trio: https://trio.readthedocs.io/en/stable/index.html
380
- .. _klen: https://github.com/klen
381
-
@@ -1,15 +0,0 @@
1
- muffin/__init__.py,sha256=8ldUFl0xhKQ7efnpU-TlB0BjXwkJ8lCOip0Uqcn-G4k,1016
2
- muffin/app.py,sha256=Xh3jDRKFG55hRReQJpdVeOF42ktVciJYP4O-bpOgVvo,5207
3
- muffin/constants.py,sha256=Ga1UJiEdXUk6dIEH_IEVYkFSZxQFPhxie7fCJwQY8V0,71
4
- muffin/errors.py,sha256=I-vKbMMBiMU07zPdKvoJKqA7s4xYAUA-4oZXrRMRzcM,701
5
- muffin/handler.py,sha256=GtIyn-SfWDXsMgJ3GyiJJiWUIezesrFoNVzSHyFI_yA,3736
6
- muffin/manage.py,sha256=Lx3WTWVXiSuGnTR8CfNZKyGXxJBM-rHnagVEPxUSxMs,8829
7
- muffin/plugins.py,sha256=-EyGr66DD_3gOzrQo3QSb_22dYgYk04kKsfTsfirld4,3177
8
- muffin/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- muffin/pytest.py,sha256=51pJ-JJ2vwqiPhR9TPqXvT78X35l3v3oEkN5LBnfY8E,2679
10
- muffin/types.py,sha256=wsUj5oAfqSZMoEf-wyFJLBlWa8Mc-eJGqKLr02HxuXE,153
11
- muffin/utils.py,sha256=zK_pGeYzWoTsv-4-Rlu6MjOhTEmS46NpprH4MsNRcVw,2899
12
- muffin-0.102.3.dist-info/METADATA,sha256=GchyqH2hPVxnBrLBWCFSnpu0puJ5-RyAuErDf90o3Uk,11331
13
- muffin-0.102.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
14
- muffin-0.102.3.dist-info/entry_points.txt,sha256=GvPS3M-tNVPzhUS5jnUpOmWw2NAqedY34VRCYgPYzlM,84
15
- muffin-0.102.3.dist-info/RECORD,,