jenkins-bot has submitted this change and it was merged.
Change subject: [FIX] Re-enable BlockEntry and MoveEntry methods
......................................................................
[FIX] Re-enable BlockEntry and MoveEntry methods
- added new methods for PatrolEntry and RightsEntry classes
- Parts of BlockEntry and MoveEntry are broken since MW 1.19 as the log
entry specific data was accessed using log type while with MW 1.19 it's
always 'params'
- new generic hidden method _params() because the data key may depend on
the entry type.
- use data['params'] for newer mw versions else data[_expectedType]
- flags must not be splitted, it is a list of flag strings now
- data['expiry'] may not exists for infinite blocks. Return None here
- isAutoblockRemoval could never be called as method. I removed it.
- deprecate new_ns and new_title methods, use the newer names.
- use target_ns instead of new_ns()
- use target_page instead of new_title(). It was renamed because the
method returns a page object
- target_title just returns the title string.
Tests are added.
bug: T100424
bug: T99516
Change-Id: I610540df99fd6f5a77f0cfc8702555c2a082307a
---
M pywikibot/logentries.py
A tests/logentry_tests.py
2 files changed, 247 insertions(+), 36 deletions(-)
Approvals:
John Vandenberg: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/logentries.py b/pywikibot/logentries.py
index c4919cb..238f0b2 100644
--- a/pywikibot/logentries.py
+++ b/pywikibot/logentries.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""Objects representing Mediawiki log entries."""
#
-# (C) Pywikibot team, 2007-2013
+# (C) Pywikibot team, 2007-2015
#
# Distributed under the terms of the MIT license.
#
@@ -10,8 +10,14 @@
__version__ = '$Id$'
#
-from pywikibot.exceptions import Error
+import sys
+
import pywikibot
+from pywikibot.exceptions import Error
+from pywikibot.tools import deprecated
+
+if sys.version_info[0] > 2:
+ basestring = (str, )
_logger = "wiki"
@@ -51,6 +57,22 @@
def __hash__(self):
return self.logid()
+ @property
+ def _params(self):
+ """
+ Additional data for some log entry types.
+
+ @rtype: dict or None
+ """
+ if 'params' in self.data:
+ return self.data['params']
+ else: # try old mw style preceding mw 1.19
+ try:
+ return self.data[self._expectedType]
+ except KeyError:
+ raise Error("action='%s': this log entry has no params
details "
+ "for type %s." % (self.action(), self.type))
+
def logid(self):
return self.data['logid']
@@ -61,7 +83,12 @@
return self.data['ns']
def title(self):
- """Page on which action was performed."""
+ """
+ Page on which action was performed.
+
+ Note: title may be missing in data dict e.g. by oversight action to
+ hide the title. In that case a KeyError exception will raise
+ """
if not hasattr(self, '_title'):
self._title = pywikibot.Page(self.site, self.data['title'])
return self._title
@@ -79,7 +106,8 @@
def timestamp(self):
"""Timestamp object corresponding to event
timestamp."""
if not hasattr(self, '_timestamp'):
- self._timestamp =
pywikibot.Timestamp.fromISOformat(self.data['timestamp'])
+ self._timestamp = pywikibot.Timestamp.fromISOformat(
+ self.data['timestamp'])
return self._timestamp
def comment(self):
@@ -118,27 +146,19 @@
else:
return super(BlockEntry, self).title()
- def isAutoblockRemoval(self):
- return self.isAutoblockRemoval
-
- def _getBlockDetails(self):
- try:
- return self.data['block']
- except KeyError:
- # No 'block' key means this is an unblocking log entry
- if self.action() == 'unblock':
- raise Error("action='unblock': this log entry has no block
details such as flags, duration, or expiry!")
- raise
-
def flags(self):
"""
Return a list of (str) flags associated with the block entry.
It raises an Error if the entry is an unblocking log entry.
+
+ @rtype: list of flag strings
"""
- if hasattr(self, '_flags'):
- return self._flags
- self._flags = self._getBlockDetails()['flags'].split(',')
+ if not hasattr(self, '_flags'):
+ self._flags = self._params['flags']
+ # pre mw 1.19 returned a delimited string.
+ if self._flags and isinstance(self._flags, basestring):
+ self._flags = self._flags.split(',')
return self._flags
def duration(self):
@@ -146,26 +166,27 @@
Return a datetime.timedelta representing the block duration.
@return: datetime.timedelta, or None if block is indefinite.
- @raises Error: the entry is an unblocking log entry.
"""
- if hasattr(self, '_duration'):
- return self._duration
- if self._getBlockDetails()['duration'] == 'indefinite':
- self._duration = None
- else:
- # Doing the difference is easier than parsing the string
- self._duration = self.expiry() - self.timestamp()
+ if not hasattr(self, '_duration'):
+ if self.expiry() is None:
+ self._duration = None
+ else:
+ # Doing the difference is easier than parsing the string
+ self._duration = self.expiry() - self.timestamp()
return self._duration
def expiry(self):
"""
Return a Timestamp representing the block expiry date.
- @raises Error: the entry is an unblocking log entry.
+ @rtype: pywikibot.Timestamp or None
"""
- if hasattr(self, '_expiry'):
- return self._expiry
- self._expiry =
pywikibot.Timestamp.fromISOformat(self._getBlockDetails()['expiry'])
+ if not hasattr(self, '_expiry'):
+ details = self._params.get('expiry')
+ if details:
+ self._expiry = pywikibot.Timestamp.fromISOformat(details)
+ else:
+ self._expiry = None # for infinite blocks
return self._expiry
@@ -181,6 +202,20 @@
"""Rights log entry."""
_expectedType = 'rights'
+
+ @property
+ def oldgroups(self):
+ """Return old rights groups."""
+ if 'old' in self._params: # old mw style
+ return self._params['old'].split(',') if
self._params['old'] else []
+ return self._params['oldgroups']
+
+ @property
+ def newgroups(self):
+ """Return new rights groups."""
+ if 'new' in self._params: # old mw style
+ return self._params['new'].split(',') if
self._params['new'] else []
+ return self._params['newgroups']
class DeleteEntry(LogEntry):
@@ -203,14 +238,38 @@
_expectedType = 'move'
+ @deprecated('target_ns.id')
def new_ns(self):
- return self.data['move']['new_ns']
+ """Return namespace id of target page."""
+ return self.target_ns.id
+ @property
+ def target_ns(self):
+ """Return namespace object of target page."""
+ # key has been changed in mw 1.19
+ return self.site.namespaces[self._params['target_ns']
+ if 'target_ns' in self._params
+ else self._params['new_ns']]
+
+ @deprecated('target_page')
def new_title(self):
"""Return page object of the new title."""
- if not hasattr(self, '_new_title'):
- self._new_title = pywikibot.Page(self.site,
self.data['move']['new_title'])
- return self._new_title
+ return self.target_page
+
+ @property
+ def target_title(self):
+ """Return the target title."""
+ # key has been changed in mw 1.19
+ return (self._params['target_title']
+ if 'target_title' in self._params
+ else self._params['new_title'])
+
+ @property
+ def target_page(self):
+ """Return target page object."""
+ if not hasattr(self, '_target_page'):
+ self._target_page = pywikibot.Page(self.site, self.target_title)
+ return self._target_page
def suppressedredirect(self):
"""
@@ -219,7 +278,7 @@
@rtype: bool
"""
# Introduced in MW r47901
- return 'suppressedredirect' in self.data['move']
+ return 'suppressedredirect' in self._params
class ImportEntry(LogEntry):
@@ -235,6 +294,25 @@
_expectedType = 'patrol'
+ @property
+ def current_id(self):
+ """Return the current id."""
+ # key has been changed in mw 1.19; try the new mw style first
+ return (self._params['curid']
+ if 'curid' in self._params else self._params['cur'])
+
+ @property
+ def previous_id(self):
+ # key has been changed in mw 1.19; try the new mw style first
+ """Return the previous id."""
+ return (self._params['previd']
+ if 'previd' in self._params else self._params['prev'])
+
+ @property
+ def auto(self):
+ """Return auto patrolled."""
+ return 'auto' in self._params and self._params['auto'] != 0
+
class NewUsersEntry(LogEntry):
diff --git a/tests/logentry_tests.py b/tests/logentry_tests.py
new file mode 100644
index 0000000..772b652
--- /dev/null
+++ b/tests/logentry_tests.py
@@ -0,0 +1,133 @@
+# -*- coding: utf-8 -*-
+"""Test logentries module."""
+#
+# (C) Pywikibot team, 2015
+#
+# Distributed under the terms of the MIT license.
+#
+from __future__ import unicode_literals
+
+__version__ = '$Id$'
+
+import datetime
+import sys
+
+import pywikibot
+from pywikibot.logentries import LogEntryFactory
+
+from tests.aspects import (
+ unittest, MetaTestCaseClass, TestCase, DeprecationTestCase
+)
+
+if sys.version_info[0] > 2:
+ unicode = str
+
+
+def get_logentry(site, logtype):
+ """Global method to retriev a single log entry."""
+ return next(iter(site.logevents(logtype=logtype, total=1)))
+
+
+class TestLogentriesMeta(MetaTestCaseClass):
+
+ """Test meta class for TestLogentries."""
+
+ def __new__(cls, name, bases, dct):
+ """Create the new class."""
+ cls.site = pywikibot.Site('de', 'wikipedia')
+
+ def test_method(logtype):
+
+ def test_logevent(self):
+ """Test a single logtype entry."""
+ logentry = get_logentry(cls.site, logtype)
+ self.assertEqual(logtype, logentry._expectedType)
+ self.assertIsInstance(logentry.action(), unicode)
+ self.assertIsInstance(logentry.comment(), unicode)
+ self.assertIsInstance(logentry.logid(), int)
+ self.assertIsInstance(logentry.ns(), int)
+ self.assertIsInstance(logentry.pageid(), int)
+ self.assertIsInstance(logentry.timestamp(), pywikibot.Timestamp)
+ if 'title' in logentry.data: # title may be missing
+ self.assertIsInstance(logentry.title(), pywikibot.Page)
+ self.assertEqual(logentry.type(), logtype)
+ self.assertIsInstance(logentry.user(), unicode)
+ self.assertGreaterEqual(logentry.logid(), 0)
+ self.assertGreaterEqual(logentry.ns(), -2)
+ self.assertGreaterEqual(logentry.pageid(), 0)
+ return test_logevent
+
+ # create test methods for package messages processed by unittest
+ for logtype in LogEntryFactory._logtypes:
+ test_name = str('test_%sEntry' % logtype.title())
+ dct[test_name] = test_method(logtype)
+
+ return super(MetaTestCaseClass, cls).__new__(cls, name, bases, dct)
+
+
+class TestLogentries(TestCase):
+
+ """Test TestLogentries processed by unittest."""
+
+ __metaclass__ = TestLogentriesMeta
+
+
+class TestLogentryParams(TestCase):
+
+ """Test Logentry params."""
+
+ family = 'wikipedia'
+ code = 'de'
+
+ def test_BlockEntry(self):
+ """Test BlockEntry methods."""
+ logentry = get_logentry(self.site, 'block')
+ if logentry.action() == 'block':
+ self.assertIsInstance(logentry.flags(), list)
+ if logentry.expiry() is not None:
+ self.assertIsInstance(logentry.expiry(), pywikibot.Timestamp)
+ self.assertIsInstance(logentry.duration(), datetime.timedelta)
+
+ def test_RightsEntry(self):
+ """Test MoveEntry methods."""
+ logentry = get_logentry(self.site, 'rights')
+ self.assertIsInstance(logentry.oldgroups, list)
+ self.assertIsInstance(logentry.newgroups, list)
+
+ def test_MoveEntry(self):
+ """Test MoveEntry methods."""
+ logentry = get_logentry(self.site, 'move')
+ self.assertIsInstance(logentry.target_ns, pywikibot.site.Namespace)
+ self.assertEqual(logentry.target_page.namespace(),
+ logentry.target_ns.id)
+ self.assertIsInstance(logentry.target_title, unicode)
+ self.assertIsInstance(logentry.target_page, pywikibot.Page)
+ self.assertIsInstance(logentry.suppressedredirect(), bool)
+
+ def test_PatrolEntry(self):
+ """Test MoveEntry methods."""
+ logentry = get_logentry(self.site, 'patrol')
+ self.assertIsInstance(logentry.current_id, int)
+ self.assertIsInstance(logentry.previous_id, int)
+ self.assertIsInstance(logentry.auto, bool)
+
+
+class TestDeprecatedMethods(DeprecationTestCase):
+
+ """Test cases for deprecated logentry methods."""
+
+ family = 'wikipedia'
+ code = 'de'
+
+ def test_MoveEntry(self):
+ """Test MoveEntry methods."""
+ logentry = get_logentry(self.site, 'move')
+ self.assertIsInstance(logentry.new_ns(), int)
+ self.assertEqual(logentry.new_title(), logentry.target_page)
+
+
+if __name__ == '__main__':
+ try:
+ unittest.main()
+ except SystemExit:
+ pass
--
To view, visit
https://gerrit.wikimedia.org/r/211802
To unsubscribe, visit
https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I610540df99fd6f5a77f0cfc8702555c2a082307a
Gerrit-PatchSet: 10
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: John Vandenberg <jayvdb(a)gmail.com>
Gerrit-Reviewer: Ladsgroup <ladsgroup(a)gmail.com>
Gerrit-Reviewer: Merlijn van Deen <valhallasw(a)arctus.nl>
Gerrit-Reviewer: Ricordisamoa <ricordisamoa(a)openmailbox.org>
Gerrit-Reviewer: XZise <CommodoreFabianus(a)gmx.de>
Gerrit-Reviewer: Xqt <info(a)gno.de>
Gerrit-Reviewer: jenkins-bot <>