datatypes.py 6.66 KB
Newer Older
1

Dan Pascu's avatar
Dan Pascu committed
2
"""Definitions for datatypes used in configuration extensions."""
3 4

import os
5
import re
Adrian Georgescu's avatar
Adrian Georgescu committed
6 7
from urllib.request import pathname2url, url2pathname
from urllib.parse import urlparse
8

9
from application.python.types import MarkerType
10
from sipsimple.configuration.datatypes import Hostname, List
11 12 13 14

from blink.resources import ApplicationData


Dan Pascu's avatar
Dan Pascu committed
15 16 17
__all__ = ['ApplicationDataPath', 'DefaultPath', 'SoundFile', 'CustomSoundFile', 'HTTPURL', 'FileURL', 'IconDescriptor', 'PresenceState', 'PresenceStateList', 'GraphTimeScale']


Adrian Georgescu's avatar
Adrian Georgescu committed
18
class ApplicationDataPath(str):
19 20 21 22
    def __new__(cls, path):
        path = os.path.normpath(path)
        if path.startswith(ApplicationData.directory+os.path.sep):
            path = path[len(ApplicationData.directory+os.path.sep):]
Adrian Georgescu's avatar
Adrian Georgescu committed
23
        return str.__new__(cls, path)
24 25 26 27 28 29 30 31 32 33

    @property
    def normalized(self):
        return ApplicationData.get(self)


class SoundFile(object):
    def __init__(self, path, volume=100):
        self.path = path
        self.volume = int(volume)
34
        if not (0 <= self.volume <= 100):
35 36 37
            raise ValueError('illegal volume level: %d' % self.volume)

    def __getstate__(self):
Adrian Georgescu's avatar
Adrian Georgescu committed
38
        return '%s,%s' % (self.__dict__['path'], self.volume)
39 40 41

    def __setstate__(self, state):
        try:
Adrian Georgescu's avatar
Adrian Georgescu committed
42
            path, volume = state.rsplit(',', 1)
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
        except ValueError:
            self.__init__(state)
        else:
            self.__init__(path, volume)

    def __repr__(self):
        return '%s(%r, %r)' % (self.__class__.__name__, self.path, self.volume)

    def _get_path(self):
        return ApplicationData.get(self.__dict__['path'])
    def _set_path(self, path):
        path = os.path.normpath(path)
        if path.startswith(ApplicationData.directory+os.path.sep):
            path = path[len(ApplicationData.directory+os.path.sep):]
        self.__dict__['path'] = path
    path = property(_get_path, _set_path)
    del _get_path, _set_path


Adrian Georgescu's avatar
Adrian Georgescu committed
62
class DefaultPath(metaclass=MarkerType): pass
63

Dan Pascu's avatar
Dan Pascu committed
64

65
class CustomSoundFile(object): # check if this data type is still needed -Dan
66 67 68
    def __init__(self, path=DefaultPath, volume=100):
        self.path = path
        self.volume = int(volume)
Dan Pascu's avatar
Dan Pascu committed
69
        if not (0 <= self.volume <= 100):
70 71 72 73
            raise ValueError('illegal volume level: %d' % self.volume)

    def __getstate__(self):
        if self.path is DefaultPath:
Adrian Georgescu's avatar
Adrian Georgescu committed
74
            return 'default'
75
        else:
Adrian Georgescu's avatar
Adrian Georgescu committed
76
            return 'file:%s,%s' % (self.__dict__['path'], self.volume)
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103

    def __setstate__(self, state):
        match = re.match(r'^(?P<type>default|file:)(?P<path>.+?)?(,(?P<volume>\d+))?$', state)
        if match is None:
            raise ValueError('illegal value: %r' % state)
        data = match.groupdict()
        if data.pop('type') == 'default':
            data['path'] = DefaultPath
        data['volume'] = data['volume'] or 100
        self.__init__(**data)

    def __repr__(self):
        return '%s(%r, %r)' % (self.__class__.__name__, self.path, self.volume)

    def _get_path(self):
        path = self.__dict__['path']
        return path if path is DefaultPath else ApplicationData.get(path)
    def _set_path(self, path):
        if path is not DefaultPath:
            path = os.path.normpath(path)
            if path.startswith(ApplicationData.directory+os.path.sep):
                path = path[len(ApplicationData.directory+os.path.sep):]
        self.__dict__['path'] = path
    path = property(_get_path, _set_path)
    del _get_path, _set_path


Adrian Georgescu's avatar
Adrian Georgescu committed
104
class HTTPURL(str):
105
    def __new__(cls, value):
Adrian Georgescu's avatar
Adrian Georgescu committed
106
        value = str(value)
107
        url = urlparse(value)
Adrian Georgescu's avatar
Adrian Georgescu committed
108
        if url.scheme not in ('http', 'https'):
109 110 111 112 113 114 115
            raise ValueError("illegal HTTP URL scheme (http and https only): %s" % url.scheme)
        Hostname(url.hostname)
        if url.port is not None and not (0 < url.port < 65536):
            raise ValueError("illegal port value: %d" % url.port)
        return value


Adrian Georgescu's avatar
Adrian Georgescu committed
116
class FileURL(str):
117 118
    def __new__(cls, value):
        if not value.startswith('file:'):
Adrian Georgescu's avatar
Adrian Georgescu committed
119
            value = 'file:' + pathname2url(os.path.abspath(value))
Adrian Georgescu's avatar
Adrian Georgescu committed
120
        return str.__new__(cls, value)
121 122


Adrian Georgescu's avatar
Adrian Georgescu committed
123
class ParsedURL(str):
124 125 126
    fragment = property(lambda self: self.__parsed__.fragment)
    netloc   = property(lambda self: self.__parsed__.netloc)
    params   = property(lambda self: self.__parsed__.params)
127
    path     = property(lambda self: self.__parsed__.path if self.scheme != 'file' else url2pathname(self.__parsed__.path))
128 129 130 131 132 133 134
    query    = property(lambda self: self.__parsed__.query)
    scheme   = property(lambda self: self.__parsed__.scheme)

    def __init__(self, value):
        self.__parsed__ = urlparse(self)


135 136
class IconDescriptor(object):
    def __init__(self, url, etag=None):
137
        self.url = ParsedURL(url)
138 139 140 141
        self.etag = etag

    def __getstate__(self):
        if self.etag is None:
Adrian Georgescu's avatar
Adrian Georgescu committed
142
            return str(self.url)
143
        else:
Adrian Georgescu's avatar
Adrian Georgescu committed
144
            return '%s,%s' % (self.url, self.etag)
145 146 147

    def __setstate__(self, state):
        try:
Adrian Georgescu's avatar
Adrian Georgescu committed
148
            url, etag = state.rsplit(',', 1)
149 150 151 152 153 154 155
        except ValueError:
            self.__init__(state)
        else:
            self.__init__(url, etag)

    def __eq__(self, other):
        if isinstance(other, IconDescriptor):
156
            return self.url == other.url and self.etag == other.etag
157 158 159 160 161 162 163 164 165 166
        return NotImplemented

    def __ne__(self, other):
        equal = self.__eq__(other)
        return NotImplemented if equal is NotImplemented else not equal

    def __repr__(self):
        return '%s(%r, %r)' % (self.__class__.__name__, self.url, self.etag)


167 168
class PresenceState(object):
    def __init__(self, state, note=None):
Adrian Georgescu's avatar
Adrian Georgescu committed
169
        self.state = str(state)
170 171 172 173
        self.note = note

    def __getstate__(self):
        if not self.note:
Adrian Georgescu's avatar
Adrian Georgescu committed
174
            return str(self.state)
175
        else:
Adrian Georgescu's avatar
Adrian Georgescu committed
176
            return '%s,%s' % (self.state, self.note)
177 178 179

    def __setstate__(self, data):
        try:
Adrian Georgescu's avatar
Adrian Georgescu committed
180
            state, note = data.split(',', 1)
181 182 183 184 185 186 187
        except ValueError:
            self.__init__(data)
        else:
            self.__init__(state, note)

    def __eq__(self, other):
        if isinstance(other, PresenceState):
188
            return self.state == other.state and self.note == other.note
189 190 191 192 193 194 195 196 197 198 199 200 201 202
        return NotImplemented

    def __ne__(self, other):
        equal = self.__eq__(other)
        return NotImplemented if equal is NotImplemented else not equal

    def __repr__(self):
        return '%s(%r, %r)' % (self.__class__.__name__, self.state, self.note)


class PresenceStateList(List):
    type = PresenceState


Dan Pascu's avatar
Dan Pascu committed
203 204 205 206 207 208 209 210 211 212 213
class GraphTimeScale(int):
    min_value = 2
    max_value = 4

    def __new__(cls, value):
        value = int(value)
        if not (cls.min_value <= value <= cls.max_value):
            raise ValueError("expected an integer number between %d and %d, found %d" % (cls.min_value, cls.max_value, value))
        return value