jenkins-bot has submitted this change and it was merged.

Change subject: Make arguments consistent across .protect() methods
......................................................................


Make arguments consistent across .protect() methods

Page.protect():
  - use a single 'protections' argument as in APISite.protect()
    although it defaults to 'None' it should be supplied.
  - use **kwargs to pass 'expiry' and 'cascade' to APISite.protect()
  - deprecated the 'edit', 'move', 'create' and 'upload' arguments
    they will be translated into 'protections' but ONLY if it was
    set to None (which is the case in exisiting scripts).
  - deprecated the 'unprotect' argument (not present in the API)

APISite.protect():
  - rename the 'summary' argument into 'reason' for consistency
    with the Page method and the API
  - use **kwargs to pass 'cascade' directly to Request()

Logic of 'edit', 'move', 'create' and 'upload':
  - If protections is not None, it will warn for each value which
    is not None and ignores them
  - If protections is None, it'll create protections dict from them.
    It will also warn for each value which is not None.
  - 'edit' and 'move' are now default to False, so it warns only
    if they are set explicitly in conjunction with an existing
    protections dict. It'll also warn when they are False and
    protections is None.

APISite:
  - .protection_types(): return a set of available protection types
  - .protection_levels(): return a set of available protection levels
  - ._add_siteinfo(): queries additional siteinfo
  - .getmagicwords(): uses _add_siteinfo()

Page.applicable_protections():
  - return a set of protections applicable to that page, but this
    is mostly hardcoded. But a future api.php update might fix it.

Change-Id: Ifc20770b7bbae8c86b920f8376e27d0bff47d2a3
---
M pywikibot/page.py
M pywikibot/site.py
2 files changed, 162 insertions(+), 56 deletions(-)

Approvals:
  John Vandenberg: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/pywikibot/page.py b/pywikibot/page.py
index cf6f367..1450642 100644
--- a/pywikibot/page.py
+++ b/pywikibot/page.py
@@ -824,6 +824,30 @@
         """
         return self.site.page_restrictions(self)
 
+    def applicable_protections(self):
+        """
+        Return the protection types allowed for that page.
+
+        If the page doesn't exists it only returns "create". Otherwise it
+        returns all protection types provided by the site, except "create".
+        It also removes "upload" if that page is not in the File namespace.
+
+        It is possible, that it returns an empty set, but only if original
+        protection types were removed.
+
+        @return: set of unicode
+        """
+        # Currently hard coded, but a future API update might allow us to
+        # properly determine the applicable protection types
+        p_types = set(self.site.protection_types())
+        if not self.exists():
+            return set(['create']) if 'create' in p_types else set()
+        else:
+            p_types.remove('create')  # no existing page allows that
+            if not self.isImage():  # only file pages allow upload
+                p_types.remove('upload')
+            return p_types
+
     def canBeEdited(self):
         """Determine whether the page may be edited.
 
@@ -1557,60 +1581,76 @@
         return self.site.undelete(self, comment)
 
     @deprecate_arg("throttle", None)
-    def protect(self, edit='sysop', move='sysop', create=None, upload=None,
-                unprotect=False, reason=None, prompt=True, expiry=None):
+    def protect(self, edit=False, move=False, create=None, upload=None,
+                unprotect=False, reason=None, prompt=True, protections=None,
+                **kwargs):
         """(Un)protect a wiki page. Requires administrator status.
 
         Valid protection levels (in MediaWiki 1.12) are '' (equivalent to
         'none'), 'autoconfirmed', and 'sysop'. If None is given, however,
         that protection will be skipped.
 
-        @param edit: Level of edit protection
-        @param move: Level of move protection
-        @param create: Level of create protection
-        @param upload: Level of upload protection
-        @param unprotect: If true, unprotect page editing and moving
-            (equivalent to set both edit and move to '')
-        @param reason: Edit summary.
-        @param prompt: If true, ask user for confirmation.
-        @param expiry: When the block should expire. This expiry will be
-            applied to all protections.
-            None, 'infinite', 'indefinite', 'never', and '' mean no expiry.
-        @type expiry: pywikibot.Timestamp, string in GNU timestamp format
-            (including ISO 8601).
+        @param protections: A dict mapping type of protection to protection
+            level of that type.
+        @type  protections: dict
+        @param reason: Reason for the action
+        @type  reason: basestring
+        @param prompt: Whether to ask user for confirmation
+        @type  prompt: bool
         """
-        if reason is None:
-            if unprotect:
-                un = u'un'
+        def deprecated(value, arg_name):
+            # if protections was set and value is None, don't interpret that
+            # argument. But otherwise warn that the parameter was set
+            # (even implicit)
+            if called_using_deprecated_arg:
+                if value is False:  # explicit test for False (don't use not)
+                    value = "sysop"
+                if value == "none":  # 'none' doesn't seem do be accepted
+                    value = ""
+                if value is not None:  # empty string is allowed
+                    protections[arg_name] = value
+                    pywikibot.bot.warning(u'"protections" argument of '
+                                          'protect() replaces 
"{}".'.format(arg_name))
             else:
-                un = u''
-            pywikibot.output(u'Preparing to %sprotect %s.'
-                             % (un, self.title(asLink=True)))
+                if value:
+                    pywikibot.bot.warning(u'"protections" argument of '
+                                          'protect() replaces "{}"; cannot '
+                                          'use both.'.format(arg_name))
+
+        # buffer that, because it might get changed
+        called_using_deprecated_arg = protections is None
+        if called_using_deprecated_arg:
+            protections = {}
+        deprecated(edit, "edit")
+        deprecated(move, "move")
+        deprecated(create, "create")
+        deprecated(upload, "upload")
+
+        if reason is None:
+            pywikibot.output(u'Preparing to protection change of %s.'
+                             % (self.title(asLink=True)))
             reason = pywikibot.input(u'Please enter a reason for the action:')
         if unprotect:
-            edit = move = ""
-            # Apply to only edit and move for backward compatibility.
-            # To unprotect article creation, for example,
-            # create must be set to '' and the rest must be None
+            pywikibot.bot.warning(u'"unprotect" argument of protect() is '
+                                  'deprecated')
+            protections = dict(
+                [(p_type, "") for p_type in self.applicable_protections()])
         answer = 'y'
+        if prompt:
+            pywikibot.bot.warning(u'"prompt" argument of protect() is '
+                                  'deprecated')
         if prompt and not hasattr(self.site, '_noProtectPrompt'):
             answer = pywikibot.inputChoice(
                 u'Do you want to change the protection level of %s?'
                 % self.title(asLink=True, forceInterwiki=True),
                 ['Yes', 'No', 'All'],
-                ['Y', 'N', 'A'],
+                ['y', 'N', 'a'],
                 'N')
-            if answer in ['a', 'A']:
+            if answer == 'a':
                 answer = 'y'
                 self.site._noProtectPrompt = True
-        if answer in ['y', 'Y']:
-            protections = {
-                'edit': edit,
-                'move': move,
-                'create': create,
-                'upload': upload,
-            }
-            return self.site.protect(self, protections, reason, expiry)
+        if answer == 'y':
+            return self.site.protect(self, protections, reason, **kwargs)
 
     def change_category(self, oldCat, newCat, comment=None, sortKey=None,
                         inPlace=True):
diff --git a/pywikibot/site.py b/pywikibot/site.py
index b70ad2d..25bfa07 100644
--- a/pywikibot/site.py
+++ b/pywikibot/site.py
@@ -1126,23 +1126,11 @@
     def getmagicwords(self, word):
         """Return list of localized "word" magic words for the site."""
         if not hasattr(self, "_magicwords"):
-            sirequest = api.CachedRequest(
-                expiry=config.API_config_expiry,
-                site=self,
-                action="query",
-                meta="siteinfo",
-                siprop="magicwords"
-            )
             try:
-                sidata = sirequest.submit()
-                assert 'query' in sidata, \
-                       "API siteinfo response lacks 'query' key"
-                sidata = sidata['query']
-                assert 'magicwords' in sidata, \
-                       "API siteinfo response lacks 'magicwords' key"
+                # don't cache in _siteinfo, because we cache it in _magicwords
+                magicwords = self._add_siteinfo("magicwords", False)
                 self._magicwords = dict((item["name"], item["aliases"])
-                                        for item in sidata["magicwords"])
-
+                                        for item in magicwords)
             except api.APIError:
                 # hack for older sites that don't support 1.13 properties
                 # probably should delete if we're not going to support pre-1.13
@@ -1186,6 +1174,44 @@
     def pagename2codes(self, default=True):
         """Return list of localized PAGENAMEE tags for the site."""
         return self.getmagicwords("pagenamee")
+
+    def _add_siteinfo(self, prop, cache, force=False):
+        """
+        Retrieve additional siteinfo and optionally cache it.
+
+        Queries the site and returns the properties. It can cache the value
+        so that future queries will access the cache. With C{force} set to
+        True it won't access the cache but it can still cache the value. If
+        the property doesn't exists it returns None.
+
+        @param prop: The property name of the siteinfo.
+        @type prop: str
+        @param cache: Should this be cached?
+        @type cache: bool
+        @param force: Should the cache be skipped?
+        @type force: bool
+        @return: The properties of the site.
+        @rtype: various (depends on prop)
+        """
+        if not hasattr(self, '_siteinfo'):
+            force = True  # if it doesn't exists there won't be a cache
+            if cache:  # but only initialise cache if that is requested
+                self._getsiteinfo()
+        if not force and prop in self._siteinfo:
+            return self._siteinfo[prop]
+        data = pywikibot.data.api.CachedRequest(
+            expiry=0 if force else pywikibot.config.API_config_expiry,
+            site=self,
+            action='query',
+            meta='siteinfo',
+            siprop=prop).submit()
+        try:
+            prop_data = data['query'][prop]
+        except KeyError:
+            prop_data = None
+        if cache:
+            self._siteinfo[prop] = prop_data
+        return prop_data
 
     def _getsiteinfo(self, force=False):
         """Retrieve siteinfo and namespaces from site."""
@@ -3202,8 +3228,45 @@
         "protect-invalidlevel": "Invalid protection level"
     }
 
+    def protection_types(self):
+        """
+        Return the protection types available on this site.
+
+        With MediaWiki version 1.23 protection types can be retrieved. To
+        support older wikis, the default protection types 'create', 'edit',
+        'move' and 'upload' are returned.
+
+        @return protection types available
+        @rtype: set of unicode instances
+        """
+        # implemented in b73b5883d486db0e9278ef16733551f28d9e096d
+        restrictions = self._add_siteinfo('restrictions', True)
+        if restrictions is None or 'types' not in restrictions:
+            return set([u'create', u'edit', u'move', u'upload'])
+        else:
+            return set(restrictions['types'])
+
+    def protection_levels(self):
+        """
+        Return the protection levels available on this site.
+
+        With MediaWiki version 1.23 protection levels can be retrieved. To
+        support older wikis, the default protection levels '', 'autoconfirmed',
+        and 'sysop' are returned.
+
+        @return protection types available
+        @rtype: set of unicode instances
+        """
+        # implemented in b73b5883d486db0e9278ef16733551f28d9e096d
+        restrictions = self._add_siteinfo('restrictions', True)
+        if restrictions is None or 'levels' not in restrictions:
+            return set([u'', u'autoconfirmed', u'sysop'])
+        else:
+            return set(restrictions['levels'])
+
     @must_be(group='sysop')
-    def protect(self, page, protections, summary, expiry=None):
+    @deprecate_arg("summary", "reason")
+    def protect(self, page, protections, reason, expiry=None, **kwargs):
         """(Un)protect a wiki page. Requires administrator status.
 
         @param protections: A dict mapping type of protection to protection
@@ -3211,22 +3274,25 @@
             'create', and 'upload'. Valid protection levels (in MediaWiki 1.12)
             are '' (equivalent to 'none'), 'autoconfirmed', and 'sysop'.
             If None is given, however, that protection will be skipped.
-        @param summary: Edit summary.
+        @type  protections: dict
+        @param reason: Reason for the action
+        @type  reason: basestring
         @param expiry: When the block should expire. This expiry will be 
applied
             to all protections. If None, 'infinite', 'indefinite', 'never', or 
''
             is given, there is no expiry.
         @type expiry: pywikibot.Timestamp, string in GNU timestamp format
             (including ISO 8601).
         """
-        token = self.token(page, "protect")
+        token = self.token(page, 'protect')
         self.lock_page(page)
 
-        protectList = [type + '=' + level for type, level in 
protections.items()
+        protectList = [ptype + '=' + level for ptype, level in 
protections.items()
                        if level is not None]
-        req = api.Request(site=self, action="protect", token=token,
+        req = api.Request(site=self, action='protect', token=token,
                           title=page.title(withSection=False),
                           protections=protectList,
-                          reason=summary)
+                          reason=reason,
+                          **kwargs)
         if isinstance(expiry, pywikibot.Timestamp):
             expiry = expiry.toISOformat()
         if expiry:

-- 
To view, visit https://gerrit.wikimedia.org/r/152187
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Ifc20770b7bbae8c86b920f8376e27d0bff47d2a3
Gerrit-PatchSet: 8
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: Ricordisamoa <[email protected]>
Gerrit-Reviewer: John Vandenberg <[email protected]>
Gerrit-Reviewer: Ladsgroup <[email protected]>
Gerrit-Reviewer: Legoktm <[email protected]>
Gerrit-Reviewer: Merlijn van Deen <[email protected]>
Gerrit-Reviewer: Mpaa <[email protected]>
Gerrit-Reviewer: Nullzero <[email protected]>
Gerrit-Reviewer: Ricordisamoa <[email protected]>
Gerrit-Reviewer: Russell Blau <[email protected]>
Gerrit-Reviewer: XZise <[email protected]>
Gerrit-Reviewer: Xqt <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
Pywikibot-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/pywikibot-commits

Reply via email to