root/session.py

Revision 297:0df29400e6b8, 6.4 kB (checked in by Stefan Schwarzer <sschwarzer@sschwarzer.net>, 1 year ago)
Made all imports relative (apart from those in the
`wsbrowser`/`wsbrowser.py` scripts, where they're necessary).

The former approach of using only absolute imports was kind of
cleaner, especially with respect to future Python versions. - However,
the absolute imports were also _very_ confusing (even for me) and
required a funny workaround for development if a Websourcebrowser
package was already installed somewhere in `sys.path`.
Line 
1 # coding: utf-8
2 # Copyright (C) 2007, Stefan Schwarzer
3 #
4 # Permission is hereby granted, free of charge, to any person
5 # obtaining a copy of this software and associated documentation files
6 # (the "Software"), to deal in the Software without restriction,
7 # including without limitation the rights to use, copy, modify, merge,
8 # publish, distribute, sublicense, and/or sell copies of the Software,
9 # and to permit persons to whom the Software is furnished to do so,
10 # subject to the following conditions:
11 #
12 # The above copyright notice and this permission notice shall be
13 # included in all copies or substantial portions of the Software.
14 #
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 # SOFTWARE.
23
24 import cgi
25 import urllib
26 import urlparse
27
28 # Websourcebrowser modules
29 import coding
30 import config
31
32
33 class SessionError(Exception):
34     pass
35
36
37 class Session(object):
38     def __init__(self, params, defaults=None):
39         """
40         Initialize the `Session` instance by providing a list of
41         parameter names as argument `params`. If the list is empty,
42         raise a `SessionError`.
43
44         If the argument `defaults` is given, it must be a mapping from
45         parameter names to their default string values. If the mapping
46         contains a name, which isn't in the list `params`, raise a
47         `SessionError`.
48         """
49         if not params:
50             raise SessionError("parameter list must not be empty")
51         self.params = params
52         if defaults is None:
53             defaults = {}
54         for name in defaults:
55             if name not in params:
56                 raise SessionError("default name isn't present in params list")
57         self.defaults = defaults
58
59     def get_from_url(self, url):
60         """
61         Return a dictionary of decoded query parameters (similar to
62         the result of `cgi.parse_qs`) from the URL `url`:
63
64         >>> s = Session(['a'])
65         >>> s.get_from_url("http://localhost/page?a=Test%20string&b=1")
66         {'a': u'Test string'}
67
68         Only the parameters in the list attribute `params` are
69         examined. That means, if `params` isn't defined, the
70         dictionary will be empty.
71
72         If one of the parameter names isn't present in the URL at all,
73         the parameter name will not occur in the dictionary, unless
74         the name is present in the `defaults` dictionary (see
75         `__init__` method).
76         """
77         query_string = urlparse.urlparse(url)[4]
78         query_mapping = cgi.parse_qs(query_string)
79         result = {}
80         # use only key/value pairs qualified in `params`
81         for param in self.params:
82             # ignore params which are not in the URL
83             if param in query_mapping:
84                 value = query_mapping[param]
85                 # make sure each string is a unicode string
86                 for index, item in enumerate(value):
87                     value[index] = coding.decode(item)
88                 # turn one-element lists into scalars
89                 if len(value) == 1:
90                     value = value[0]
91                 result[param] = value
92         # try to use a default if a parameter wasn't set
93         for param in self.defaults:
94             if param not in result:
95                 result[param] = self.defaults[param]
96         return result
97
98     def add_to_url(self, url, params):
99         """
100         Return an updated URL so that the parameter values from the
101         dictionary `params` is included in the query string of the
102         original URL. Only parameters from the instance variable
103         `params` are considered.
104
105         Example of method usage:
106
107         >>> s = Session(['a'])
108         >>> s.add_to_url("/page", {'a': u"Test"})
109         '/page?a=Test'
110
111         If a parameter is set to its default value, don't include it
112         in the URL.
113         """
114         parsed_url = list(urlparse.urlparse(url))
115         query_string = urlparse.urlparse(url)[4]
116         query_mapping = cgi.parse_qs(query_string)
117         # use only key/value pairs qualified in `params`
118         for param in self.params:
119             if param in params:
120                 value = params[param]
121                 # don't include defaults
122                 if (param in self.defaults) and (self.defaults[param] == value):
123                     continue
124                 # make sure the value is a list
125                 if isinstance(value, basestring):
126                     value = [value]
127                 # if necessary, encode unicode values
128                 for index, item in enumerate(value):
129                     if isinstance(item, unicode):
130                         value[index] = coding.encode(item)
131                 query_mapping[param] = value
132         query_string = urllib.urlencode(query_mapping, doseq=True)
133         parsed_url[4] = query_string
134         return urlparse.urlunparse(parsed_url).replace("+", "%20")
135
136
137 # define the default session, i. e. one with all the usual query
138 #  parameters, including parameter conversion
139 class DefaultSession(Session):
140     def __init__(self):
141         super(DefaultSession, self).__init__(
142           ['anchor', 'dir_levels', 'frames'],
143           defaults={'anchor': "", 'frames': "yes"})
144
145     def get_from_url(self, url):
146         # `config.dir_levels` is only known after parsing the command
147         # line, and we don't want depend on the position of the
148         # import of the `session` module
149         self.defaults['dir_levels'] = str(config.dir_levels)
150         params = super(DefaultSession, self).get_from_url(url)
151         try:
152             int_dir_levels = int(params['dir_levels'])
153             if int_dir_levels >= 1:
154                 params['dir_levels'] = int_dir_levels
155         except ValueError:
156             params['dir_levels'] = int(self.defaults['dir_levels'])
157         return params
158
159     def add_to_url(self, url, params):
160         # don't modify the dictionary which was passed in as argument
161         params = params.copy()
162         if 'dir_levels' in params:
163             params['dir_levels'] = str(params['dir_levels'])
164         return super(DefaultSession, self).add_to_url(url, params)
165
166
167 default_session = DefaultSession()
Note: See TracBrowser for help on using the browser.