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

Better error handling · python-webuntis/python-webuntis@c02907b · GitHub

Commit c02907b

Browse files
committed
Better error handling
AND MOAR DOCS
1 parent 239e4d5 commit c02907b

9 files changed

Lines changed: 161 additions & 66 deletions

File tree

‎docs/conf.py‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,3 +251,12 @@
251251

252252
# How to display URL addresses: 'footnote', 'no', or 'inline'.
253253
#texinfo_show_urls = 'footnote'
254+
255+
256+
# http://stackoverflow.com/a/8415805
257+
def skip_modules_docstring(app, what, name, obj, options, lines):
258+
if what == 'module':
259+
del lines[:]
260+
261+
def setup(app):
262+
app.connect('autodoc-process-docstring', skip_modules_docstring)

‎docs/etc.rst‎

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ Timetable Utils
1313

1414
.. autofunction:: table
1515

16-
.. module:: datetime_utils
17-
:members:
18-
1916
Options
2017
============
2118

@@ -24,18 +21,18 @@ Options
2421
In an instance of :py:class:`webuntis.session.Session`, a dictionary-like
2522
object named ``options`` is created. It accepts the following keys:
2623

27-
- *credentials*: A dictionary containing *username* and *password*. Before
24+
- ``credentials``: A dictionary containing ``username`` and ``password``. Before
2825
the session is used, :py:meth:`webuntis.session.JSONRPCSession.login` must
29-
be called, in order to add a *jsessionid* key, which will be deleted when
26+
be called, in order to add a ``jsessionid`` key, which will be deleted when
3027
calling :py:meth:`webuntis.session.JSONRPCSession.logout`.
3128

3229
In theory, you can obtain the jsessionid yourself and add it to the
33-
*credentials* dictionary. In this case, the other two keys are obviously
30+
``credentials`` dictionary. In this case, the other two keys are obviously
3431
not needed.
3532

36-
- *school*: A string containing a valid school name.
33+
- ``school``: A string containing a valid school name.
3734

38-
- *server*: A string containing a host name, a URL, or a URL without path::
35+
- ``server``: A string containing a host name, a URL, or a URL without path::
3936

4037
>>> s.options['server'] = 'thalia.webuntis.com'
4138
>>> s.options['server']
@@ -50,9 +47,9 @@ object named ``options`` is created. It accepts the following keys:
5047
>>> s.options['server']
5148
'http://thalia.webuntis.com/'
5249
>>> s.options['server'] = '!"$%/WebUntis/jsonrpc.do'
53-
ValueError
50+
Traceback blah blah something ValueError
5451

55-
- *useragent*: A string containing a useragent.
52+
- ``useragent``: A string containing a useragent.
5653

5754
Please include useful information, such as an email address, for the server
5855
maintainer. Just like you would do with the HTTP useragents of bots.
@@ -65,4 +62,14 @@ default. You can set the length with::
6562

6663
s = webuntis.Session(..., cachelen=40)
6764

68-
Setting it to `0` obviously disables the cache.
65+
Setting it to ``0`` obviously disables the cache.
66+
67+
Errors and Exceptions
68+
=====================
69+
70+
`python-webuntis` tries to cover as many error codes recieved by the API as possible.
71+
72+
.. automodule:: webuntis.errors
73+
:members:
74+
:show-inheritance:
75+
:member-order: bysource

‎docs/objects.rst‎

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,90 +7,92 @@ Objects and Models
77
Base Classes
88
============
99
.. autoclass:: Result
10-
:members:
10+
:members:
1111

1212

1313
.. autoclass:: ListResult
14-
:members:
14+
:members:
1515

1616

1717
.. autoclass:: ListItem
18-
:members:
18+
:members:
19+
1920

2021
Departments
2122
===========
2223
.. autoclass:: DepartmentList
23-
:members:
24+
:members:
2425

2526
.. autoclass:: DepartmentObject
26-
:members:
27+
:members:
2728

2829
Holidays
2930
========
3031
.. autoclass:: HolidayList
31-
:members:
32+
:members:
3233

3334
.. autoclass:: HolidayObject
34-
:members:
35+
:members:
3536

3637
Klassen
3738
=======
3839
.. autoclass:: KlassenList
3940

4041
.. autoclass:: KlassenObject
41-
:members:
42+
:members:
4243

4344
Timetables and Periods
4445
======================
4546
.. autoclass:: PeriodList
46-
:members:
47+
:members:
4748

4849
.. autoclass:: PeriodObject
49-
:members:
50+
:members:
5051

5152
Rooms
5253
=====
5354
.. autoclass:: RoomList
54-
:members:
55+
:members:
5556

5657
.. autoclass:: RoomObject
57-
:members:
58+
:members:
5859

5960
Schoolyears
6061
===========
6162
.. autoclass:: SchoolyearList
62-
:members:
63+
:members:
6364

6465
.. autoclass:: SchoolyearObject
65-
:members:
66+
:members:
6667

6768
Subjects
6869
========
6970
.. autoclass:: SubjectList
70-
:members:
71+
:members:
7172

7273
.. autoclass:: SubjectObject
73-
:members:
74+
:members:
7475

7576
Teachers
7677
========
7778
.. autoclass:: TeacherList
79+
:members:
7880

7981
.. autoclass:: TeacherObject
80-
:members:
82+
:members:
8183

8284
Timegrid and Timeunits
8385
======================
8486
.. autoclass:: TimeunitList
85-
:members:
87+
:members:
8688

8789
.. autoclass:: TimeunitObject
88-
:members:
90+
:members:
8991

9092
Lesson Types and Period Codes
9193
==============================
9294
.. autoclass:: StatusData
93-
:members:
95+
:members:
9496

9597
.. autoclass:: ColorInfo
96-
:members:
98+
:members:

‎docs/session.rst‎

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,10 @@ Session
44

55
.. module:: webuntis.session
66

7-
.. autoclass:: JSONRPCSession
8-
:members:
9-
107
.. autoclass:: Session
8+
:members:
9+
:inherited-members:
1110

12-
You never should use :py:class:`JSONRPCSession` directly unless you know what
13-
you're doing. Rather use :py:class:`Session`, which actually provides more
14-
than a JSON-RPC session library.
11+
.. note::
1512

16-
For a list of API methods of :py:class:`Session` see :doc:`objects`.
13+
For a list of API methods of :py:class:`Session` see :doc:`objects`.

‎webuntis/errors.py‎

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,35 @@
99

1010

1111
class Error(Exception):
12-
pass
12+
'''Superclass for all `python-webuntis`-specific errors, never gets raised
13+
directly. Positional arguments get joined with ``", "`` and get dumped into
14+
``self.msg``'''
15+
16+
def __init__(self, *args):
17+
Exception.__init__(self, *args)
18+
self.msg = ', '.join(args)
1319

1420

1521
class RemoteError(Error, IOError):
22+
'''There was some kind of error while interacting with the server.'''
23+
pass
24+
25+
26+
class MethodNotFoundError(RemoteError):
27+
'''The JSON-RPC method was not found. This really should not occur.'''
1628
pass
1729

1830

1931
class AuthError(RemoteError):
32+
'''Usually missing credentials, but also other problems while logging in
33+
are covered with this.'''
2034
pass
35+
36+
37+
class BadCredentialsError(AuthError):
38+
'''Invalid username or password'''
39+
pass
40+
41+
42+
class NotLoggedInError(AuthError):
43+
'''The session expired or we never logged in.'''

‎webuntis/objects.py‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -218,10 +218,11 @@ class KlassenList(ListResult):
218218
id of it.
219219
220220
::
221-
>>> s.klassen()
222-
>>>
223-
>>> year = s.schoolyears().filter(id=2)
224-
>>> s.klassen(schoolyear=year)
221+
222+
s.klassen()
223+
224+
year = s.schoolyears().filter(id=2)
225+
s.klassen(schoolyear=year)
225226
226227
'''
227228
_itemclass = KlassenObject
@@ -327,6 +328,8 @@ class PeriodList(ListResult):
327328
s.timetable(klasse=schoolclass) # which is the same as...
328329
s.periods(klasse=schoolclass)
329330
331+
:raises: :py:class:`builtins.ValueError` -- if something was wrong with the
332+
arguments supplied.
330333
'''
331334
_itemclass = PeriodObject
332335
_jsonrpc_method = 'getTimetable'

‎webuntis/session.py‎

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,19 @@
2626

2727

2828
class JSONRPCSession(object):
29-
'''Contains the functions for not much more than a simple JSON-RPC Session.
30-
Can be used as a context-handler.
31-
'''
29+
'''Lower-level version of :py:class:`Session`. Do not use this.'''
30+
3231
options = None
32+
'''Contains a options dict upon initialization. See
33+
:py:class:`webuntis.utils.option_utils` for more information.'''
34+
35+
_errorcodes = {
36+
-32601: errors.MethodNotFoundError,
37+
-8504: errors.BadCredentialsError,
38+
-8520: errors.NotLoggedInError
39+
}
40+
'''This lists the API-errorcodes python-webuntis is able to interpret,
41+
together with the exception that will be thrown.'''
3342

3443
def __init__(self, **kwargs):
3544
# The OptionStore is an extended dictionary, associating validators
@@ -72,17 +81,21 @@ def logout(self, suppress_errors=False):
7281
'''
7382
Log out of session
7483
75-
:param suppress_errors: boolean, whether to not raise an error if we
84+
:param suppress_errors: boolean, whether to suppress errors if we
7685
already were logged out.
86+
87+
:raises: :py:class:`webuntis.errors.NotLoggedInError`
7788
'''
7889
# Send a JSON-RPC 'logout' method without parameters to log out
7990
try:
80-
self.options['credentials']['jsessionid'] # aborts if we don't have creds
91+
# aborts if we don't have creds
92+
self.options['credentials']['jsessionid']
93+
8194
self._make_request('logout')
8295
del self.options['credentials']['jsessionid']
8396
except KeyError as e:
8497
if not suppress_errors:
85-
raise e
98+
raise errors.NotLoggedInError('We already were logged out.')
8699

87100
def login(self):
88101
'''Initializes an authentication, provided we have the credentials for
@@ -92,6 +105,9 @@ def login(self):
92105
chaining::
93106
94107
s = webuntis.Session(...).login()
108+
109+
:raises: :py:class:`webuntis.errors.BadCredentialsError`
110+
:raises: :py:class:`webuntis.errors.AuthError`
95111
'''
96112

97113
if 'username' not in self.options['credentials'] \
@@ -114,7 +130,10 @@ def login(self):
114130
self.options['credentials']['jsessionid'] = res['sessionId']
115131
logging.debug(self.options['credentials']['jsessionid'])
116132
else:
117-
raise errors.AuthError('Something went wrong while authenticating', res)
133+
raise errors.AuthError(
134+
'Something went wrong while authenticating',
135+
res
136+
)
118137

119138
return self
120139

@@ -126,15 +145,32 @@ def _request(self, method, params=None):
126145
self._cache[key] = self._make_request(method, params)
127146
return self._cache[key]
128147

148+
def _handle_json_error(self, req_data, res_data):
149+
'''Given the request and response objects, this raises the appropriate
150+
exceptions.'''
151+
logging.error(res_data)
152+
try:
153+
error = res_data['error']
154+
your_weapon = self._errorcodes[error['code']](error['message'])
155+
except KeyError:
156+
your_weapon = errors.RemoteError(
157+
'Some JSON-RPC-ish error happened. Please report this to the \
158+
developer so he can implement a proper handling.',
159+
str(res_data),
160+
str(req_data)
161+
)
162+
163+
raise your_weapon
164+
129165
def _make_request(self, method, params=None):
130166
'''
131167
A method for sending a JSON-RPC request.
132168
133169
:param method: The JSON-RPC method to be executed
134170
:type method: str
135171
136-
:param params: JSON-RPC parameters to the method \
137-
(should be JSON serializable)
172+
:param params: JSON-RPC parameters to the method (should be JSON
173+
serializable)
138174
:type params: dict
139175
'''
140176

@@ -194,17 +230,12 @@ def _make_request(self, method, params=None):
194230
elif 'result' in res_data:
195231
return res_data['result']
196232
else:
197-
logging.error(res_data)
198-
raise errors.RemoteError(
199-
'Some JSON-RPC-ish error happened',
200-
str(res_data),
201-
str(req_data)
202-
)
233+
self._handle_json_error(req_data, res_data)
203234

204235

205236
class Session(JSONRPCSession):
206-
'''Provides an abstraction layer above the JSON-RPC Instance.
207-
'''
237+
'''The origin of everything you want to do with the WebUntis API. Can be
238+
used as a context-handler.'''
208239

209240
def __getattr__(self, name):
210241
'''Returns a callable which creates an instance (or reuses an old one)

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL