jenkins-bot submitted this change.

View Change


Approvals: Xqt: Looks good to me, approved jenkins-bot: Verified
[cleanup] remove pre mw 1.27 code in ParamInfo

- deprecate modules_only_mode parameter of ParamInfo
- deprecate ParamInfo.paraminfo_keys
- remove ParamInfo._emulate_pageset method
- simplify normalize_paraminfo and change it to a staticmethod
- combine module_paths with _module_set as suggested
- remove _prefix_submodules and add this one-liner to submodules
- update several tests

Bug: T306637
Change-Id: Iedae9616aa19db5f4a63f1558f4d76e675182b21
---
M tests/paraminfo_tests.py
M pywikibot/data/api/_paraminfo.py
M tests/dry_api_tests.py
M tests/api_tests.py
4 files changed, 249 insertions(+), 427 deletions(-)

diff --git a/pywikibot/data/api/_paraminfo.py b/pywikibot/data/api/_paraminfo.py
index 4112919..35b8e1b 100644
--- a/pywikibot/data/api/_paraminfo.py
+++ b/pywikibot/data/api/_paraminfo.py
@@ -9,7 +9,8 @@

import pywikibot
from pywikibot import config
-from pywikibot.backports import Dict, batched, removeprefix
+from pywikibot.backports import Dict, FrozenSet, Iterable, Set, batched
+from pywikibot.tools import classproperty, deprecated, remove_last_args


__all__ = ['ParamInfo']
@@ -17,35 +18,27 @@

class ParamInfo(Sized, Container):

- """
- API parameter information data object.
+ """API parameter information data object.

Provides cache aware fetching of parameter information.

- It does not support the format modules.
+ .. seealso:: :api:`Parameter information`
"""

- paraminfo_keys = frozenset(['modules', 'querymodules', 'formatmodules',
- 'mainmodule', 'pagesetmodule'])
-
- root_modules = frozenset(['main', 'pageset'])
-
+ root_modules = frozenset(['main'])
init_modules = frozenset(['main', 'paraminfo'])
+ param_modules = ('list', 'meta', 'prop')

- def __init__(
- self,
- site,
- preloaded_modules=None,
- modules_only_mode=None
- ) -> None:
- """
- Initializer.
+ @remove_last_args(['modules_only_mode'])
+ def __init__(self,
+ site,
+ preloaded_modules: Optional[Set[str]] = None) -> None:
+ """Initializer.
+
+ .. deprecated:: 8.4
+ the *modules_only_mode* parameter

:param preloaded_modules: API modules to preload
- :type preloaded_modules: set of string
- :param modules_only_mode: use the 'modules' only syntax for API request
- :type modules_only_mode: bool or None to only use default, which True
- if the site is 1.25wmf4+
"""
self.site = site

@@ -61,21 +54,14 @@
self._modules = {} # filled in _init() (and enlarged in fetch)
self._limit = None

- self.preloaded_modules = self.init_modules
+ self._preloaded_modules = self.init_modules
if preloaded_modules:
- self.preloaded_modules |= set(preloaded_modules)
+ self._preloaded_modules |= set(preloaded_modules)

- self.modules_only_mode = modules_only_mode
- if self.modules_only_mode:
- self.paraminfo_keys = frozenset(['modules'])
-
- def _add_submodules(self, name, modules) -> None:
- """Add the modules to the internal cache or check if equal."""
- # The current implementation here doesn't support submodules inside of
- # submodules, because that would require to fetch all modules when only
- # the names of them were requested
+ def _add_submodules(self, name: str,
+ modules: Union[Set[str], Dict[str, str]]) -> None:
+ """Add the modules to the internal cache."""
assert '+' not in name
- modules = frozenset(modules)
if name == 'main':
# The main module behaves differently as it has no prefix
if self._action_modules:
@@ -83,89 +69,53 @@
else:
self._action_modules = modules
elif name in self._modules:
- assert modules == self._modules[name]
+ # update required to updates from dict and set
+ self._modules[name].update(modules)
else:
self._modules[name] = modules

def _init(self):
assert ('query' in self._modules) is ('main' in self._paraminfo)
+
+ # Skip if ParamInfo is already initialized
if 'query' in self._modules:
return
- mw_ver = self.site.mw_version

- # The paraminfo api deprecated the old request syntax of
- # querymodules='info'; to avoid warnings sites with 1.25wmf4+
- # must only use 'modules' parameter.
- if self.modules_only_mode is None:
- self.modules_only_mode = mw_ver >= '1.25wmf4'
- if self.modules_only_mode:
- self.paraminfo_keys = frozenset(['modules'])
+ # Assume that it will be desirable to prefetch 'query'
+ self._preloaded_modules |= {'query'}

- # Assume that by v1.26, it will be desirable to prefetch 'query'
- self.preloaded_modules |= {'query'}
-
- self._fetch(self.preloaded_modules)
+ self._fetch(self._preloaded_modules)

main_modules_param = self.parameter('main', 'action')
assert main_modules_param
assert 'type' in main_modules_param
assert isinstance(main_modules_param['type'], list)
assert self._action_modules == set(main_modules_param['type'])
-
- # While deprecated with warning in 1.25, paraminfo param 'querymodules'
- # provides a list of all query modules. This will likely be removed
- # from the API in the future, in which case the fallback is the use
- # the same data available in the paraminfo for query.
- query_modules_param = self.parameter('paraminfo', 'querymodules')
-
- if 'limit' not in query_modules_param:
- raise RuntimeError('"limit" not found in query modules')
- self._limit = query_modules_param['limit']
-
- if query_modules_param and 'type' in query_modules_param:
- # 'type' is the list of modules
- self._add_submodules('query', query_modules_param['type'])
-
- if 'query' not in self._modules:
- assert 'query' not in self._paraminfo
- self._fetch({'query'})
assert 'query' in self._modules
-
- def _emulate_pageset(self) -> None:
- """Emulate the pageset module, which existed until MW 1.24."""
- # pageset isn't a module in the new system, so it is emulated, with
- # the paraminfo from the query module.
assert 'query' in self._paraminfo

- self._paraminfo['pageset'] = {
- 'name': 'pageset',
- 'path': 'pageset',
- 'classname': 'ApiPageSet',
- 'prefix': '',
- 'readrights': '',
- 'helpurls': [],
- 'parameters': self._paraminfo['query']['parameters']
- }
+ # Retrieve all query submodules
+ self._limit = 50
+ for param in self.param_modules:
+ query_modules_param = self.parameter('query', param)
+ self._limit = min(query_modules_param['limit'], self._limit)
+ self._add_submodules('query', query_modules_param['submodules'])

@staticmethod
- def _modules_to_set(modules) -> set:
- """Return modules as a set.
-
- :type modules: iterable or str
- """
+ def _modules_to_set(modules: Union[Iterable, str]) -> Set[str]:
+ """Return modules as a set."""
if isinstance(modules, str):
return set(modules.split('|'))
return set(modules)

- def fetch(self, modules) -> None:
- """
- Fetch paraminfo for multiple modules.
+ def fetch(self, modules: Union[Iterable, str]) -> None:
+ """Fetch paraminfo for multiple modules.

- No exception is raised when paraminfo for a module does not exist.
- Use __getitem__ to cause an exception if a module does not exist.
+ No exception is raised when paraminfo for a module does not
+ exist. ``paraminfo[module]`` to cause an exception if a module
+ does not exist.

:param modules: API modules to load
- :type modules: iterable or str
"""
if 'main' not in self._paraminfo:
# The first request should be 'paraminfo', so that
@@ -217,38 +167,12 @@
# subsets, which are unlikely to change. i.e. first request core
# modules which have been a stable part of the API for a long time.
# Also detecting extension based modules may help.
- # Also, when self.modules_only_mode is disabled, both modules and
- # querymodules may each be filled with self._limit items, doubling the
- # number of modules that may be processed in a single batch.
for module_batch in module_generator():
- if self.modules_only_mode and 'pageset' in module_batch:
- pywikibot.debug('paraminfo fetch: removed pageset')
- module_batch.remove('pageset')
- # If this occurred during initialisation,
- # also record it in the preloaded_modules.
- # (at least so tests know an extra load was intentional)
- if 'query' not in self._paraminfo:
- pywikibot.debug('paraminfo batch: added query')
- module_batch.append('query')
- self.preloaded_modules |= {'query'}
-
params = {
'action': 'paraminfo',
+ 'modules': module_batch,
}

- if self.modules_only_mode:
- params['modules'] = module_batch
- else:
- params['modules'] = [mod for mod in module_batch
- if not mod.startswith('query+')
- and mod not in self.root_modules]
- params['querymodules'] = [removeprefix(mod, 'query+')
- for mod in module_batch
- if mod.startswith('query+')]
-
- for mod in set(module_batch) & self.root_modules:
- params[mod + 'module'] = 1
-
# Request need ParamInfo to determine use_get
request = self.site._request(expiry=config.API_config_expiry,
use_get=True,
@@ -277,8 +201,8 @@
normalized_result = {missing_modules[0]: normalized_result}
elif len(module_batch) > 1 and missing_modules:
# Rerequest the missing ones separately
- pywikibot.log('Inconsistency in batch "{}"; rerequest '
- 'separately'.format(missing_modules))
+ pywikibot.log(f'Inconsistency in batch "{missing_modules}";'
+ ' rerequest separately')
failed_modules.extend(missing_modules)

# Remove all modules which weren't requested, we can't be sure that
@@ -291,9 +215,6 @@
for mod in normalized_result.values():
self._generate_submodules(mod['path'])

- if 'pageset' in modules and 'pageset' not in self._paraminfo:
- self._emulate_pageset()
-
def _generate_submodules(self, module) -> None:
"""Check and generate submodules for the given module."""
parameters = self._paraminfo[module].get('parameters', [])
@@ -306,26 +227,27 @@
or 'submodules' not in param:
continue

- for submodule in param['submodules'].values():
+ for child, submodule in param['submodules'].items():
if '+' in submodule:
- parent, child = submodule.rsplit('+', 1)
+ parent = submodule.rsplit('+', 1)[0]
else:
- parent, child = 'main', submodule
+ parent = 'main'
if parent == module:
submodules.add(child)

if submodules:
self._add_submodules(module, submodules)
+
if module == 'query':
- # Previously also modules from generator were used as query
- # modules, but verify that those are just a subset of the
- # prop/list/meta modules. There is no sanity check as this
- # needs to be revisited if query has no generator parameter
+ # Verify that submodules from generator are just a subset of the
+ # prop/list/meta modules.
for param in parameters:
if param['name'] == 'generator':
break
else:
- param = {}
+ raise RuntimeError(
+ "'query' module has no 'generator' parameter")
+
assert param['name'] == 'generator' \
and submodules >= set(param['type'])

@@ -343,8 +265,7 @@
for mod in modules}

def normalize_modules(self, modules) -> set:
- """
- Convert the modules into module paths.
+ """Convert the modules into module paths.

Add query+ to any query module name not also in action modules.

@@ -353,72 +274,42 @@
self._init()
return self._normalize_modules(modules)

- @classmethod
- def normalize_paraminfo(cls, data):
- """
- Convert both old and new API JSON into a new-ish data structure.
+ @staticmethod
+ def normalize_paraminfo(data: Dict[str, Any]) -> Dict[str, Any]:
+ """Convert API JSON into a new data structure with path as key.

For duplicate paths, the value will be False.
+
+ .. versionchanged:: 8.4
+ ``normalize_paraminfo`` became a staticmethod.
"""
result_data = {}
- for paraminfo_key, modules_data in data['paraminfo'].items():
- if not modules_data:
+ modules_data = data['paraminfo'].get('modules', [])
+ for mod_data in modules_data:
+ if 'missing' in mod_data:
continue

- if paraminfo_key[:-len('module')] in cls.root_modules:
- modules_data = [modules_data]
- elif not paraminfo_key.endswith('modules'):
- continue
-
- for mod_data in modules_data:
- if 'missing' in mod_data:
- continue
-
- name = mod_data.get('name')
- php_class = mod_data.get('classname')
-
- if not name and php_class:
- name = removeprefix(php_class, 'Api').lower()
- if name not in ('main', 'pageset'):
- pywikibot.warning(
- f'Unknown paraminfo module "{php_class}"')
- name = '<unknown>:' + php_class
-
- mod_data['name'] = name
-
- if 'path' not in mod_data:
- # query modules often contain 'ApiQuery' and have a suffix.
- # 'ApiQuery' alone is the action 'query'
- if ('querytype' in mod_data
- or php_class and len(php_class) > 8
- and 'ApiQuery' in php_class):
- mod_data['path'] = 'query+' + name
- else:
- mod_data['path'] = name
-
- path = mod_data['path']
-
- if path in result_data:
- # Only warn first time
- if result_data[path] is not False:
- pywikibot.warning(f'Path "{path}" is ambiguous.')
- else:
- pywikibot.log(f'Found another path "{path}"')
- result_data[path] = False
- else:
- result_data[path] = mod_data
+ path = mod_data['path']
+ if path not in result_data:
+ result_data[path] = mod_data
+ elif result_data[path] is not False:
+ # Only warn first time
+ result_data[path] = False
+ pywikibot.warning(f'Path "{path}" is ambiguous.')
+ else:
+ pywikibot.log(f'Found another path "{path}"')

return result_data

def __getitem__(self, key):
- """
- Return a paraminfo module for the module path, caching it.
+ """Return a paraminfo module for the module path, caching it.

- Use the module path, such as 'query+x', to obtain the paraminfo for
- submodule 'x' in the query module.
+ Use the module path, such as 'query+x', to obtain the paraminfo
+ for submodule 'x' in the query module.

- If the key does not include a '+' and is not present in the top level
- of the API, it will fallback to looking for the key 'query+x'.
+ If the key does not include a '+' and is not present in the top
+ level of the API, it will fallback to looking for the key
+ 'query+x'.
"""
self.fetch({key})
if key in self._paraminfo:
@@ -444,8 +335,7 @@
module: str,
param_name: str
) -> Optional[Dict[str, Any]]:
- """
- Get details about one modules parameter.
+ """Get details about one modules parameter.

Returns None if the parameter does not exist.

@@ -453,13 +343,6 @@
:param param_name: parameter name in the module
:return: metadata that describes how the parameter may be used
"""
- # TODO: the 'description' field of each parameter is not in the default
- # output of v1.25, and can't removed from previous API versions.
- # There should be an option to remove this verbose data from the cached
- # version, for earlier versions of the API, and/or extract any useful
- # data and discard the entire received paraminfo structure. There are
- # also params which are common to many modules, such as those provided
- # by the ApiPageSet php class: titles, pageids, redirects, etc.
try:
module = self[module]
except KeyError:
@@ -471,8 +354,7 @@
pywikibot.warning(f"module '{module}' has no parameters")
return None

- param_data = [param for param in params
- if param['name'] == param_name]
+ param_data = [param for param in params if param['name'] == param_name]

if not param_data:
return None
@@ -485,17 +367,12 @@
@property
def module_paths(self):
"""Set of all modules using their paths."""
- return self._module_set(True)
-
- # As soon as modules() is removed, module_paths and _module_set can be
- # combined, so don't add any code between these two methods.
- def _module_set(self, path):
# Load the submodules of all action modules available
self.fetch(self.action_modules)
modules = set(self.action_modules)
for parent_module in self._modules:
- submodules = self.submodules(parent_module, path)
- assert not submodules & modules or not path
+ submodules = self.submodules(parent_module, path=True)
+ assert not submodules & modules
modules |= submodules
return modules

@@ -510,9 +387,8 @@
"""Set of all query module names without query+ path prefix."""
return self.submodules('query')

- def submodules(self, name: str, path: bool = False) -> set:
- """
- Set of all submodules.
+ def submodules(self, name: str, path: bool = False) -> Set[str]:
+ """Set of all submodules.

:param name: The name of the parent module.
:param path: Whether the path and not the name is returned.
@@ -522,39 +398,35 @@
self.fetch([name])
submodules = self._modules[name]
if path:
- submodules = self._prefix_submodules(submodules, name)
+ # prefix submodules
+ submodules = {f'{name}+{mod}' for mod in submodules}
return submodules

- @staticmethod
- def _prefix_submodules(modules, prefix):
- """Prefix submodules with path."""
- return {f'{prefix}+{mod}' for mod in modules}
-
@property
- def prefix_map(self):
- """
- Mapping of module to its prefix for all modules with a prefix.
+ def prefix_map(self) -> Dict[str, str]:
+ """Mapping of module to its prefix for all modules with a prefix.

This loads paraminfo for all modules.
"""
if not self._prefix_map:
- self._prefix_map = {module: prefix
- for module, prefix
- in self.attributes('prefix').items()
- if prefix}
+ self._prefix_map = {
+ module: prefix
+ for module, prefix in self.attributes('prefix').items()
+ if prefix
+ }
return self._prefix_map.copy()

- def attributes(self, attribute: str, modules: Optional[set] = None):
- """
- Mapping of modules with an attribute to the attribute value.
+ def attributes(self, attribute: str,
+ modules: Optional[set] = None) -> Dict[str, Any]:
+ """Mapping of modules with an attribute to the attribute value.

- It will include all modules which have that attribute set, also if that
- attribute is empty or set to False.
+ It will include all modules which have that attribute set, also
+ if that attribute is empty or set to False.

:param attribute: attribute name
- :param modules: modules to include. If None (default), it'll load all
- modules including all submodules using the paths.
- :rtype: dict using modules as keys
+ :param modules: modules to include. If None (default), it'll
+ load all modules including all submodules using the paths.
+ :return: dict using modules as keys
"""
if modules is None:
modules = self.module_paths
@@ -562,3 +434,21 @@

return {mod: self[mod][attribute]
for mod in modules if attribute in self[mod]}
+
+ @classproperty
+ @deprecated(since='8.4.0')
+ def paraminfo_keys(cls) -> FrozenSet[str]:
+ """Return module types.
+
+ .. deprecated:: 8.4.0
+ """
+ return frozenset(['modules'])
+
+ @property
+ @deprecated(since='8.4.0')
+ def preloaded_modules(self) -> Union[FrozenSet[str], Set[str]]:
+ """Return set of preloaded modules.
+
+ .. deprecated:: 8.4.0
+ """
+ return self._preloaded_modules
diff --git a/tests/api_tests.py b/tests/api_tests.py
index edbae80..ea03a0e 100755
--- a/tests/api_tests.py
+++ b/tests/api_tests.py
@@ -18,11 +18,7 @@
from pywikibot.exceptions import APIError, NoUsernameError
from pywikibot.throttle import Throttle
from pywikibot.tools import suppress_warnings
-from tests.aspects import (
- DefaultDrySiteTestCase,
- DefaultSiteTestCase,
- TestCase,
-)
+from tests.aspects import DefaultDrySiteTestCase, DefaultSiteTestCase, TestCase
from tests.utils import FakeLoginManager


@@ -89,7 +85,7 @@

self.assertIn('main', pi._paraminfo)
self.assertIn('paraminfo', pi._paraminfo)
- self.assertLength(pi, pi.preloaded_modules)
+ self.assertLength(pi, pi._preloaded_modules)

self.assertIn('info', pi.query_modules)
self.assertIn('login', pi._action_modules)
@@ -103,7 +99,7 @@
self.assertIn('query', pi._paraminfo)

def test_init_pageset(self):
- """Test initializing with only the pageset."""
+ """Test initializing with deprecated pageset."""
site = self.get_site()
self.assertNotIn('query', api.ParamInfo.init_modules)
pi = api.ParamInfo(site, {'pageset'})
@@ -113,36 +109,30 @@

self.assertIn('main', pi._paraminfo)
self.assertIn('paraminfo', pi._paraminfo)
- self.assertIn('pageset', pi._paraminfo)
-
- if 'query' in pi.preloaded_modules:
- self.assertIn('query', pi._paraminfo)
- self.assertLength(pi, 4)
- else:
- self.assertNotIn('query', pi._paraminfo)
- self.assertLength(pi, 3)
-
- self.assertLength(pi, pi.preloaded_modules)
-
- generators_param = pi.parameter('pageset', 'generator')
- self.assertGreater(len(generators_param['type']), 1)
+ self.assertNotIn('pageset', pi._paraminfo)
+ self.assertIn('query', pi._paraminfo)
+ self.assertLength(pi, 3)
+ self.assertLength(pi._preloaded_modules, 4)
+ with self.assertRaisesRegex(ValueError,
+ "paraminfo for 'pageset' not loaded"):
+ pi.parameter('pageset', 'generator')

def test_generators(self):
"""Test requesting the generator parameter."""
site = self.get_site()
- pi = api.ParamInfo(site, {'pageset', 'query'})
+ pi = api.ParamInfo(site, {'query'})
self.assertIsEmpty(pi)
pi._init()

self.assertIn('main', pi._paraminfo)
self.assertIn('paraminfo', pi._paraminfo)
- self.assertIn('pageset', pi._paraminfo)
self.assertIn('query', pi._paraminfo)

- pageset_generators_param = pi.parameter('pageset', 'generator')
query_generators_param = pi.parameter('query', 'generator')
-
- self.assertEqual(pageset_generators_param, query_generators_param)
+ self.assertIn('submodules', query_generators_param)
+ self.assertEqual(query_generators_param['submoduleparamprefix'], 'g')
+ for submodule, query in query_generators_param['submodules'].items():
+ self.assertEqual('query+' + submodule, query)

def test_with_module_info(self):
"""Test requesting the module info."""
@@ -154,7 +144,7 @@

self.assertIn('main', pi._paraminfo)
self.assertIn('paraminfo', pi._paraminfo)
- self.assertLength(pi, 1 + len(pi.preloaded_modules))
+ self.assertLength(pi, 1 + len(pi._preloaded_modules))

self.assertEqual(pi['info']['prefix'], 'in')

@@ -178,7 +168,7 @@

self.assertIn('main', pi._paraminfo)
self.assertIn('paraminfo', pi._paraminfo)
- self.assertLength(pi, 1 + len(pi.preloaded_modules))
+ self.assertLength(pi, 1 + len(pi._preloaded_modules))

self.assertEqual(pi['revisions']['prefix'], 'rv')

@@ -204,7 +194,7 @@
self.assertIn('main', pi._paraminfo)
self.assertIn('paraminfo', pi._paraminfo)

- self.assertLength(pi, 2 + len(pi.preloaded_modules))
+ self.assertLength(pi, 2 + len(pi._preloaded_modules))

def test_with_invalid_module(self):
"""Test requesting different kind of invalid modules."""
@@ -225,7 +215,7 @@
self.assertIn('main', pi._paraminfo)
self.assertIn('paraminfo', pi._paraminfo)

- self.assertLength(pi, pi.preloaded_modules)
+ self.assertLength(pi, pi._preloaded_modules)

def test_submodules(self):
"""Test another module apart from query having submodules."""
@@ -233,7 +223,7 @@
self.assertFalse(pi._modules)
pi.fetch(['query'])
self.assertIn('query', pi._modules)
- self.assertIsInstance(pi._modules['query'], frozenset)
+ self.assertIsInstance(pi._modules['query'], set)
self.assertIn('revisions', pi._modules['query'])
self.assertEqual(pi.submodules('query'), pi.query_modules)
for mod in pi.submodules('query', True):
@@ -244,7 +234,7 @@
with patch.object(pywikibot, 'warning') as w, \
self.assertRaises(KeyError):
pi.__getitem__('query+foobar')
- # The warning message may be different with older MW versions.
+
self.assertIn('API warning (paraminfo): ', w.call_args[0][0])

with self.assertRaises(KeyError):
@@ -277,33 +267,6 @@
self.assertEqual(mod, pi[mod]['path'])
self.assertEqual(value, '')

- @patch.object(pywikibot, 'warning') # ignore several warnings
- def test_old_mode(self, *args):
- """Test the old mode explicitly."""
- site = self.get_site()
- pi = api.ParamInfo(site, modules_only_mode=False)
- pi.fetch(['info'])
- self.assertIn('query+info', pi._paraminfo)
-
- self.assertIn('main', pi._paraminfo)
- self.assertIn('paraminfo', pi._paraminfo)
-
- self.assertLength(pi, 1 + len(pi.preloaded_modules))
-
- self.assertIn('query+revisions', pi.prefix_map)
-
- def test_new_mode(self):
- """Test the new modules-only mode explicitly."""
- site = self.get_site()
- pi = api.ParamInfo(site, modules_only_mode=True)
- pi.fetch(['info'])
- self.assertIn('query+info', pi._paraminfo)
- self.assertIn('main', pi._paraminfo)
- self.assertIn('paraminfo', pi._paraminfo)
-
- self.assertLength(pi, 1 + len(pi.preloaded_modules))
- self.assertIn('query+revisions', pi.prefix_map)
-

class TestOtherSubmodule(TestCase):

@@ -322,7 +285,7 @@
self.assertIn('flow', pi._modules)
other_modules = set()
for modules in pi._modules.values():
- self.assertIsInstance(modules, frozenset)
+ self.assertIsInstance(modules, set)
other_modules |= modules

other_modules -= pi.action_modules
diff --git a/tests/dry_api_tests.py b/tests/dry_api_tests.py
index dd0ec6e..7ad0dd3 100755
--- a/tests/dry_api_tests.py
+++ b/tests/dry_api_tests.py
@@ -312,62 +312,72 @@

"""Test extracting data from the ParamInfo."""

- prop_info_param_data = { # data from 1.25
- 'name': 'info',
- 'classname': 'ApiQueryInfo',
- 'path': 'query+info',
- 'group': 'prop',
- 'prefix': 'in',
- 'parameters': [
- {
- 'name': 'prop',
- 'multi': '',
- 'limit': 500,
- 'lowlimit': 50,
- 'highlimit': 500,
- 'type': [
- 'protection',
- 'talkid',
- 'watched',
- 'watchers',
- 'notificationtimestamp',
- 'subjectid',
- 'url',
- 'readable',
- 'preload',
- 'displaytitle'
- ]
- },
- {
- 'name': 'token',
- 'deprecated': '',
- 'multi': '',
- 'limit': 500,
- 'lowlimit': 50,
- 'highlimit': 500,
- 'type': [
- 'edit',
- 'delete',
- 'protect',
- 'move',
- 'block',
- 'unblock',
- 'email',
- 'import',
- 'watch'
- ]
- },
- {
- 'name': 'continue',
- 'type': 'string'
- }
- ],
- 'querytype': 'prop'
- }
-
- edit_action_param_data = {
- 'name': 'edit',
- 'path': 'edit'
+ # https://en.wikipedia.org/w/api.php?action=paraminfo&modules=query+info|query+tokens
+ paraminfodata = {
+ 'paraminfo': {
+ 'modules': [
+ {
+ 'name': 'info',
+ 'classname': 'ApiQueryInfo',
+ 'path': 'query+info',
+ 'group': 'prop',
+ 'prefix': 'in',
+ 'parameters': [
+ {
+ 'index': 1,
+ 'name': 'prop',
+ 'type': [
+ 'displaytitle'
+ 'notificationtimestamp',
+ 'protection',
+ 'subjectid',
+ 'talkid',
+ 'url',
+ 'watched',
+ 'watchers',
+ 'preload',
+ 'readable',
+ ],
+ 'multi': '',
+ 'lowlimit': 50,
+ 'highlimit': 500,
+ 'limit': 50,
+ 'deprecatedvalues': [
+ 'preload',
+ 'readable'
+ ]
+ }
+ ]
+ },
+ {
+ 'name': 'tokens',
+ 'classname': 'ApiQueryTokens',
+ 'path': 'query+tokens',
+ 'group': 'meta',
+ 'prefix': '',
+ 'parameters': [
+ {
+ 'index': 1,
+ 'name': 'type',
+ 'type': [
+ 'csrf',
+ 'deleteglobalaccount',
+ 'login',
+ 'patrol',
+ 'rollback',
+ 'userrights',
+ 'watch'
+ ],
+ 'default': 'csrf',
+ 'multi': '',
+ 'limit': 50,
+ 'lowlimit': 50,
+ 'highlimit': 500
+ }
+ ]
+ }
+ ]
+ }
}

def setUp(self):
@@ -380,86 +390,43 @@
site._paraminfo._paraminfo[mod] = {}
site._paraminfo._action_modules = frozenset(['edit'])
site._paraminfo._modules = {'query': frozenset(['info'])}
+ data = site._paraminfo.normalize_paraminfo(self.paraminfodata)
+ site._paraminfo._paraminfo.update(data)

- def test_new_format(self):
+ def test_format(self):
"""Test using a dummy formatted in the new modules-only mode."""
pi = self.get_site()._paraminfo
- # Set it to the new limited set of keys.
- pi.paraminfo_keys = frozenset(['modules'])
-
- data = pi.normalize_paraminfo({
- 'paraminfo': {
- 'modules': [
- self.prop_info_param_data,
- self.edit_action_param_data,
- ]
- }
- })
-
- pi._paraminfo.update(data)
- self.assertIn('edit', pi._paraminfo)
self.assertIn('query+info', pi._paraminfo)
+ self.assertIn('query+tokens', pi._paraminfo)
self.assertIn('edit', pi)
self.assertIn('info', pi)
-
- def test_old_format(self):
- """Test using a dummy formatted in the old mode."""
- pi = self.get_site()._paraminfo
- # Reset it to the complete set of possible keys defined in the class
- pi.paraminfo_keys = ParamInfo.paraminfo_keys
-
- data = pi.normalize_paraminfo({
- 'paraminfo': {
- 'querymodules': [self.prop_info_param_data],
- 'modules': [self.edit_action_param_data],
- }
- })
-
- pi._paraminfo.update(data)
- self.assertIn('edit', pi._paraminfo)
- self.assertIn('query+info', pi._paraminfo)
- self.assertIn('edit', pi)
- self.assertIn('info', pi)
+ self.assertIn('tokens', pi)

def test_attribute(self):
"""Test using __getitem__."""
pi = self.get_site()._paraminfo
- # Reset it to the complete set of possible keys defined in the class
- pi.paraminfo_keys = ParamInfo.paraminfo_keys
+ self.assertEqual(pi._paraminfo['query+info']['group'], 'prop')
+ self.assertEqual(pi['query+info']['prefix'], 'in')

- data = pi.normalize_paraminfo({
- 'paraminfo': {
- 'querymodules': [self.prop_info_param_data],
- }
- })
-
- pi._paraminfo.update(data)
-
- self.assertEqual(pi._paraminfo['query+info']['querytype'], 'prop')
- self.assertEqual(pi['info']['prefix'], 'in')
-
- def test_parameter(self):
- """Test parameter() method."""
+ def test_info_parameter(self):
+ """Test parameter() method with 'info' module."""
pi = self.get_site()._paraminfo
- # Reset it to the complete set of possible keys defined in the class
- pi.paraminfo_keys = ParamInfo.paraminfo_keys
-
- data = pi.normalize_paraminfo({
- 'paraminfo': {
- 'querymodules': [self.prop_info_param_data],
- }
- })
-
- pi._paraminfo.update(data)
-
- param = pi.parameter('info', 'token')
+ param = pi.parameter('info', 'prop')
self.assertIsInstance(param, dict)
-
- self.assertEqual(param['name'], 'token')
- self.assertIn('deprecated', param)
-
+ self.assertEqual(param['name'], 'prop')
+ self.assertIn('deprecatedvalues', param)
self.assertIsInstance(param['type'], list)
- self.assertIn('email', param['type'])
+ self.assertIn('preload', param['type'])
+
+ def test_tokens_parameter(self):
+ """Test parameter() method with 'tokens' module."""
+ pi = self.get_site()._paraminfo
+ param = pi.parameter('tokens', 'type')
+ self.assertIsInstance(param, dict)
+ self.assertEqual(param['name'], 'type')
+ self.assertIn('default', param)
+ self.assertIsInstance(param['type'], list)
+ self.assertIn('login', param['type'])


class QueryGenTests(DefaultDrySiteTestCase):
diff --git a/tests/paraminfo_tests.py b/tests/paraminfo_tests.py
index f5877f4..608b2ec 100755
--- a/tests/paraminfo_tests.py
+++ b/tests/paraminfo_tests.py
@@ -83,23 +83,14 @@

def test_watchlist_show_flags(self):
"""Test watchlist show flags."""
- types = ['minor', 'bot', 'anon', 'patrolled']
- if self.site.mw_version >= '1.24':
- types.append('unread')
-
+ types = ['minor', 'bot', 'anon', 'patrolled', 'unread']
known = types + [f'!{item}' for item in types]

self._check_param_subset(self.site, 'query+watchlist', 'show', known)

def test_watchlist_type(self):
"""Test watchlist type."""
- known = ['edit', 'external', 'new', 'log']
-
- mw_ver = self.site.mw_version
-
- if mw_ver.version >= (1, 27) \
- and (mw_ver >= '1.27.0-wmf.4' or mw_ver.suffix == 'alpha'):
- known.append('categorize')
+ known = ['categorize', 'edit', 'external', 'log', 'new']

self._check_param_values(self.site, 'query+watchlist', 'type', known)

@@ -148,20 +139,13 @@

def test_content_model(self):
"""Test content model."""
- base = [
- 'wikitext',
- 'javascript',
- 'css',
- 'text',
- ]
+ base = ['css', 'javascript', 'json', 'text', 'wikitext']
wmf = [
'MassMessageListContent',
'SecurePoll',
'Scribunto',
'JsonSchema',
]
- if self.site.mw_version >= '1.24':
- base.append('json')

self._check_param_subset(self.site, 'edit', 'contentmodel', base)
self._check_param_subset(self.site, 'parse', 'contentmodel', base)

To view, visit change 845037. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Change-Id: Iedae9616aa19db5f4a63f1558f4d76e675182b21
Gerrit-Change-Number: 845037
Gerrit-PatchSet: 9
Gerrit-Owner: Xqt <info@gno.de>
Gerrit-Reviewer: Xqt <info@gno.de>
Gerrit-Reviewer: jenkins-bot
Gerrit-MessageType: merged