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
|