Skip to content

Commit 4f3a593

Browse files
committedAug 23, 2011
edit support!
1 parent 3bb5228 commit 4f3a593

File tree

3 files changed

+136
-48
lines changed

3 files changed

+136
-48
lines changed
 

‎bot/wiki/exceptions.py

+25-6
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"""
44
EarwigBot's Wiki Toolset: Exceptions
55
6-
This module contains all exceptions used by the wiki package.
6+
This module contains all exceptions used by the wiki package. There are a lot.
77
"""
88

99
class WikiToolsetError(Exception):
@@ -22,11 +22,6 @@ class LoginError(WikiToolsetError):
2222
"""An error occured while trying to login. Perhaps the username/password is
2323
incorrect."""
2424

25-
class PermissionsError(WikiToolsetError):
26-
"""We tried to do something we don't have permission to, like a non-admin
27-
trying to delete a page, or trying to edit a page when no login information
28-
was provided."""
29-
3025
class NamespaceNotFoundError(WikiToolsetError):
3126
"""A requested namespace name or namespace ID does not exist."""
3227

@@ -45,3 +40,27 @@ class RedirectError(WikiToolsetError):
4540
class UserNotFoundError(WikiToolsetError):
4641
"""Attempting to get certain information about a user that does not
4742
exist."""
43+
44+
class EditError(WikiToolsetError):
45+
"""We got some error while editing. Sometimes, a subclass of this exception
46+
will be used, like PermissionsError or EditConflictError."""
47+
48+
class PermissionsError(EditError):
49+
"""We tried to do something we don't have permission to, like a non-admin
50+
trying to delete a page, or trying to edit a page when no login information
51+
was provided."""
52+
53+
class EditConflictError(EditError):
54+
"""We've gotten an edit conflict or a (rarer) delete/recreate conflict."""
55+
56+
class NoContentError(EditError):
57+
"""We tried to create a page or new section with no content."""
58+
59+
class ContentTooBigError(EditError):
60+
"""The edit we tried to push exceeded the article size limit."""
61+
62+
class SpamDetectedError(EditError):
63+
"""The spam filter refused our edit."""
64+
65+
class FilteredError(EditError):
66+
"""The edit filter refused our edit."""

‎bot/wiki/page.py

+108-41
Original file line numberDiff line numberDiff line change
@@ -222,24 +222,104 @@ def _load_content(self, result=None):
222222
self._load_attributes()
223223
self._force_existence()
224224

225-
def _get_token(self):
226-
"""Tries to get an edit token for the page.
227-
228-
This is actually the same as the delete and protect tokens, so we'll
229-
use it for everything. Raises PermissionError if we're not allowed to
230-
edit the page, otherwise sets self._token and self._starttimestamp.
225+
def _edit(self, params=None, text=None, summary=None, minor=None, bot=None,
226+
force=None, section=None, captcha_id=None, captcha_word=None,
227+
tries=0):
228+
"""Edit a page!
229+
230+
If `params` is given,
231231
"""
232-
params = {"action": "query", "prop": "info", "intoken": "edit",
233-
"titles": self._title}
234-
result = self._site._api_query(params)
235-
236-
try:
237-
self._token = result["query"]["pages"].values()[0]["edittoken"]
238-
except KeyError:
232+
if not self._token:
233+
self._load_attributes()
234+
if not self._token:
239235
e = "You don't have permission to edit this page."
240236
raise PermissionsError(e)
237+
self._force_validity() # Weed these out before we get too far
238+
239+
if not params:
240+
params = self._build_edit_params(text, summary, minor, bot, force,
241+
section, captcha_id, captcha_word)
242+
243+
try:
244+
result = self._site._api_query(params)
245+
except SiteAPIError as error:
246+
if not hasattr(error, code):
247+
raise
248+
result = self._handle_edit_exceptions(error, params, tries)
249+
250+
# These attributes are now invalidated:
251+
self._content = None
252+
self._basetimestamp = None
253+
254+
return result
255+
256+
def _build_edit_params(self, text, summary, minor, bot, force, section,
257+
captcha_id, captcha_word):
258+
"""Something."""
259+
hashed = md5(text).hexdigest() # Checksum to ensure text is correct
260+
params = {"action": "edit", "title": self._title, "text": text,
261+
"token": self._token, "summary": summary, "md5": hashed}
262+
263+
if section:
264+
params["section"] = section
265+
if captcha_id and captcha_word:
266+
params["captchaid"] = captcha_id
267+
params["captchaword"] = captcha_word
268+
if minor:
269+
params["minor"] = "true"
241270
else:
242-
self._starttimestamp = strftime("%Y-%m-%dT%H:%M:%SZ")
271+
params["notminor"] = "true"
272+
if bot:
273+
params["bot"] = "true"
274+
if self._exists == 2: # Page does not already exist
275+
params["recreate"] = "true"
276+
277+
if not force:
278+
params["starttimestamp"] = self._starttimestamp
279+
if self._basetimestamp:
280+
params["basetimestamp"] = self._basetimestamp
281+
if self._exists == 3:
282+
# Page exists; don't re-create it by accident if it's deleted:
283+
params["nocreate"] = "true"
284+
else:
285+
# Page does not exist; don't edit if it already exists:
286+
params["createonly"] = "true"
287+
288+
return params
289+
290+
def _handle_edit_exceptions(self, error, params, tries):
291+
"""Something."""
292+
if error.code in ["noedit", "cantcreate", "protectedtitle",
293+
"noimageredirect"]:
294+
raise PermissionsError(error.info)
295+
296+
elif error.code in ["noedit-anon", "cantcreate-anon",
297+
"noimageredirect-anon"]:
298+
if not all(self._site._login_info): # Insufficient login info
299+
raise PermissionsError(error.info)
300+
if self.tries == 0: # We have login info; try to login:
301+
self._site._login(self._site._login_info)
302+
return self._edit(params=params, tries=1)
303+
else: # We already tried to log in and failed!
304+
e = "Although we should be logged in, we are not. This may be a cookie problem or an odd bug."
305+
raise LoginError(e)
306+
307+
elif error.code in ["editconflict", "pagedeleted", "articleexists"]:
308+
raise EditConflictError(error.info)
309+
310+
elif error.code in ["emptypage", "emptynewsection"]:
311+
raise NoContentError(error.info)
312+
313+
elif error.code == "contenttoobig":
314+
raise ContentTooBigError(error.info)
315+
316+
elif error.code == "spamdetected":
317+
raise SpamDetectedError(error.info)
318+
319+
elif error.code == "filtered":
320+
raise FilteredError(error.info)
321+
322+
raise EditError(", ".join((error.code, error.info)))
243323

244324
def title(self, force=False):
245325
"""Returns the Page's title, or pagename.
@@ -482,36 +562,23 @@ def edit(self, text, summary, minor=False, bot=True, force=False):
482562
the edit will be marked as a bot edit, but only if we actually have a
483563
bot flag.
484564
485-
Use `force` to ignore edit conflicts and page deletions/recreations
486-
that occured between getting our edit token and editing our page. Be
487-
careful with this!
565+
Use `force` to push the new content even if there's an edit conflict or
566+
the page was deleted/recreated between getting our edit token and
567+
editing our page. Be careful with this!
488568
"""
489-
if not self._token:
490-
self._get_token()
569+
self._edit(text=text, summary=summary, minor=minor, bot=bot,
570+
force=force)
491571

492-
hashed = md5(text).hexdigest()
572+
def add_section(self, text, title, minor=False, bot=True, force=False):
573+
"""Adds a new section to the bottom of the page.
493574
494-
params = {"action": "edit", "title": self._title, "text": text,
495-
"token": self._token, "summary": summary, "md5": hashed}
575+
The arguments for this are the same as those for edit(), but instead of
576+
providing a summary, you provide a section title.
496577
497-
if minor:
498-
params["minor"] = "true"
499-
else:
500-
params["notminor"] = "true"
501-
if bot:
502-
params["bot"] = "true"
503-
504-
if not force:
505-
params["starttimestamp"] = self._starttimestamp
506-
if self._basetimestamp:
507-
params["basetimestamp"] = self._basetimestamp
508-
else:
509-
params["recreate"] = "true"
510-
511-
result = self._site._api_query(params)
512-
print result
578+
Likewise, raised exceptions are the same as edit()'s.
513579
514-
def add_section(self, text, title, minor=False, bot=True):
515-
"""
580+
This should create the page if it does not already exist, with just the
581+
new section as content.
516582
"""
517-
pass
583+
self._edit(text=text, summary=title, minor=minor, bot=bot, force=force,
584+
section="new")

‎bot/wiki/site.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,9 @@ def _api_query(self, params, tries=0, wait=5):
176176
return self._api_query(params, tries=tries, wait=wait*3)
177177
else:
178178
e = 'API query failed: got error "{0}"; server says: "{1}".'
179-
raise SiteAPIError(e.format(code, info))
179+
error = SiteAPIError(e.format(code, info))
180+
error.code, error.info = code, info
181+
raise error
180182

181183
def _load_attributes(self, force=False):
182184
"""Load data about our Site from the API.

0 commit comments

Comments
 (0)
Please sign in to comment.