Browse Source

Remove skema in favor of simple custom data modeling stuff

pull/3/head
Andrei 9 years ago
parent
commit
4b9a7f61ff
  1. 107
      disco/gateway/events.py
  2. 99
      disco/types/base.py
  3. 44
      disco/types/channel.py
  4. 93
      disco/types/guild.py
  5. 28
      disco/types/invite.py
  6. 67
      disco/types/message.py
  7. 7
      disco/types/permissions.py
  8. 20
      disco/types/user.py
  9. 30
      disco/types/voice.py
  10. 36
      disco/util/__init__.py
  11. 36
      disco/util/types.py
  12. 1
      requirements.txt

107
disco/gateway/events.py

@ -1,25 +1,19 @@
import inflection import inflection
import skema
import six import six
from disco.util import skema_find_recursive_by_type
from disco.types import Guild, Channel, User, GuildMember, Role, Message, VoiceState from disco.types import Guild, Channel, User, GuildMember, Role, Message, VoiceState
from disco.types.base import Model, snowflake, alias, listof
# TODO: clean this... use BaseType, etc # TODO: clean this... use BaseType, etc
class GatewayEvent(skema.Model): class GatewayEvent(Model):
@staticmethod @staticmethod
def from_dispatch(client, data): def from_dispatch(client, data):
cls = globals().get(inflection.camelize(data['t'].lower())) cls = globals().get(inflection.camelize(data['t'].lower()))
if not cls: if not cls:
raise Exception('Could not find cls for {}'.format(data['t'])) raise Exception('Could not find cls for {}'.format(data['t']))
obj = cls.create(data['d']) return cls.create(data['d'])
for field, value in skema_find_recursive_by_type(obj, skema.ModelType):
value.client = client
return obj
@classmethod @classmethod
def create(cls, obj): def create(cls, obj):
@ -28,31 +22,29 @@ class GatewayEvent(skema.Model):
alias, model = cls._wraps_model alias, model = cls._wraps_model
data = { data = {
k: obj.pop(k) for k in six.iterkeys(model._fields_by_stored_name) if k in obj k: obj.pop(k) for k in six.iterkeys(model._fields) if k in obj
} }
obj[alias] = data obj[alias] = data
self = cls(obj) return cls(obj)
self.validate()
return self
def wraps_model(model, alias=None): def wraps_model(model, alias=None):
alias = alias or model.__name__.lower() alias = alias or model.__name__.lower()
def deco(cls): def deco(cls):
cls.add_field(alias, skema.ModelType(model)) cls._fields[alias] = model
cls._wraps_model = (alias, model) cls._wraps_model = (alias, model)
return cls return cls
return deco return deco
class Ready(GatewayEvent): class Ready(GatewayEvent):
version = skema.IntType(stored_name='v') version = alias(int, 'v')
session_id = skema.StringType() session_id = str
user = skema.ModelType(User) user = User
guilds = skema.ListType(skema.ModelType(Guild)) guilds = listof(Guild)
class Resumed(GatewayEvent): class Resumed(GatewayEvent):
@ -61,17 +53,17 @@ class Resumed(GatewayEvent):
@wraps_model(Guild) @wraps_model(Guild)
class GuildCreate(GatewayEvent): class GuildCreate(GatewayEvent):
unavailable = skema.BooleanType(default=None) unavailable = bool
@wraps_model(Guild) @wraps_model(Guild)
class GuildUpdate(GatewayEvent): class GuildUpdate(GatewayEvent):
guild = skema.ModelType(Guild) pass
class GuildDelete(GatewayEvent): class GuildDelete(GatewayEvent):
id = skema.SnowflakeType() id = snowflake
unavailable = skema.BooleanType(default=None) unavailable = bool
@wraps_model(Channel) @wraps_model(Channel)
@ -90,8 +82,8 @@ class ChannelDelete(ChannelCreate):
class ChannelPinsUpdate(GatewayEvent): class ChannelPinsUpdate(GatewayEvent):
channel_id = skema.SnowflakeType() channel_id = snowflake
last_pin_timestamp = skema.IntType() last_pin_timestamp = int
@wraps_model(User) @wraps_model(User)
@ -112,8 +104,8 @@ class GuildIntegrationsUpdate(GatewayEvent):
class GuildMembersChunk(GatewayEvent): class GuildMembersChunk(GatewayEvent):
guild_id = skema.SnowflakeType() guild_id = snowflake
members = skema.ListType(skema.ModelType(GuildMember)) members = listof(GuildMember)
@wraps_model(GuildMember, alias='member') @wraps_model(GuildMember, alias='member')
@ -122,29 +114,27 @@ class GuildMemberAdd(GatewayEvent):
class GuildMemberRemove(GatewayEvent): class GuildMemberRemove(GatewayEvent):
guild_id = skema.SnowflakeType() guild_id = snowflake
user = skema.ModelType(User) user = User
class GuildMemberUpdate(GatewayEvent): class GuildMemberUpdate(GatewayEvent):
guild_id = skema.SnowflakeType() guild_id = snowflake
user = skema.ModelType(User) user = User
roles = skema.ListType(skema.SnowflakeType()) roles = listof(snowflake)
class GuildRoleCreate(GatewayEvent): class GuildRoleCreate(GatewayEvent):
guild_id = skema.SnowflakeType() guild_id = snowflake
role = skema.ModelType(Role) role = Role
class GuildRoleUpdate(GatewayEvent): class GuildRoleUpdate(GuildRoleCreate):
guild_id = skema.SnowflakeType() pass
role = skema.ModelType(Role)
class GuildRoleDelete(GatewayEvent): class GuildRoleDelete(GuildRoleCreate):
guild_id = skema.SnowflakeType() pass
role = skema.ModelType(Role)
@wraps_model(Message) @wraps_model(Message)
@ -159,32 +149,33 @@ class MessageUpdate(MessageCreate):
class MessageDelete(GatewayEvent): class MessageDelete(GatewayEvent):
id = skema.SnowflakeType() id = snowflake
channel_id = skema.SnowflakeType() channel_id = snowflake
class MessageDeleteBulk(GatewayEvent): class MessageDeleteBulk(GatewayEvent):
channel_id = skema.SnowflakeType() channel_id = snowflake
ids = skema.ListType(skema.SnowflakeType()) ids = listof(snowflake)
class PresenceUpdate(GatewayEvent): class PresenceUpdate(GatewayEvent):
class Game(skema.Model): class Game(Model):
type = skema.IntType() # TODO enum
name = skema.StringType() type = int
url = skema.StringType(required=False) name = str
url = str
user = skema.ModelType(User) user = User
guild_id = skema.SnowflakeType() guild_id = snowflake
roles = skema.ListType(skema.SnowflakeType()) roles = listof(snowflake)
game = skema.ModelType(Game) game = Game
status = skema.StringType() status = str
class TypingStart(GatewayEvent): class TypingStart(GatewayEvent):
channel_id = skema.SnowflakeType() channel_id = snowflake
user_id = skema.SnowflakeType() user_id = snowflake
timestamp = skema.IntType() timestamp = snowflake
@wraps_model(VoiceState, alias='state') @wraps_model(VoiceState, alias='state')
@ -193,6 +184,6 @@ class VoiceStateUpdate(GatewayEvent):
class VoiceServerUpdate(GatewayEvent): class VoiceServerUpdate(GatewayEvent):
token = skema.StringType() token = str
endpoint = skema.StringType() endpoint = str
guild_id = skema.SnowflakeType() guild_id = snowflake

99
disco/types/base.py

@ -1,29 +1,98 @@
import skema import six
import inspect
import functools import functools
from disco.util import skema_find_recursive_by_type from datetime import datetime as real_datetime
# from disco.util.types import DeferredModel
class BaseType(skema.Model): def snowflake(data):
return int(data)
def enum(typ):
def _f(data):
return typ.get(data)
return _f
def listof(typ):
def _f(data):
return list(map(typ, data))
return _f
def dictof(typ, key=None):
def _f(data):
if key:
return {getattr(v, key): v for v in map(typ, data)}
else:
return {k: typ(v) for k, v in six.iteritems(data)}
return _f
def alias(typ, name):
return ('alias', name, typ)
def datetime(typ):
return real_datetime.strptime(typ.rsplit('+', 1)[0], '%Y-%m-%dT%H:%M:%S.%f')
def text(obj):
return six.text_type(obj)
def binary(obj):
return six.text_type(obj)
class ModelMeta(type):
def __new__(cls, name, parents, dct):
fields = {}
for k, v in six.iteritems(dct):
if isinstance(v, tuple):
if v[0] == 'alias':
fields[v[1]] = (k, v[2])
continue
if callable(v) or inspect.isclass(v):
fields[k] = v
dct['_fields'] = fields
return super(ModelMeta, cls).__new__(cls, name, parents, dct)
class Model(six.with_metaclass(ModelMeta)):
def __init__(self, obj, client=None):
for name, typ in self.__class__._fields.items():
dest_name = name
if isinstance(typ, tuple):
dest_name, typ = typ
if name not in obj or not obj[name]:
continue
try:
v = typ(obj[name])
except TypeError as e:
print('Failed during parsing of field {} => {} (`{}`)'.format(name, typ, obj[name]))
raise e
if client and isinstance(v, Model):
v.client = client
setattr(self, dest_name, v)
def update(self, other): def update(self, other):
for name, field in other.__class__._fields.items(): for name in six.iterkeys(self.__class__.fields):
value = getattr(other, name) value = getattr(other, name)
if value: if value:
setattr(self, name, value) setattr(self, name, value)
@classmethod @classmethod
def create(cls, client, data): def create(cls, client, data):
obj = cls(data) return cls(data)
# Valdiate
obj.validate()
for field, value in skema_find_recursive_by_type(obj, skema.ModelType):
value.client = client
obj.client = client
return obj
@classmethod @classmethod
def create_map(cls, client, data): def create_map(cls, client, data):

44
disco/types/channel.py

@ -1,12 +1,11 @@
import skema
from holster.enum import Enum from holster.enum import Enum
from disco.types.base import Model, snowflake, enum, listof, dictof, alias, text
from disco.types.permissions import PermissionValue
from disco.util.functional import cached_property from disco.util.functional import cached_property
from disco.util.types import ListToDictType
from disco.types.base import BaseType
from disco.types.user import User from disco.types.user import User
from disco.types.permissions import PermissionType, Permissions, Permissible from disco.types.permissions import Permissions, Permissible
from disco.voice.client import VoiceClient from disco.voice.client import VoiceClient
@ -23,7 +22,7 @@ PermissionOverwriteType = Enum(
) )
class PermissionOverwrite(BaseType): class PermissionOverwrite(Model):
""" """
A PermissionOverwrite for a :class:`Channel` A PermissionOverwrite for a :class:`Channel`
@ -39,14 +38,14 @@ class PermissionOverwrite(BaseType):
denied : :class:`PermissionValue` denied : :class:`PermissionValue`
All denied permissions All denied permissions
""" """
id = skema.SnowflakeType()
type = skema.StringType(choices=PermissionOverwriteType.ALL_VALUES)
allow = PermissionType() id = snowflake
deny = PermissionType() type = enum(PermissionOverwriteType)
allow = PermissionValue
deny = PermissionValue
class Channel(BaseType, Permissible): class Channel(Model, Permissible):
""" """
Represents a Discord Channel Represents a Discord Channel
@ -71,19 +70,16 @@ class Channel(BaseType, Permissible):
overwrites : dict(snowflake, :class:`disco.types.channel.PermissionOverwrite`) overwrites : dict(snowflake, :class:`disco.types.channel.PermissionOverwrite`)
Channel permissions overwrites. Channel permissions overwrites.
""" """
id = skema.SnowflakeType() id = snowflake
guild_id = skema.SnowflakeType(required=False) guild_id = snowflake
name = text
name = skema.StringType() topic = text
topic = skema.StringType() _last_message_id = alias(snowflake, 'last_message_id')
_last_message_id = skema.SnowflakeType(stored_name='last_message_id') position = int
position = skema.IntType() bitrate = int
bitrate = skema.IntType(required=False) recipients = listof(User)
type = enum(ChannelType)
recipients = skema.ListType(skema.ModelType(User)) overwrites = alias(dictof(PermissionOverwrite, key='id'), 'permission_overwrites')
type = skema.IntType(choices=ChannelType.ALL_VALUES)
overwrites = ListToDictType('id', skema.ModelType(PermissionOverwrite), stored_name='permission_overwrites')
def get_permissions(self, user): def get_permissions(self, user):
""" """

93
disco/types/guild.py

@ -1,17 +1,15 @@
import skema
import copy from disco.types.base import Model, snowflake, listof, dictof, datetime, text, binary
from disco.api.http import APIException from disco.api.http import APIException
from disco.util import to_snowflake from disco.util import to_snowflake
from disco.util.types import PreHookType, ListToDictType
from disco.types.base import BaseType
from disco.types.user import User from disco.types.user import User
from disco.types.voice import VoiceState from disco.types.voice import VoiceState
from disco.types.permissions import PermissionType, PermissionValue, Permissions, Permissible from disco.types.permissions import PermissionValue, Permissions, Permissible
from disco.types.channel import Channel from disco.types.channel import Channel
class Emoji(BaseType): class Emoji(Model):
""" """
An emoji object An emoji object
@ -28,14 +26,14 @@ class Emoji(BaseType):
roles : list(snowflake) roles : list(snowflake)
Roles this emoji is attached to. Roles this emoji is attached to.
""" """
id = skema.SnowflakeType() id = snowflake
name = skema.StringType() name = text
require_colons = skema.BooleanType() require_colons = bool
managed = skema.BooleanType() managed = bool
roles = skema.ListType(skema.SnowflakeType()) roles = listof(snowflake)
class Role(BaseType): class Role(Model):
""" """
A role object A role object
@ -56,16 +54,16 @@ class Role(BaseType):
position : int position : int
The position of this role in the hierarchy. The position of this role in the hierarchy.
""" """
id = skema.SnowflakeType() id = snowflake
name = skema.StringType() name = text
hoist = skema.BooleanType() hoist = bool
managed = skema.BooleanType() managed = bool
color = skema.IntType() color = int
permissions = PermissionType() permissions = PermissionValue
position = skema.IntType() position = int
class GuildMember(BaseType): class GuildMember(Model):
""" """
A GuildMember object A GuildMember object
@ -84,12 +82,12 @@ class GuildMember(BaseType):
roles : list(snowflake) roles : list(snowflake)
Roles this member is part of. Roles this member is part of.
""" """
user = skema.ModelType(User) user = User
guild_id = skema.SnowflakeType(required=False) guild_id = snowflake
mute = skema.BooleanType() mute = bool
deaf = skema.BooleanType() deaf = bool
joined_at = PreHookType(lambda k: k[:-6], skema.DateTimeType()) joined_at = datetime
roles = skema.ListType(skema.SnowflakeType()) roles = listof(snowflake)
def get_voice_state(self): def get_voice_state(self):
""" """
@ -126,7 +124,7 @@ class GuildMember(BaseType):
return self.user.id return self.user.id
class Guild(BaseType, Permissible): class Guild(Model, Permissible):
""" """
A guild object A guild object
@ -170,29 +168,24 @@ class Guild(BaseType, Permissible):
All of the guilds voice states. All of the guilds voice states.
""" """
id = skema.SnowflakeType() id = snowflake
owner_id = snowflake
owner_id = skema.SnowflakeType() afk_channel_id = snowflake
afk_channel_id = skema.SnowflakeType() embed_channel_id = snowflake
embed_channel_id = skema.SnowflakeType() name = text
icon = binary
name = skema.StringType() splash = binary
icon = skema.BinaryType(None) region = str
splash = skema.BinaryType(None) afk_timeout = int
region = skema.StringType() embed_enabled = bool
verification_level = int
afk_timeout = skema.IntType() mfa_level = int
embed_enabled = skema.BooleanType() features = listof(str)
verification_level = skema.IntType() members = dictof(GuildMember, key='id')
mfa_level = skema.IntType() channels = dictof(Channel, key='id')
roles = dictof(Role, key='id')
features = skema.ListType(skema.StringType()) emojis = dictof(Emoji, key='id')
voice_states = dictof(VoiceState, key='session_id')
members = ListToDictType('id', skema.ModelType(copy.deepcopy(GuildMember)))
channels = ListToDictType('id', skema.ModelType(Channel))
roles = ListToDictType('id', skema.ModelType(Role))
emojis = ListToDictType('id', skema.ModelType(Emoji))
voice_states = ListToDictType('session_id', skema.ModelType(VoiceState))
def get_permissions(self, user): def get_permissions(self, user):
""" """

28
disco/types/invite.py

@ -1,13 +1,10 @@
import skema from disco.types.base import Model, datetime
from disco.util.types import PreHookType
from disco.types.base import BaseType
from disco.types.user import User from disco.types.user import User
from disco.types.guild import Guild from disco.types.guild import Guild
from disco.types.channel import Channel from disco.types.channel import Channel
class Invite(BaseType): class Invite(Model):
""" """
An invite object An invite object
@ -32,15 +29,12 @@ class Invite(BaseType):
created_at : datetime created_at : datetime
When this invite was created. When this invite was created.
""" """
code = skema.StringType() code = str
inviter = User
inviter = skema.ModelType(User) guild = Guild
guild = skema.ModelType(Guild) channel = Channel
channel = skema.ModelType(Channel) max_age = int
max_uses = int
max_age = skema.IntType() uses = int
max_uses = skema.IntType() temporary = bool
uses = skema.IntType() created_at = datetime
temporary = skema.BooleanType()
created_at = PreHookType(lambda k: k[:-6], skema.DateTimeType())

67
disco/types/message.py

@ -1,14 +1,13 @@
import re import re
import skema
from disco.types.base import Model, snowflake, text, datetime, dictof, listof
from disco.util import to_snowflake from disco.util import to_snowflake
from disco.util.functional import cached_property from disco.util.functional import cached_property
from disco.util.types import PreHookType, ListToDictType
from disco.types.base import BaseType
from disco.types.user import User from disco.types.user import User
class MessageEmbed(BaseType): class MessageEmbed(Model):
""" """
Message embed object Message embed object
@ -23,13 +22,13 @@ class MessageEmbed(BaseType):
url : str url : str
URL of the embed. URL of the embed.
""" """
title = skema.StringType() title = text
type = skema.StringType() type = str
description = skema.StringType() description = text
url = skema.StringType() url = str
class MessageAttachment(BaseType): class MessageAttachment(Model):
""" """
Message attachment object Message attachment object
@ -50,16 +49,16 @@ class MessageAttachment(BaseType):
width : int width : int
Width of the attachment. Width of the attachment.
""" """
id = skema.SnowflakeType() id = str
filename = skema.StringType() filename = text
url = skema.StringType() url = str
proxy_url = skema.StringType() proxy_url = str
size = skema.IntType() size = int
height = skema.IntType() height = int
width = skema.IntType() width = int
class Message(BaseType): class Message(Model):
""" """
Represents a Message created within a Channel on Discord. Represents a Message created within a Channel on Discord.
@ -94,26 +93,20 @@ class Message(BaseType):
attachments : list(:class:`MessageAttachment`) attachments : list(:class:`MessageAttachment`)
All attachments for this message. All attachments for this message.
""" """
id = skema.SnowflakeType() id = snowflake
channel_id = skema.SnowflakeType() channel_id = snowflake
author = User
author = skema.ModelType(User) content = text
content = skema.StringType() nonce = snowflake
nonce = skema.StringType() timestamp = datetime
edited_timestamp = datetime
timestamp = PreHookType(lambda k: k[:-6], skema.DateTimeType()) tts = bool
edited_timestamp = PreHookType(lambda k: k[:-6], skema.DateTimeType()) mention_everyone = bool
pinned = bool
tts = skema.BooleanType() mentions = dictof(User, key='id')
mention_everyone = skema.BooleanType() mention_roles = listof(snowflake)
embeds = listof(MessageEmbed)
pinned = skema.BooleanType(required=False) attachments = dictof(MessageAttachment, key='id')
mentions = ListToDictType('id', skema.ModelType(User))
mention_roles = skema.ListType(skema.SnowflakeType())
embeds = skema.ListType(skema.ModelType(MessageEmbed))
attachments = ListToDictType('id', skema.ModelType(MessageAttachment))
def __str__(self): def __str__(self):
return '<Message {} ({})>'.format(self.id, self.channel_id) return '<Message {} ({})>'.format(self.id, self.channel_id)

7
disco/types/permissions.py

@ -1,5 +1,3 @@
from skema import NumberType
from holster.enum import Enum, EnumAttr from holster.enum import Enum, EnumAttr
Permissions = Enum( Permissions = Enum(
@ -104,11 +102,6 @@ class PermissionValue(object):
return cls(66060288) return cls(66060288)
class PermissionType(NumberType):
def __init__(self, *args, **kwargs):
super(PermissionType, self).__init__(number_class=PermissionValue, number_type='PermissionValue', *args, **kwargs)
class Permissible(object): class Permissible(object):
def can(self, user, *args): def can(self, user, *args):
perms = self.get_permissions(user) perms = self.get_permissions(user)

20
disco/types/user.py

@ -1,17 +1,13 @@
import skema from disco.types.base import Model, snowflake, text, binary
from disco.types.base import BaseType
class User(Model):
class User(BaseType): id = snowflake
id = skema.SnowflakeType() username = text
discriminator = str
username = skema.StringType() avatar = binary
discriminator = skema.StringType() verified = bool
avatar = skema.BinaryType(None) email = str
verified = skema.BooleanType(required=False)
email = skema.EmailType(required=False)
def to_string(self): def to_string(self):
return '{}#{}'.format(self.username, self.discriminator) return '{}#{}'.format(self.username, self.discriminator)

30
disco/types/voice.py

@ -1,20 +1,16 @@
import skema from disco.types.base import Model, snowflake
from disco.types.base import BaseType
class VoiceState(Model):
session_id = str
class VoiceState(BaseType): guild_id = snowflake
session_id = skema.StringType() channel_id = snowflake
user_id = snowflake
guild_id = skema.SnowflakeType() deaf = bool
channel_id = skema.SnowflakeType() mute = bool
user_id = skema.SnowflakeType() self_deaf = bool
self_mute = bool
deaf = skema.BooleanType() suppress = bool
mute = skema.BooleanType()
self_deaf = skema.BooleanType()
self_mute = skema.BooleanType()
suppress = skema.BooleanType()
@property @property
def guild(self): def guild(self):

36
disco/util/__init__.py

@ -1,5 +1,4 @@
import six import six
import skema
def to_snowflake(i): def to_snowflake(i):
@ -11,38 +10,3 @@ def to_snowflake(i):
return i.id return i.id
raise Exception('{} ({}) is not convertable to a snowflake'.format(type(i), i)) raise Exception('{} ({}) is not convertable to a snowflake'.format(type(i), i))
def _recurse(typ, field, value):
result = []
if isinstance(field, skema.ModelType):
result += skema_find_recursive_by_type(value, typ)
if isinstance(field, (skema.ListType, skema.SetType, skema.DictType)):
if isinstance(field, skema.DictType):
value = value.values()
for item in value:
if isinstance(field.field, typ):
result.append((field.field, item))
result += _recurse(typ, field.field, item)
return result
def skema_find_recursive_by_type(base, typ):
result = []
for name, field in base._fields_by_stored_name.items():
v = getattr(base, name, None)
if not v:
continue
if isinstance(field, typ):
result.append((field, v))
result += _recurse(typ, field, v)
return result

36
disco/util/types.py

@ -1,36 +0,0 @@
from skema import BaseType, DictType
class PreHookType(BaseType):
_hashable = False
def __init__(self, func, field, **kwargs):
self.func = func
self.field = field
super(PreHookType, self).__init__(**kwargs)
def to_python(self, value):
value = self.func(value)
return self.field.to_python(value)
def to_storage(self, *args, **kwargs):
return self.field.to_storage(*args, **kwargs)
class ListToDictType(DictType):
def __init__(self, key, *args, **kwargs):
super(ListToDictType, self).__init__(*args, **kwargs)
self.key = key
def to_python(self, value):
if not value:
return {}
to_python = self.field.to_python
obj = {}
for item in value:
item = to_python(item)
obj[getattr(item, self.key)] = item
return obj

1
requirements.txt

@ -3,5 +3,4 @@ holster==1.0.1
inflection==0.3.1 inflection==0.3.1
requests==2.11.1 requests==2.11.1
six==1.10.0 six==1.10.0
# skema==0.0.1
websocket-client==0.37.0 websocket-client==0.37.0

Loading…
Cancel
Save