PK ! VdNw w hugo/__init__.py"""
The MIT License (MIT)
Copyright (c) 2017-2018 Nariman Safiulin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
__version__ = "0.5.0"
PK ! @E
hugo/client.py"""
The MIT License (MIT)
Copyright (c) 2017-2018 Nariman Safiulin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
import asyncio
import discord
from .constants import EventType
from .context import Context
from .middleware import Middleware
class Client(discord.Client):
"""Wrapper around default library client.
Parameters
----------
root_middleware : :class:`Middleware`
A middleware to run on new events.
"""
def __init__(self, root_middleware: Middleware, *args, **kwargs):
super().__init__(*args, **kwargs)
self.root_middleware = root_middleware
async def default_next_callable(ctx, *args, **kwargs): # noqa: D401
"""Default callable as the `next` parameter.
Ideally, it should not be called due to it is just a "right" argument
for event handlers that should ignore next callables.
"""
pass
def dispatch(self, event, *args, **kwargs): # noqa: D401
"""Wrapper around default event dispatcher for a client."""
super().dispatch(event, *args, **kwargs)
try:
event_type = EventType(event)
except ValueError:
event_type = EventType.UNKNOWN
#
ctx = Context(self, event_type, *args, **kwargs)
asyncio.ensure_future(
self._run_event(
self.root_middleware.run,
event,
ctx=ctx,
next=self.default_next_callable,
),
loop=self.loop,
)
PK ! hugo/constants.py"""
The MIT License (MIT)
Copyright (c) 2017-2018 Nariman Safiulin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
import enum
class EventType(enum.Enum):
"""List of event types which can be received."""
UNKNOWN = None
CONNECT = "connect"
READY = "ready"
SHARD_READY = "shard_ready"
RESUMED = "resumed"
ERROR = "error"
SOCKET_RAW_RECEIVE = "socket_raw_receive"
SOCKET_RAW_SEND = "socket_raw_send"
TYPING = "typing"
MESSAGE = "message"
MESSAGE_DELETE = "message_delete"
RAW_MESSAGE_DELETE = "raw_message_delete"
RAW_BULK_MESSAGE_DELETE = "raw_bulk_message_delete"
MESSAGE_EDIT = "message_edit"
RAW_MESSAGE_EDIT = "raw_message_edit"
REACTION_ADD = "reaction_add"
RAW_REACTION_ADD = "raw_reaction_add"
REACTION_REMOVE = "reaction_remove"
RAW_REACTION_REMOVE = "raw_reaction_remove"
REACTION_CLEAR = "reaction_clear"
RAW_REACTION_CLEAR = "raw_reaction_clear"
PRIVATE_CHANNEL_CREATE = "private_channel_create"
PRIVATE_CHANNEL_DELETE = "private_channel_delete"
PRIVATE_CHANNEL_UPDATE = "private_channel_update"
PRIVATE_CHANNEL_PINS_UPDATE = "private_channel_pins_update"
GUILD_CHANNEL_CREATE = "guild_channel_create"
GUILD_CHANNEL_DELETE = "guild_channel_delete"
GUILD_CHANNEL_UPDATE = "guild_channel_update"
GUILD_CHANNEL_PINS_UPDATE = "guild_channel_pins_update"
MEMBER_JOIN = "member_join"
MEMBER_REMOVE = "member_remove"
MEMBER_UPDATE = "member_update"
GUILD_JOIN = "guild_join"
GUILD_REMOVE = "guild_remove"
GUILD_UPDATE = "guild_update"
GUILD_ROLE_CREATE = "guild_role_create"
GUILD_ROLE_DELETE = "guild_role_delete"
GUILD_ROLE_UPDATE = "guild_role_update"
GUILD_EMOJIS_UPDATE = "guild_emojis_update"
GUILD_AVAILABLE = "guild_available"
GUILD_UNAVAILABLE = "guild_unavailable"
VOICE_STATE_UPDATE = "voice_state_update"
MEMBER_BAN = "member_ban"
MEMBER_UNBAN = "member_unban"
GROUP_JOIN = "group_join"
GROUP_REMOVE = "group_remove"
RELATIONSHIP_ADD = "relationship_add"
RELATIONSHIP_REMOVE = "relationship_remove"
RELATIONSHIP_UPDATE = "relationship_update"
PK ! R`y y hugo/context.py"""
The MIT License (MIT)
Copyright (c) 2017-2018 Nariman Safiulin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
from .constants import EventType
class Context:
"""Event processing context."""
def __init__(self, client, event: EventType, *args, **kwargs):
self.client = client
self.event = event
self.args = list(args)
self.kwargs = kwargs
PK ! iI hugo/exceptions.py"""
The MIT License (MIT)
Copyright (c) 2017-2018 Nariman Safiulin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
class HugoException(Exception):
"""Base exception class for the library."""
pass
PK ! >! ! hugo/handler.py"""
The MIT License (MIT)
Copyright (c) 2017-2018 Nariman Safiulin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
import re
import discord
from .constants import EventType
from .context import Context
from .middleware import Middleware, MiddlewareResult, middleware
class EventNormalization(Middleware):
"""Event parameters normalization.
A middleware for converting positional parameters into keyword for known
events.
"""
EVENTS = {
EventType.CONNECT: tuple(),
EventType.READY: tuple(),
EventType.SHARD_READY: ("shard_id",),
EventType.RESUMED: tuple(),
EventType.ERROR: tuple(),
EventType.SOCKET_RAW_RECEIVE: ("msg",),
EventType.SOCKET_RAW_SEND: ("payload",),
EventType.TYPING: ("channel", "user", "when"),
EventType.MESSAGE: ("message",),
EventType.MESSAGE_DELETE: ("message",),
EventType.RAW_MESSAGE_DELETE: ("payload",),
EventType.RAW_BULK_MESSAGE_DELETE: ("payload",),
EventType.MESSAGE_EDIT: ("before", "after"),
EventType.RAW_MESSAGE_EDIT: ("payload",),
EventType.REACTION_ADD: ("reaction", "user"),
EventType.RAW_REACTION_ADD: ("payload",),
EventType.REACTION_REMOVE: ("reaction, user",),
EventType.RAW_REACTION_REMOVE: ("payload",),
EventType.REACTION_CLEAR: ("message", "reactions"),
EventType.RAW_REACTION_CLEAR: ("payload",),
EventType.PRIVATE_CHANNEL_CREATE: ("channel",),
EventType.PRIVATE_CHANNEL_DELETE: ("channel",),
EventType.PRIVATE_CHANNEL_UPDATE: ("before", "after"),
EventType.PRIVATE_CHANNEL_PINS_UPDATE: ("channel", "last_pin"),
EventType.GUILD_CHANNEL_CREATE: ("channel",),
EventType.GUILD_CHANNEL_DELETE: ("channel",),
EventType.GUILD_CHANNEL_UPDATE: ("before", "after"),
EventType.GUILD_CHANNEL_PINS_UPDATE: ("channel", "last_pin"),
EventType.MEMBER_JOIN: ("member",),
EventType.MEMBER_REMOVE: ("member",),
EventType.MEMBER_UPDATE: ("before", "after"),
EventType.GUILD_JOIN: ("guild",),
EventType.GUILD_REMOVE: ("guild",),
EventType.GUILD_UPDATE: ("before", "after"),
EventType.GUILD_ROLE_CREATE: ("role",),
EventType.GUILD_ROLE_DELETE: ("role",),
EventType.GUILD_ROLE_UPDATE: ("before", "after"),
EventType.GUILD_EMOJIS_UPDATE: ("guild", "before", "after"),
EventType.GUILD_AVAILABLE: ("guild",),
EventType.GUILD_UNAVAILABLE: ("guild",),
EventType.VOICE_STATE_UPDATE: ("member", "before", "after"),
EventType.MEMBER_BAN: ("guild", "user"),
EventType.MEMBER_UNBAN: ("guild", "user"),
EventType.GROUP_JOIN: ("channel", "user"),
EventType.GROUP_REMOVE: ("channel", "user"),
EventType.RELATIONSHIP_ADD: ("relationship",),
EventType.RELATIONSHIP_REMOVE: ("relationship",),
EventType.RELATIONSHIP_UPDATE: ("before", "after"),
}
async def run(self, *args, ctx: Context, next, **kwargs):
if ctx.event in self.EVENTS:
for i, parameter in enumerate(self.EVENTS[ctx.event]):
ctx.kwargs[parameter] = ctx.args[i]
plen = len(self.EVENTS[ctx.event])
ctx.args = ctx.args[plen:]
#
return await next(*args, ctx=ctx, **kwargs)
class EventConstraint(Middleware):
"""Event type filter.
Attributes
----------
event : :class:`.constants.EventType`
Event type to allow.
"""
def __init__(self, event: EventType):
self.event = event
async def run(self, *args, ctx: Context, next, **kwargs):
if ctx.event == self.event:
return await next(*args, ctx=ctx, **kwargs)
return MiddlewareResult.IGNORE
def event(event: EventType):
"""Append a :class:`EventConstraint` middleware to the chain with given
event type constraint.
"""
return middleware(EventConstraint(event))
class Pattern(Middleware):
"""Message context filter.
The message should match the given regex pattern to invoke the next
middleware.
Only named subgroups will be passed to the next middleware.
Attributes
----------
pattern : str
The source regex string.
"""
def __init__(self, pattern: str):
self.pattern = pattern
async def run(self, *args, ctx: Context, next, **kwargs):
result = re.search(self.pattern, ctx.kwargs["message"].content)
if result:
kwargs.update(result.groupdict())
return await next(*args, ctx=ctx, **kwargs)
#
return MiddlewareResult.IGNORE
def pattern(pattern: str):
"""Append a :class:`Pattern` middleware to the chain.
Parameters
----------
pattern : str
The source regex string.
"""
return middleware(Pattern(pattern))
class BotConstraint(Middleware):
"""Message context filter.
The message should be authored by or not authored by a real user to invoke
the next middleware.
Attributes
----------
authored_by_bot : bool
Is the message should be authored by bot or not.
"""
def __init__(self, *, authored_by_bot: bool):
self.authored_by_bot = authored_by_bot
async def run(self, *args, ctx: Context, next, **kwargs):
if not self.authored_by_bot ^ ctx.kwargs["message"].author.bot:
return await next(*args, ctx=ctx, **kwargs)
return MiddlewareResult.IGNORE
def not_authored_by_bot():
"""Append a :class:`BotConstraint` middleware to the chain with a "not
authored by bot" constraint.
"""
return middleware(BotConstraint(authored_by_bot=False))
def authored_by_bot():
"""Append a :class:`BotConstraint` middleware to the chain with a "authored
by bot" constraint.
"""
return middleware(BotConstraint(authored_by_bot=True))
class ChannelType(Middleware):
"""Message context filter.
The message should be sent in the given channel types to invoke the next
middleware.
"""
def __init__(
self,
*,
guild: bool = False,
private: bool = False,
text: bool = False,
# voice: bool = False,
dm: bool = False,
group: bool = False,
):
self.text = guild or text
# self.voice = guild or voice
self.dm = private or dm
self.group = private or group
async def run(self, *args, ctx: Context, next, **kwargs):
channel = ctx.kwargs["message"].channel
# fmt: off
if (
self.text and isinstance(channel, discord.TextChannel)
# or self.voice and isinstance(channel, discord.VoiceChannel)
or self.dm and isinstance(channel, discord.DMChannel)
or self.group and isinstance(channel, discord.GroupChannel)
):
return await next(*args, ctx=ctx, **kwargs)
# fmt: on
return MiddlewareResult.IGNORE
def channel_type(
*,
guild: bool = False,
private: bool = False,
text: bool = False,
# voice: bool = False,
dm: bool = False,
group: bool = False,
):
"""Append a :class:`ChannelType` middleware to the chain with given
constraints.
"""
return middleware(
ChannelType(
guild=guild,
private=private,
text=text,
# voice=voice,
dm=dm,
group=group,
)
)
PK ! "cw!t, t, hugo/middleware.py"""
The MIT License (MIT)
Copyright (c) 2017-2018 Nariman Safiulin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
import abc
import asyncio
import enum
import typing
from . import context
class MiddlewareResult(enum.Enum):
"""Enum values for middleware results.
One of these values can be returned in a middleware instead of actual data.
Anything returned by a middleware and is not a enum value is considered as a
success of the process.
"""
OK = enum.auto()
IGNORE = enum.auto()
class Middleware(abc.ABC):
"""Event processing middleware.
Middleware are useful for filtering events or extending functionality.
Middleware should return something (even `None`) to indicate success,
otherwise :class:`MiddlewareResult` values can be used.
Functions can also be converted into a middleware by using
:class:`MiddlewareFunction` or :func:`as_middleware` decorator.
Attributes
----------
fn : Optional[Callable]
The source function, when the last middleware in the middleware chain is
a converted function or it is a converted function itself. Can be not
presented, if middleware chain created not with :func:`middleware`
decorator.
.. seealso::
:class:`MiddlewareChain`.
"""
def __init__(self):
self.fn = None
@abc.abstractmethod
async def run(self, *args, ctx: context.Context, next, **kwargs):
"""Middleware's main logic.
.. note::
Context is a keyword parameter.
Parameters
----------
ctx : :class:`.context.Context`
Event processing context.
.. note::
Provided context can be replaced and passed to the next
middleware, but do it only if needed.
next : Callable
The next function to call. Not necessarily a middleware. Pass
context, all positional and keyword parameters, even if unused.
Should be awaited.
.. warning::
Context is a keyword parameter. If you will pass it as a
positional parameter, this can cause errors on all next
middleware in a chain.
Returns
-------
:class:`MiddlewareResult`
A enum value when no actual data can be provided.
:any:`typing.Any`
Middleware data.
"""
pass # pragma: no cover
@staticmethod
def is_successful_result(value):
"""Return `True`, if given value is a successful middleware result."""
if value == MiddlewareResult.IGNORE:
return False
return True
async def __call__(self, *args, ctx: context.Context, next, **kwargs):
"""Invoke middleware with given parameters."""
return await self.run(*args, ctx=ctx, next=next, **kwargs)
class MiddlewareFunction(Middleware):
"""Middleware class for converting functions into valid middleware.
Parameters
----------
fn : Callable
A function to convert into a middleware. Should be a coroutine.
Raises
------
ValueError
If given function is not a coroutine.
Attributes
----------
fn : Callable
A function converted into a middleware. A coroutine.
"""
def __init__(self, fn):
super().__init__()
if not asyncio.iscoroutinefunction(fn):
raise ValueError("Not a coroutine")
self.fn = fn
async def run(self, *args, ctx: context.Context, next, **kwargs):
"""Invoke function as a middleware with given parameters."""
return await self.fn(*args, ctx=ctx, next=next, **kwargs)
class MiddlewareState(Middleware):
"""Middleware class that can provide a state for next middleware.
It is an alternative to middleware as class methods.
By default, just adds given state to parameters for the next middleware as
`state` parameter.
Parameters
----------
state : :any:`typing.Any`
A state to provide.
Attributes
----------
fn : Callable
A state for the next middleware.
"""
def __init__(self, state):
super().__init__()
self.state = state
async def run(self, *args, ctx: context.Context, next, **kwargs): # noqa: D102
return await next(*args, ctx=ctx, state=self.state, **kwargs)
class MiddlewareCollection(Middleware, abc.ABC):
"""Class for grouping middleware. It is a middleware itself.
Method :meth:`run` is abstract. Each subclass should implement own behavior
of how to run group of middleware. For example, run only one middleware, if
success, or run all middleware, or run middleware until desired results is
obtained, etc. Useful, when it is known, what middleware can return.
Attributes
----------
collection : List[:class:`Middleware`]
List of middleware to run. Take a note that order of middleware in the
list can be used in a subclass implementation.
"""
def __init__(self):
super().__init__()
self.collection = []
def add_middleware(self, middleware: Middleware):
"""Add middleware to the list.
Can be used as a decorator.
Parameters
----------
middleware : :class:`Middleware`
A middleware to add to the list.
Returns
-------
:class:`Middleware`
A given middleware.
Raises
------
ValueError
If given parameter is not a middleware.
"""
if not isinstance(middleware, Middleware):
raise ValueError("Not a middleware")
#
self.collection.append(middleware)
return middleware
@abc.abstractmethod
async def run(self, *args, ctx: context.Context, next, **kwargs): # noqa: D102
pass # pragma: no cover
class MiddlewareChain(MiddlewareCollection):
"""Class for chaining middleware. It is a middleware itself.
Attributes
----------
collection : List[:class:`Middleware`]
List of middleware to run in a certain order. The first items is a
last-to-call middleware (in other words, list is reversed).
"""
def __init__(self):
super().__init__()
def add_middleware(self, middleware: Middleware): # noqa: D102
super().add_middleware(middleware)
if len(self.collection) == 1:
self.fn = middleware.fn
return middleware
async def run(self, *args, ctx: context.Context, next, **kwargs): # noqa: D102
# Oh dear! Please, rewrite it...
for current in self.collection:
next = (
lambda current, next: lambda *args, ctx, **kwargs: current.run(
*args, ctx=ctx, next=next, **kwargs
)
)(current, next)
return await next(*args, ctx=ctx, **kwargs)
def as_middleware(fn: typing.Callable):
"""Convert function into a middleware.
If you are planning to chain the converted function with another middleware,
just use :func:`middleware` helper. It will convert the function into a
middleware for you, if needed.
.. warning::
Do not use it, if not sure.
Parameters
----------
fn : Callable
A function to convert into a middleware.
"""
# We don't care, when somebody is convering a middleware into another one...
return MiddlewareFunction(fn)
def collection_of(
collection_class: typing.Type[MiddlewareCollection],
middleware: typing.Sequence[typing.Union[Middleware, typing.Callable]],
):
"""Create a new collection of given middleware.
If any of given parameters is not a middleware, it will be converted into a
middleware for you.
Parameters
----------
collection : Type[:class:`MiddlewareCollection`]
A collection class to create collection of.
middleware : Sequence[Union[Middleware, Callable]]
A list of middleware to create collection of.
"""
collection = collection_class()
for mw in middleware:
if not isinstance(mw, Middleware):
mw = as_middleware(mw)
collection.add_middleware(mw)
#
return collection
def chain_of(
middleware: typing.Sequence[typing.Union[Middleware, typing.Callable]]
):
"""Create a new chain of given middleware.
If any of given parameters is not a middleware, it will be converted into a
middleware for you.
Parameters
----------
middleware : Sequence[Union[Middleware, Callable]]
A list of middleware to create collection of.
"""
return collection_of(MiddlewareChain, middleware)
def middleware(outer_middleware: Middleware):
"""Append a middleware to the chain.
If decorated function is not a middleware, it will be converted into a
middleware by decorator.
Parameters
----------
outer_middleware : :class:`Middleware`
A middleware to append to the chain.
"""
if not isinstance(outer_middleware, Middleware):
outer_middleware = as_middleware(outer_middleware)
def decorator(inner_middleware: Middleware):
if isinstance(inner_middleware, MiddlewareChain):
inner_middleware.add_middleware(outer_middleware)
return inner_middleware
#
return collection_of(
MiddlewareChain, [inner_middleware, outer_middleware]
)
return decorator
class OneOfAll(MiddlewareCollection):
"""Middleware group with "first success" condition.
It will process middleware list until one of them return successful result.
See :class:`Middleware` for information about successful results.
"""
async def run(self, *args, ctx: context.Context, next, **kwargs): # noqa: D102
for mw in self.collection:
result = await mw.run(*args, ctx=ctx, next=next, **kwargs)
if self.is_successful_result(result):
return result
return MiddlewareResult.IGNORE
PK ! VbK K hugo-0.5.0.dist-info/LICENSEMIT License
Copyright (c) 2017-2018 Nariman Safiulin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
PK !HlŃT T hugo-0.5.0.dist-info/WHEEL
A
н#J@Z|Jmqvh&#hڭw!Ѭ"J˫(} %PK !H 1 hugo-0.5.0.dist-info/METADATAn0EiKrM" bq(螖2QTɡ]}V"+Q3HVvVi~"Vʠmlx_(1:Tװv^{`֩&uAEiyLaF٠6:I~+/{'2fKXET@FVn7?=+wC3ɍ\_sU!FV[%)sg4BN{g(3YXB[c
Xk&px':b$\qnٍI>ͧjry_CuGl7z`W
3yoa/WPK !Hr hugo-0.5.0.dist-info/RECORDmɒ@ <f8ҀIYy%W
0_TGƵ1+6 :e