Source code for pywikibot.site._tokenwallet
#
# (C) Pywikibot team, 2008-2026
#
# Distributed under the terms of the MIT license.
#
"""Objects representing api tokens."""
from __future__ import annotations
from collections.abc import Container
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from pywikibot.site import APISite
[docs]
class TokenWallet(Container):
"""Container for tokens.
You should not use this container class directly; use
:attr:`APISite.tokens<pywikibot.site._apisite.APISite.tokens>`
instead which gives access to the site's TokenWallet instance.
"""
def __init__(self, site: APISite) -> None:
"""Initializer."""
self.site: APISite = site
self._tokens: dict[str, str] = {}
self._currentuser: str | None = site.user()
# guess the needed token in update_tokens
self._last_token_key: str | None = None
def __getitem__(self, key: str) -> str:
"""Get token value for the given key.
.. version-changed:: 11.0
Support of legacy API tokens was dropped.
"""
if self.site.user() is None and key != 'login':
self.site.login()
if self.site.user() != self._currentuser:
self._currentuser = self.site.user()
self.clear()
if not self._tokens:
self._tokens = self.site.get_tokens([])
try:
token = self._tokens[key]
except KeyError:
raise KeyError(
f'Invalid token {key!r} for user {self._currentuser!r} on '
f'{self.site} wiki.') from None
self._last_token_key = key
return token
def __contains__(self, key) -> bool:
"""Return True if the token name is cached for the current user."""
try:
self[key]
except KeyError:
return False
return True
def __str__(self) -> str:
"""Return a str representation of the internal tokens dictionary."""
return str(self._tokens)
def __repr__(self) -> str:
"""Return a representation of the TokenWallet.
>>> import pywikibot
>>> site = pywikibot.Site('wikipedia:test')
>>> repr(site.tokens)
"TokenWallet(pywikibot.Site('wikipedia:test'))"
.. version-changed:: 8.0
Provide a string which looks like a valid Python expression.
"""
user = f', user={self._currentuser!r}' if self._currentuser else ''
return (f'{type(self).__name__}'
f'(pywikibot.Site({self.site.sitename!r}{user}))')
[docs]
def clear(self) -> None:
"""Clear the self._tokens cache. Tokens are reloaded when needed.
.. version-added:: 8.0
"""
self._tokens.clear()
[docs]
def update_tokens(self, tokens: list[str]) -> list[str]:
"""Return a list of new tokens for a given list of tokens.
This method can be used if a token is outdated and has to be
renewed but the token type is unknown and we only have the old
token. It first gets the token names from all given tokens,
clears the cache and returns fresh new tokens of the found types.
**Usage:**
>>> import pywikibot
>>> site = pywikibot.Site()
>>> tokens = [site.tokens['csrf']] # doctest: +SKIP
>>> new_tokens = site.tokens.update_tokens(tokens) # doctest: +SKIP
.. code-block:: Python
:caption: An example for replacing request token parameters
r._params['token'] = r.site.tokens.update_tokens(r._params['token'])
.. version-added:: 8.0
:param tokens: A list of token types that need to be updated.
:return: A list of updated tokens corresponding to the given
*tokens* types.
:raises KeyError: If no valid token types can be determined to
update.
"""
# find the token types
types = [key
for key, value in self._tokens.items() for token in tokens
if value == token]
# fallback to _last_token_key if no types found
if not types and self._last_token_key is not None:
types = [self._last_token_key]
if not types:
raise KeyError('No valid token types found to update.')
self.clear() # clear the cache
return [self[token_type] for token_type in types]