Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/tutorius/uam/__init__.py
blob: babb1d865cb15b7ce675e68f96ff4223bd5cc8d0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# Copyright (C) 2009, Tutorius.org
# Copyright (C) 2009, Vincent Vinet <vince.vinet@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
"""
Universal Addressing Mechanism module

Allows addressing Events, signals, widgets, etc for supported platforms

Different subschemes must implement a parser in the form

    from . import UAMParser

    class MyParser(UAMParser):
        @classmethod
        def getAddon(self, uri):
            ...

    where getAddon accepts a single parameter which is a URI as defined here

A base parser, UAMParser, should be sufficient for most uses
The base parser will attempt to create any addon according to this uri format:

<scheme>://<activity>/?<query>#<addon>

where:

<scheme> is the base scheme

<activity> is the activity's dns identifier, such as battleship.tutorius.org

<query> is a list of key=value attributes to pass to the constructor. If 
    multiple values are passed for the same key, the last one will prevail

<addon> is the addon to use, and passed directly to addon.create()
"""

from urllib2 import urlparse
from .. import addon

SCHEME="tap" #Tutorius Adressing Protocol

_parsers = {}

def addParser(name, cls):
    """ Reconfigure urlparse to accept an additional scheme
    @param name schema to add
    """
    #Check if abstract. If so, ignore
    if getattr(cls, "__abstract__", False):
        return

    _parsers[name] = cls

    #Add to uses_netloc
    if not name in urlparse.uses_netloc:
        urlparse.uses_netloc.append(name)
    
    #Add to uses_relative
    if not name in urlparse.uses_relative:
        urlparse.uses_relative.append(name)

    ##Add to uses_params
    #if not name in urlparse.uses_params:
    #    urlparse.uses_params.append(name)

    #Add to uses_query
    if not name in urlparse.uses_query:
        urlparse.uses_query.append(name)

    #Add to uses_frament
    if not name in urlparse.uses_fragment:
        urlparse.uses_fragment.append(name)

class UAMParserMeta(type):
    """
    UAM Parsers should have this as a Metaclass in order to 
    automatically register with urlparse.
    """
    def __new__(cls, name, bases, attrs):
        """ Type pre-constructor
        @param cls This metaclass
        @param name name of the new class
        @param bases list of bases for the new class
        @param attrs dictionary of attributes
        """
        # Generate the scheme and subscheme if possible
        #FIXME Keep the parent scheme if we don't have one?
        if "__scheme__" in attrs:
            scheme = attrs["__scheme__"]
            for base in bases:
                if hasattr(base, "__scheme__"):
                    scheme = getattr(base, "__scheme__") + "." + scheme
            attrs["__scheme__"] = scheme
        return type.__new__(cls, name, bases, attrs)
            
    def __init__(cls, name, bases, attrs):
        """ Type constructor
        @param cls constructed class
        @param name name of the new class
        @param bases list of bases for the new class
        @param attrs dictionary of attributes
        """
        if "__scheme__" in attrs:
            #Register the parser in the module and urlparse
            addParser(attrs["__scheme__"], cls)

class UAMParser(object):
    __metaclass__ = UAMParserMeta
    __scheme__ = SCHEME
    __abstract__ = False #Only there to show it exists

    @classmethod
    def getAddon(cls, uri):
        """Parse an uri
        @param uri URI object
        """
        params = UAMParser.stripQueryLists(uri.query)
        return addon.create(uri.addon, **params)
        
    @staticmethod
    def stripQueryLists(querydict):
        newdict = {}
        for key, value in querydict.items():
            newdict[key] = urlparse.unquote(value[-1])
        return newdict

class SchemeError(Exception):
    def __init__(self, message):
        Exception.__init__(self, message)
        ## Commenting this line as it is causing an error in the tests
        ##self.message = message

class ParserError(Exception):
    pass

class URI(object):
    def __init__(self, uri):
        self._uri = None
        self._set_uri(uri)

    def _get_uri(self):
        return self._uri.geturl()
    def _set_uri(self, uri):
        if isinstance(uri, urlparse.ParseResult):
            self._uri = uri
        else:
            self._uri = urlparse.urlparse(uri)
        #Ensure we only use valid schemes
        if self.scheme not in _parsers:
            raise SchemeError("Scheme not supported: %s" % self.scheme)

    uri = property(fset=_set_uri, fget=_get_uri)
    activity = property(lambda self: self._uri.hostname)
    scheme = property(lambda self: self._uri.scheme)
    addon = property(lambda self: self._uri.fragment)
    path = property(lambda self: self._uri.path)
    #params = property(lambda self: urlparse.parse_qs(self._uri.params))
    def _get_querydict(self):
        return urlparse.parse_qs(self._uri.query)
    query = property(_get_querydict)

    #Serialization functions
    def __getstate__(self):
        return dict(uri=self.uri)
    def __setstate__(self, state):
        self.uri = state["uri"]

    def getAddon(self):
        return _parsers[self.scheme].getAddon(self)

#Import the different parsers that we know about
import gtkparser
import gobjectparser