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 +15 -11
- muffin/handler.py +7 -10
- muffin/manage.py +62 -50
- muffin/plugins.py +21 -13
- muffin/utils.py +3 -3
- muffin-1.0.0.dist-info/METADATA +187 -0
- muffin-1.0.0.dist-info/RECORD +15 -0
- {muffin-0.102.3.dist-info → muffin-1.0.0.dist-info}/WHEEL +1 -1
- muffin-0.102.3.dist-info/METADATA +0 -381
- muffin-0.102.3.dist-info/RECORD +0 -15
- {muffin-0.102.3.dist-info → muffin-1.0.0.dist-info}/entry_points.txt +0 -0
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
|
7
|
+
from inspect import currentframe, isawaitable
|
7
8
|
from logging.config import dictConfig
|
8
|
-
from typing import TYPE_CHECKING, Any, Final, Mapping
|
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
|
-
|
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:
|
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 =
|
107
|
+
bgtasks = BACKGROUND_TASKS.get()
|
107
108
|
if bgtasks is not None:
|
108
109
|
await aio_wait(*bgtasks)
|
109
|
-
|
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
|
-
|
129
|
-
|
130
|
-
|
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 =
|
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
|
-
|
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,
|
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
|
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
|
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:
|
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:
|
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:
|
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,
|
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
|
-
|
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
|
-
|
166
|
-
argname = name.lower()
|
162
|
+
for name, param in sig.parameters.items():
|
167
163
|
arghelp = docs.get(name, "")
|
168
|
-
|
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
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
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
|
-
|
204
|
-
|
174
|
+
if param.kind == param.VAR_POSITIONAL:
|
175
|
+
parser.add_argument(name, nargs="*", metavar=name, help=arghelp)
|
176
|
+
continue
|
205
177
|
|
206
|
-
|
207
|
-
|
178
|
+
if param.kind == param.VAR_KEYWORD:
|
179
|
+
# **kwargs not supported in CLI parser
|
180
|
+
continue
|
208
181
|
|
209
|
-
|
210
|
-
|
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:
|
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
|
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
|
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:
|
45
|
+
conftest: Callable[[], AbstractAsyncContextManager] | None = None
|
45
46
|
|
46
|
-
def __init__(self, app:
|
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
|
68
|
+
if iscoroutinefunction(self.startup):
|
68
69
|
await self.startup()
|
69
70
|
|
70
71
|
async def __aexit__(self, exc_type, exc, tb):
|
71
|
-
if
|
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:
|
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
|
103
|
+
if callable(self.middleware):
|
103
104
|
app.middleware(self.middleware)
|
104
105
|
|
105
106
|
# Bind startup
|
106
|
-
if
|
107
|
+
if callable(self.startup):
|
107
108
|
app.on_startup(self.startup)
|
108
109
|
|
109
110
|
# Bind shutdown
|
110
|
-
if
|
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"
|
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
|
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,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
|
-
|
muffin-0.102.3.dist-info/RECORD
DELETED
@@ -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,,
|
File without changes
|