FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Restructuring of requests, WIP · pascaaaal/python-webuntis@932575f · GitHub

Repository navigation

Commit 932575f

Browse files
committed
Restructuring of requests, WIP
1 parent 4a52d89 commit 932575f

7 files changed

Lines changed: 159 additions & 177 deletions

File tree

‎docs/etc.rst‎

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,12 @@ _:py:class:`webuntis.session.Session`. Unless noted otherwise, they get saved
2323
in a dictionary located in the instance's ``options`` attribute and can be
2424
modified afterwards.
2525

26-
- ``credentials``: A dictionary containing ``username`` and ``password``. Before
27-
the session is used, :py:meth:`webuntis.session.JSONRPCSession.login` must
28-
be called, in order to add a ``jsessionid`` key, which will be deleted when
29-
calling :py:meth:`webuntis.session.JSONRPCSession.logout`.
30-
31-
In theory, you can obtain the jsessionid yourself and add it to the
32-
``credentials`` dictionary. In this case, the other two keys are obviously
33-
not needed.
26+
- ``username``: The username used for the API.
27+
28+
- ``password``: The password used for the API.
29+
30+
- ``jsessionid``: The current session key. Shouldn't be changed unless you
31+
know what you're doing.
3432

3533
- ``school``: A string containing a valid school name.
3634

‎webuntis/session.py‎

Lines changed: 127 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,112 @@
2525
import simplejson as json # from dependency "simplejson"
2626

2727

28+
class JSONRPCRequest(object):
29+
def __init__(self, session, method, params=None):
30+
self._session = session
31+
self._method = method
32+
self._params = params or {}
33+
34+
def request(self):
35+
data = None
36+
i = 0
37+
while data is None and i <= self._session.options['login_repeat']:
38+
i += 1
39+
try:
40+
data = self._make_request()
41+
except errors.NotLoggedInError as e:
42+
if self._session.options['login_repeat'] > 0:
43+
self._session.logout(suppress_errors=True)
44+
self._session.login()
45+
else:
46+
raise e
47+
48+
return data
49+
50+
def _make_request(self):
51+
'''
52+
A method for sending a JSON-RPC request.
53+
54+
:param method: The JSON-RPC method to be executed
55+
:type method: str
56+
57+
:param params: JSON-RPC parameters to the method (should be JSON
58+
serializable)
59+
:type params: dict
60+
'''
61+
62+
def _handle_json_error():
63+
'''A helper function for handling JSON error codes.'''
64+
logging.error(res_data)
65+
try:
66+
error = res_data['error']
67+
exc = self._errorcodes[error['code']](error['message'])
68+
except KeyError:
69+
exc = errors.RemoteError(
70+
'Some JSON-RPC-ish error happened. Please report this to the \
71+
developer so he can implement a proper handling.',
72+
str(res_data),
73+
str(req_data)
74+
)
75+
76+
raise exc
77+
78+
url = self._session.options['server']
79+
url += '?school=' + self._session.options['school']
80+
cookie_header = True
81+
if self._method == 'authenticate':
82+
cookie_header = False
83+
elif 'jsessionid' not in self._session.options:
84+
raise errors.AuthError('Don\'t have JSESSIONID. Did you already log out?')
85+
86+
req_data = {
87+
'id': str(datetime.datetime.today()),
88+
'method': self._method,
89+
'params': self._params,
90+
'jsonrpc': '2.0'
91+
}
92+
93+
req_data_json = json.dumps(req_data).encode()
94+
95+
logging.debug('Making new request:')
96+
logging.debug('URL: ' + url)
97+
logging.debug(req_data_json)
98+
99+
req = urlrequest.Request(
100+
url,
101+
req_data_json,
102+
{
103+
'User-Agent': self._session.options['useragent'],
104+
'Content-Type': 'application/json'
105+
}
106+
)
107+
if cookie_header:
108+
req.add_header(
109+
'Cookie',
110+
'JSESSIONID=' + self._session.options['jsessionid']
111+
)
112+
113+
# this will eventually raise errors, e.g. if there's an unexpected http
114+
# status code
115+
res = urlrequest.urlopen(req)
116+
117+
res_str = res.read().decode('utf-8')
118+
119+
try:
120+
res_data = json.loads(res_str)
121+
logging.debug('Valid JSON found')
122+
logging.debug(res_data)
123+
except ValueError:
124+
raise errors.RemoteError('Invalid JSON', str(res_str))
125+
126+
if res_data['id'] != req_data['id']:
127+
raise errors.RemoteError('Request id was not the same as the one returned')
128+
elif 'result' in res_data:
129+
return res_data['result']
130+
else:
131+
handle_json_error(req_data, res_data)
132+
133+
28134
class JSONRPCSession(object):
29135
'''Lower-level version of :py:class:`Session`. Do not use this.'''
30136

@@ -39,7 +145,7 @@ class JSONRPCSession(object):
39145
def __init__(self, **kwargs):
40146
# The OptionStore is an extended dictionary, associating validators
41147
# and other helper methods with each key
42-
self.options = utils.FilterDict(utils.option_utils.option_parsers)
148+
self.options = utils.FilterDict(utils.option_utils.options)
43149
options = {
44150
'server': None,
45151
'school': None,
@@ -48,24 +154,17 @@ def __init__(self, **kwargs):
48154
'password': None,
49155
'jsessionid': None,
50156
'cachelen': 20, # Not implemented in LruDict
51-
'keep_session_alive': True
157+
'login_repeat': 0
52158
}
53159
options.update(kwargs)
54160

55-
self.options.update({
56-
'server': options['server'],
57-
'school': options['school'],
58-
'useragent': options['useragent'],
59-
'credentials': {
60-
'username': options['username'],
61-
'password': options['password'],
62-
'jsessionid': options['jsessionid']
63-
},
64-
'keep_session_alive': options['keep_session_alive']
65-
})
66161
if options['cachelen'] > 0:
67162
self._cache = utils.LruDict(maxlen=options['cachelen'])
68163

164+
del options['cachelen']
165+
166+
self.options.update(options)
167+
69168
def __enter__(self):
70169
'''Context-manager'''
71170
return self
@@ -87,10 +186,10 @@ def logout(self, suppress_errors=False):
87186
# Send a JSON-RPC 'logout' method without parameters to log out
88187
try:
89188
# aborts if we don't have creds
90-
self.options['credentials']['jsessionid']
189+
self.options['jsessionid']
91190

92191
self._make_request('logout')
93-
del self.options['credentials']['jsessionid']
192+
del self.options['jsessionid']
94193
except KeyError:
95194
if not suppress_errors:
96195
raise errors.NotLoggedInError('We already were logged out.')
@@ -108,25 +207,25 @@ def login(self):
108207
:raises: :py:class:`webuntis.errors.AuthError`
109208
'''
110209

111-
if 'username' not in self.options['credentials'] \
112-
or 'password' not in self.options['credentials']:
210+
if 'username' not in self.options \
211+
or 'password' not in self.options:
113212
raise errors.AuthError('No login data specified.')
114213

115214
logging.debug('Trying to authenticate with username/password...')
116215
logging.debug('Username: ' +
117-
self.options['credentials']['username'] +
216+
self.options['username'] +
118217
' Password: ' +
119-
self.options['credentials']['password'])
218+
self.options['password'])
120219
res = self._make_request('authenticate', {
121-
'user': self.options['credentials']['username'],
122-
'password': self.options['credentials']['password'],
220+
'user': self.options['username'],
221+
'password': self.options['password'],
123222
'client': self.options['useragent']
124223
})
125224
logging.debug(res)
126225
if 'sessionId' in res:
127226
logging.debug('Did get a jsessionid from the server:')
128-
self.options['credentials']['jsessionid'] = res['sessionId']
129-
logging.debug(self.options['credentials']['jsessionid'])
227+
self.options['jsessionid'] = res['sessionId']
228+
logging.debug(self.options['jsessionid'])
130229
else:
131230
raise errors.AuthError(
132231
'Something went wrong while authenticating',
@@ -136,7 +235,7 @@ def login(self):
136235
return self
137236

138237
def _request(self, method, params=None):
139-
'''A wrapper for _make_request using the LRU Cache'''
238+
'''A wrapper for :py:class:`JSONRPCRequest` using the LRU Cache'''
140239

141240
if self._cache is None:
142241
return self._make_request(method, params)
@@ -146,6 +245,10 @@ def _request(self, method, params=None):
146245
self._cache[key] = self._make_request(method, params)
147246
return self._cache[key]
148247

248+
def _make_request(self, method, params=None):
249+
return JSONRPCRequest(self, method, params).request()
250+
251+
149252
_errorcodes = {
150253
-32601: errors.MethodNotFoundError,
151254
-8504: errors.BadCredentialsError,
@@ -156,96 +259,7 @@ def _request(self, method, params=None):
156259

157260

158261

159-
def _make_request(self, method, params=None):
160-
'''
161-
A method for sending a JSON-RPC request.
162-
163-
:param method: The JSON-RPC method to be executed
164-
:type method: str
165-
166-
:param params: JSON-RPC parameters to the method (should be JSON
167-
serializable)
168-
:type params: dict
169-
'''
170262

171-
def _handle_json_error():
172-
'''A helper function for handling JSON error codes.'''
173-
logging.error(res_data)
174-
try:
175-
error = res_data['error']
176-
exc = self._errorcodes[error['code']](error['message'])
177-
except KeyError:
178-
exc = errors.RemoteError(
179-
'Some JSON-RPC-ish error happened. Please report this to the \
180-
developer so he can implement a proper handling.',
181-
str(res_data),
182-
str(req_data)
183-
)
184-
185-
if type(exc) is errors.NotLoggedInError and self.options['keep_session_alive']:
186-
self.logout(suppress_errors=True)
187-
self.login()
188-
return self._make_request(method, params)
189-
else:
190-
raise exc
191-
192-
url = self.options['server']
193-
url += '?school=' + self.options['school']
194-
cookie_header = True
195-
if method == 'authenticate':
196-
cookie_header = False
197-
elif 'jsessionid' not in self.options['credentials']:
198-
raise errors.AuthError('Don\'t have JSESSIONID. Did you already log out?')
199-
200-
if not params:
201-
params = {}
202-
203-
req_data = {
204-
'id': str(datetime.datetime.today()),
205-
'method': method,
206-
'params': params,
207-
'jsonrpc': '2.0'
208-
}
209-
210-
req_data_json = json.dumps(req_data).encode()
211-
212-
logging.debug('Making new request:')
213-
logging.debug('URL: ' + url)
214-
logging.debug(req_data_json)
215-
216-
req = urlrequest.Request(
217-
url,
218-
req_data_json,
219-
{
220-
'User-Agent': self.options['useragent'],
221-
'Content-Type': 'application/json'
222-
}
223-
)
224-
if cookie_header:
225-
req.add_header(
226-
'Cookie',
227-
'JSESSIONID=' + self.options['credentials']['jsessionid']
228-
)
229-
230-
# this will eventually raise errors, e.g. if there's an unexpected http
231-
# status code
232-
res = urlrequest.urlopen(req)
233-
234-
res_str = res.read().decode('utf-8')
235-
236-
try:
237-
res_data = json.loads(res_str)
238-
logging.debug('Valid JSON found')
239-
logging.debug(res_data)
240-
except ValueError:
241-
raise errors.RemoteError('Invalid JSON', str(res_str))
242-
243-
if res_data['id'] != req_data['id']:
244-
raise errors.RemoteError('Request id was not the same as the one returned')
245-
elif 'result' in res_data:
246-
return res_data['result']
247-
else:
248-
return handle_json_error(req_data, res_data)
249263

250264

251265
class Session(JSONRPCSession):

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL