jenkins-bot has submitted this change. ( https://gerrit.wikimedia.org/r/c/pywikibot/core/+/934674 )
Change subject: [IMPR] Use f-strings within pywikibot ......................................................................
[IMPR] Use f-strings within pywikibot
Changes made by flint 0.78
Change-Id: I55d8a55ef7d526ac9f32b40ce131073c08697cdc --- M pywikibot/exceptions.py M pywikibot/data/sparql.py M docs/conf.py M pywikibot/logentries.py M pywikibot/pagegenerators/_filters.py M pywikibot/pagegenerators/_generators.py M pywikibot/site/_datasite.py M pywikibot/page/_wikibase.py M pywikibot/data/api/_optionset.py M pywikibot/comms/eventstreams.py M pywikibot/page/_links.py M pywikibot/scripts/generate_family_file.py M pywikibot/data/api/_paraminfo.py M pywikibot/scripts/version.py M pywikibot/config.py M pywikibot/scripts/shell.py M pywikibot/page/_collections.py M pywikibot/textlib.py M pywikibot/version.py M pywikibot/site/_generators.py M pywikibot/scripts/preload_sites.py M pywikibot/date.py M pywikibot/page/_user.py M pywikibot/page/_decorators.py M pywikibot/data/memento.py M pywikibot/login.py M pywikibot/tools/djvu.py M pywikibot/specialbots/_upload.py M pywikibot/tools/chars.py M pywikibot/site/_obsoletesites.py M pywikibot/family.py M pywikibot/page/_category.py M pywikibot/page/_basepage.py M pywikibot/scripts/wrapper.py M pywikibot/site/_extensions.py M pywikibot/userinterfaces/terminal_interface_base.py M pywikibot/i18n.py M pywikibot/page/_filepage.py M pywikibot/tools/threading.py M pywikibot/scripts/generate_user_files.py M pywikibot/site/_upload.py M pywikibot/bot.py M pywikibot/tools/__init__.py M pywikibot/__init__.py M pywikibot/bot_choice.py M pywikibot/interwiki_graph.py M pywikibot/time.py M pywikibot/comms/http.py M pywikibot/diff.py M pywikibot/pagegenerators/_factory.py M pywikibot/tools/_deprecate.py M pywikibot/site/_basesite.py M pywikibot/data/api/_generators.py M pywikibot/proofreadpage.py 54 files changed, 247 insertions(+), 341 deletions(-)
Approvals: JJMC89: Looks good to me, approved jenkins-bot: Verified
diff --git a/docs/conf.py b/docs/conf.py index 786fecc..6a3f9c2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -526,7 +526,7 @@ for index, line in enumerate(lines): # highlight the first line if index == 0: # highlight the first line - lines[0] = '**{}**'.format(line.strip('.')) + lines[0] = f"**{line.strip('.')}**"
# add link for pagegenerators options elif line == '¶ms;': diff --git a/pywikibot/__init__.py b/pywikibot/__init__.py index 2b376c3..e17528f 100644 --- a/pywikibot/__init__.py +++ b/pywikibot/__init__.py @@ -148,8 +148,7 @@ if not self._entity: if self.globe not in self.site.globes(): raise exceptions.CoordinateGlobeUnknownError( - '{} is not supported in Wikibase yet.' - .format(self.globe)) + f'{self.globe} is not supported in Wikibase yet.') return self.site.globes()[self.globe]
if isinstance(self._entity, ItemPage): @@ -435,8 +434,7 @@ if site is None: site = Site().data_repository() if site is None: - raise ValueError('Site {} has no data repository' - .format(Site())) + raise ValueError(f'Site {Site()} has no data repository') calendarmodel = site.calendarmodel() self.calendarmodel = calendarmodel # if precision is given it overwrites the autodetection above @@ -1014,9 +1012,8 @@ :param label: Label describing the data type in error messages. """ if not isinstance(page, Page): - raise ValueError( - 'Page {} must be a pywikibot.Page object not a {}.' - .format(page, type(page))) + raise ValueError(f'Page {page} must be a pywikibot.Page object ' + f'not a {type(page)}.')
# validate page exists if not page.exists(): @@ -1304,8 +1301,7 @@ fam = fam or _config.family
if not (code and fam): - raise ValueError('Missing Site {}' - .format('code' if not code else 'family')) + raise ValueError(f"Missing Site {'code' if not code else 'family'}")
if not isinstance(fam, Family): fam = Family.load(fam) @@ -1337,8 +1333,7 @@ key = f'{interface.__name__}:{fam}:{code}:{user}' if key not in _sites or not isinstance(_sites[key], interface): _sites[key] = interface(code=code, fam=fam, user=user) - debug("Instantiated {} object '{}'" - .format(interface.__name__, _sites[key])) + debug(f"Instantiated {interface.__name__} object '{_sites[key]}'")
if _sites[key].code != code: warn('Site {} instantiated using different code "{}"' diff --git a/pywikibot/bot.py b/pywikibot/bot.py index 946d2dc..d40f640 100644 --- a/pywikibot/bot.py +++ b/pywikibot/bot.py @@ -420,8 +420,8 @@ logfile = config.datafilepath('logs', config.logfilename) else: # add PID to logfle name - logfile = config.datafilepath('logs', '{}-{}bot.log' - .format(module_name, pid)) + logfile = config.datafilepath('logs', + f'{module_name}-{pid}bot.log')
# give up infinite rotating file handler with logfilecount of -1; # set it to 999 and use the standard implementation @@ -473,8 +473,8 @@ This may help the user to track errors or report bugs. """ log('') - log('=== Pywikibot framework v{} -- Logging header ===' - .format(pywikibot.__version__)) + log(f'=== Pywikibot framework v{pywikibot.__version__} -- Logging header' + ' ===')
# script call log(f'COMMAND: {sys.argv}') @@ -511,7 +511,7 @@ for name in sorted(packages.keys()): info = packages[name] info.setdefault('path', - '[{}]'.format(info.get('type', 'path unknown'))) + f"[{info.get('type', 'path unknown')}]") info.setdefault('ver', '??') if 'err' in info: log(' {name}: {err}'.format_map(info)) @@ -797,12 +797,11 @@ # at the beginning of the link, start red color. # at the end of the link, reset the color to default pywikibot.info(text[max(0, rng[0] - self.context): rng[0]] - + '<<lightred>>{}<<default>>'.format( - text[rng[0]: rng[1]]) + + f'<<lightred>>{text[rng[0]:rng[1]]}<<default>>' + text[rng[1]: rng[1] + self.context]) else: - question += '<<lightred>>{}<<default>> '.format( - self._old.canonical_title()) + question += ( + f'<<lightred>>{self._old.canonical_title()}<<default>> ')
if self._new is False: question += 'be unlinked?' @@ -947,8 +946,8 @@ elif option in ('-cosmeticchanges', '-cc'): config.cosmetic_changes = (strtobool(value) if value else not config.cosmetic_changes) - output('NOTE: option cosmetic_changes is {}\n' - .format(config.cosmetic_changes)) + output(f'NOTE: option cosmetic_changes is ' + f'{config.cosmetic_changes}\n') elif option == '-simulate': config.simulate = value or True # @@ -1158,8 +1157,7 @@ super().__init__(options)
def __missing__(self, key: str) -> None: - raise Error("'{}' is not a valid option for {}." - .format(key, self._classname)) + raise Error(f"'{key}' is not a valid option for {self._classname}.")
def __getattr__(self, name: str) -> Any: """Get item from dict.""" @@ -1242,8 +1240,7 @@ self.opt.update((opt, options[opt]) for opt in received_options & valid_options) for opt in received_options - valid_options: - pywikibot.warning('{} is not a valid option. It was ignored.' - .format(opt)) + pywikibot.warning(f'{opt} is not a valid option. It was ignored.')
class BaseBot(OptionHandler): @@ -1385,8 +1382,7 @@ msg = f'Working on {page.title()!r}' if config.colorized_output: log(msg) - stdout('\n\n>>> <<lightpurple>>{}<<default>> <<<' - .format(page.title())) + stdout(f'\n\n>>> <<lightpurple>>{page.title()}<<default>> <<<') else: stdout(msg)
@@ -1445,7 +1441,7 @@ pywikibot.showDiff(oldtext, newtext)
if 'summary' in kwargs: - pywikibot.info('Edit summary: {}'.format(kwargs['summary'])) + pywikibot.info(f"Edit summary: {kwargs['summary']}")
page.text = newtext return self._save_page(page, page.save, **kwargs) @@ -1489,22 +1485,21 @@ if not ignore_save_related_errors: raise if isinstance(e, EditConflictError): - pywikibot.info('Skipping {} because of edit conflict' - .format(page.title())) + pywikibot.info( + f'Skipping {page.title()} because of edit conflict') elif isinstance(e, SpamblacklistError): pywikibot.info('Cannot change {} because of blacklist ' 'entry {}'.format(page.title(), e.url)) elif isinstance(e, LockedPageError): - pywikibot.info('Skipping {} (locked page)' - .format(page.title())) + pywikibot.info(f'Skipping {page.title()} (locked page)') else: pywikibot.error('Skipping {} because of a save related ' 'error: {}'.format(page.title(), e)) except ServerError as e: if not ignore_server_errors: raise - pywikibot.error('Server Error while processing {}: {}' - .format(page.title(), e)) + pywikibot.error( + f'Server Error while processing {page.title()}: {e}') else: return True return False @@ -1536,8 +1531,7 @@
pywikibot.info() for op, count in self.counter.items(): - pywikibot.info('{} {} operation{}' - .format(count, op, 's' if count > 1 else '')) + pywikibot.info(f"{count} {op} operation{'s' if count > 1 else ''}")
if hasattr(self, '_start_ts'): write_delta = pywikibot.Timestamp.now() - self._start_ts @@ -1678,8 +1672,7 @@
self.generator_completed = True except QuitKeyboardInterrupt: - pywikibot.info('\nUser quit {} bot run...' - .format(self.__class__.__name__)) + pywikibot.info(f'\nUser quit {self.__class__.__name__} bot run...') except KeyboardInterrupt: if config.verbose_output: raise @@ -1817,8 +1810,8 @@ if self._site == value: pywikibot.warning('Defined site without changing it.') else: - pywikibot.warning('Changed the site from "{}" to ' - '"{}"'.format(self._site, value)) + pywikibot.warning( + f'Changed the site from "{self._site}" to "{value}"') self._site = value
def init_page(self, item: Any) -> 'pywikibot.page.BasePage': @@ -2206,7 +2199,7 @@ pywikibot.info(json.dumps(diff, indent=4, sort_keys=True))
if 'summary' in kwargs: - pywikibot.info('Change summary: {}'.format(kwargs['summary'])) + pywikibot.info(f"Change summary: {kwargs['summary']}")
# TODO PageSaveRelatedErrors should be actually raised in editEntity # (bug T86083) @@ -2245,8 +2238,7 @@ if sourceclaim: claim.addSource(sourceclaim)
- pywikibot.info('Adding {} --> {}'.format(claim.getID(), - claim.getTarget())) + pywikibot.info(f'Adding {claim.getID()} --> {claim.getTarget()}') return self._save_page(item, item.addClaim, claim, bot=bot, **kwargs)
def getSource(self, site: 'BaseSite') -> Optional['pywikibot.page.Claim']: diff --git a/pywikibot/bot_choice.py b/pywikibot/bot_choice.py index b322367..18c00a5 100644 --- a/pywikibot/bot_choice.py +++ b/pywikibot/bot_choice.py @@ -58,7 +58,7 @@ for option in options: formatted_options.append(option.format(default=default)) # remove color highlights before fill function - text = '{} ({})'.format(text, ', '.join(formatted_options)) + text = f"{text} ({', '.join(formatted_options)})" pattern = '<<[a-z]+>>' highlights = re.findall(pattern, text) return fill(re.sub(pattern, '{}', text), width=77).format(*highlights) diff --git a/pywikibot/comms/eventstreams.py b/pywikibot/comms/eventstreams.py index 7f07bc0..66cba5f 100644 --- a/pywikibot/comms/eventstreams.py +++ b/pywikibot/comms/eventstreams.py @@ -338,8 +338,8 @@ try: event = next(self.source) except (ProtocolError, OSError, httplib.IncompleteRead) as e: - warning('Connection error: {}.\n' - 'Try to re-establish connection.'.format(e)) + warning( + f'Connection error: {e}.\nTry to re-establish connection.') del self.source if event is not None: self.sse_kwargs['last_id'] = event.id @@ -349,8 +349,7 @@ try: element = json.loads(event.data) except ValueError as e: - warning('Could not load json data from\n{}\n{}' - .format(event, e)) + warning(f'Could not load json data from\n{event}\n{e}') else: if self.streamfilter(element): n += 1 diff --git a/pywikibot/comms/http.py b/pywikibot/comms/http.py index 8fed6a6..5ef5f3a 100644 --- a/pywikibot/comms/http.py +++ b/pywikibot/comms/http.py @@ -348,8 +348,8 @@ raise Client414Error(HTTPStatus(response.status_code).description)
if response.status_code == HTTPStatus.GATEWAY_TIMEOUT: - raise Server504Error('Server {} timed out' - .format(urlparse(response.url).netloc)) + raise Server504Error( + f'Server {urlparse(response.url).netloc} timed out')
if (not response.ok and response.status_code >= HTTPStatus.INTERNAL_SERVER_ERROR): @@ -436,8 +436,7 @@ if auth is not None and len(auth) == 4: if isinstance(requests_oauthlib, ImportError): warn(str(requests_oauthlib), ImportWarning) - error('OAuth authentication not supported: {}' - .format(requests_oauthlib)) + error(f'OAuth authentication not supported: {requests_oauthlib}') auth = None else: auth = requests_oauthlib.OAuth1(*auth) @@ -532,8 +531,7 @@ try: content.decode(encoding) except LookupError: - pywikibot.warning('Unknown or invalid encoding {!r}' - .format(encoding)) + pywikibot.warning(f'Unknown or invalid encoding {encoding!r}') except UnicodeDecodeError as e: pywikibot.warning(f'{e} found in {content}') else: diff --git a/pywikibot/config.py b/pywikibot/config.py index c261267..e26387d 100644 --- a/pywikibot/config.py +++ b/pywikibot/config.py @@ -34,7 +34,7 @@ default. Editor detection functions were moved to :mod:`editor`. """ # -# (C) Pywikibot team, 2003-2022 +# (C) Pywikibot team, 2003-2023 # # Distributed under the terms of the MIT license. # @@ -369,8 +369,8 @@ elif win_version in (6, 10): sub_dir = ['AppData', 'Roaming'] else: - raise OSError('Windows version {} not supported yet.' - .format(win_version)) + raise OSError( + f'Windows version {win_version} not supported yet.') base_dir_cand.extend([[home] + sub_dir + ['Pywikibot'], [home] + sub_dir + ['pywikibot']]) else: @@ -452,8 +452,8 @@ if not os.path.exists(folder_path): if not_exists_ok: return - raise FileNotFoundError('Family folder {!r} does not exist' - .format(folder_path)) + raise FileNotFoundError( + f'Family folder {folder_path!r} does not exist')
if os.path.isdir(folder_path): for file_name in os.listdir(folder_path): diff --git a/pywikibot/data/api/_generators.py b/pywikibot/data/api/_generators.py index 8164754..1a30567 100644 --- a/pywikibot/data/api/_generators.py +++ b/pywikibot/data/api/_generators.py @@ -6,7 +6,7 @@ :class:`tools.collections.GeneratorWrapper` """ # -# (C) Pywikibot team, 2008-2022 +# (C) Pywikibot team, 2008-2023 # # Distributed under the terms of the MIT license. # @@ -157,13 +157,12 @@ n = 0 while True: self.request[self.continue_name] = offset - pywikibot.debug('{}: Request: {}' - .format(type(self).__name__, self.request)) + pywikibot.debug(f'{type(self).__name__}: Request: {self.request}') data = self.request.submit()
n_items = len(data[self.data_name]) - pywikibot.debug('{}: Retrieved {} items' - .format(type(self).__name__, n_items)) + pywikibot.debug( + f'{type(self).__name__}: Retrieved {n_items} items') if n_items > 0: for item in data[self.data_name]: yield item @@ -561,9 +560,8 @@ else: resultdata = [resultdata[k] for k in sorted(resultdata)] - pywikibot.debug('{name} received {keys}; limit={limit}' - .format(name=type(self).__name__, keys=keys, - limit=self.limit)) + pywikibot.debug( + f'{type(self).__name__} received {keys}; limit={self.limit}') return resultdata
def _extract_results(self, resultdata): @@ -811,9 +809,8 @@ elif isinstance(v, list): old_dict.setdefault(k, []).extend(v) else: - raise ValueError( - 'continued API result had an unexpected type: {}' - .format(type(v).__name__)) + raise ValueError(f'continued API result had an unexpected ' + f'type: {type(v).__name__}')
class ListGenerator(QueryGenerator): @@ -878,13 +875,11 @@ # Something is wrong. if page.site.sametitle(page.title(), pagedict['title']) \ and 'invalid' in pagedict: - raise InvalidTitleError('{}: {}' - .format(page, pagedict['invalidreason'])) + raise InvalidTitleError(f"{page}: {pagedict['invalidreason']}") if int(pagedict['ns']) < 0: raise UnsupportedPageError(page) - raise RuntimeError( - "Page {} has neither 'pageid' nor 'missing' attribute" - .format(pagedict['title'])) + raise RuntimeError(f"Page {pagedict['title']} has neither 'pageid'" + " nor 'missing' attribute")
def _update_contentmodel(page, pagedict: dict) -> None: @@ -1003,8 +998,7 @@ if 'imageinfo' in pagedict: if not isinstance(page, pywikibot.FilePage): raise RuntimeError( - '"imageinfo" found but {} is not a FilePage object' - .format(page)) + f'"imageinfo" found but {page} is not a FilePage object') page._load_file_revisions(pagedict['imageinfo'])
if 'categoryinfo' in pagedict: diff --git a/pywikibot/data/api/_optionset.py b/pywikibot/data/api/_optionset.py index a947d06..3b91d14 100644 --- a/pywikibot/data/api/_optionset.py +++ b/pywikibot/data/api/_optionset.py @@ -113,8 +113,7 @@ elif value is None: removed.add(name) else: - raise ValueError('Dict contains invalid value "{}"'.format( - value)) + raise ValueError(f'Dict contains invalid value "{value}"') invalid_names = ( (enabled - self._valid_enable) | (disabled - self._valid_disable) | (removed - self._valid_enable - self._valid_disable) diff --git a/pywikibot/data/api/_paraminfo.py b/pywikibot/data/api/_paraminfo.py index d2a9cc8..1097c00 100644 --- a/pywikibot/data/api/_paraminfo.py +++ b/pywikibot/data/api/_paraminfo.py @@ -400,8 +400,8 @@ if not name and php_class: name = removeprefix(php_class, 'Api').lower() if name not in ('main', 'pageset'): - pywikibot.warning('Unknown paraminfo module "{}"' - .format(php_class)) + pywikibot.warning( + f'Unknown paraminfo module "{php_class}"') name = '<unknown>:' + php_class
mod_data['name'] = name @@ -421,8 +421,7 @@ if path in result_data: # Only warn first time if result_data[path] is not False: - pywikibot.warning('Path "{}" is ambiguous.' - .format(path)) + pywikibot.warning(f'Path "{path}" is ambiguous.') else: pywikibot.log(f'Found another path "{path}"') result_data[path] = False @@ -499,9 +498,8 @@ return None
if len(param_data) != 1: - raise RuntimeError( - 'parameter data length is eiter empty or not unique.\n{}' - .format(param_data)) + raise RuntimeError(f'parameter data length is eiter empty or not ' + f'unique.\n{param_data}') return param_data[0]
@property diff --git a/pywikibot/data/memento.py b/pywikibot/data/memento.py index af2c085..ed770fe 100644 --- a/pywikibot/data/memento.py +++ b/pywikibot/data/memento.py @@ -287,8 +287,8 @@ allow_redirects=follow_redirects, timeout=timeout or 9) except (InvalidSchema, MissingSchema): - raise ValueError('Only HTTP URIs are supported, ' - 'URI {} unrecognized.'.format(uri)) + raise ValueError( + f'Only HTTP URIs are supported, URI {uri} unrecognized.') if session_set: session.close()
diff --git a/pywikibot/data/sparql.py b/pywikibot/data/sparql.py index 1b3fcc4..5c4e14e 100644 --- a/pywikibot/data/sparql.py +++ b/pywikibot/data/sparql.py @@ -1,6 +1,6 @@ """SPARQL Query interface.""" # -# (C) Pywikibot team, 2016-2022 +# (C) Pywikibot team, 2016-2023 # # Distributed under the terms of the MIT license. # @@ -67,8 +67,8 @@ 'Please provide the endpoint and entity_url ' 'parameters instead of a repo.') if not self.endpoint: - raise Error('The site {} does not provide a sparql endpoint.' - .format(repo)) + raise Error( + f'The site {repo} does not provide a sparql endpoint.') else: if not entity_url: raise Error('If initialised with an endpoint the entity_url ' @@ -126,8 +126,7 @@ values[var] = None elif full_data: if row[var]['type'] not in VALUE_TYPES: - raise ValueError('Unknown type: {}' - .format(row[var]['type'])) + raise ValueError(f"Unknown type: {row[var]['type']}") valtype = VALUE_TYPES[row[var]['type']] values[var] = valtype(row[var], entity_url=self.entity_url) diff --git a/pywikibot/date.py b/pywikibot/date.py index 00a654e..c344cba 100644 --- a/pywikibot/date.py +++ b/pywikibot/date.py @@ -499,8 +499,7 @@ for i, param in enumerate(params)) return strPattern % str_params assert len(decoders) == 1, ( - 'A single parameter does not match {} decoders.' - .format(len(decoders))) + f'A single parameter does not match {len(decoders)} decoders.') # convert integer parameter into its textual representation assert isinstance(params, int) return strPattern % _make_parameter(decoders[0], params) @@ -690,8 +689,8 @@ self.data[key] = value
def __delitem__(self, key: str) -> None: - raise NotImplementedError("Deleting of key '{}' is not implemented" - .format(key)) + raise NotImplementedError( + f"Deleting of key '{key}' is not implemented")
def __iter__(self) -> Iterator[str]: return iter(self.data) @@ -1675,7 +1674,7 @@
:param lang: language code """ - assert len(patterns) == 12, 'pattern %s does not have 12 elements' % lang + assert len(patterns) == 12, f'pattern {lang} does not have 12 elements'
for i in range(12): if patterns[i] is not None: @@ -1989,8 +1988,7 @@ max_day = calendar.monthrange(year, month)[1] if not 1 <= day <= max_day: raise ValueError( - 'Wrong day value {day}; must be 1-{max_day}' - .format(day=day, max_day=max_day)) + f'Wrong day value {day}; must be 1-{max_day}') assert isinstance(lang, str) return formats[dayMnthFmts[month - 1]][lang](day)
diff --git a/pywikibot/diff.py b/pywikibot/diff.py index fa99103..7e3f78a 100644 --- a/pywikibot/diff.py +++ b/pywikibot/diff.py @@ -1,6 +1,6 @@ """Diff module.""" # -# (C) Pywikibot team, 2014-2022 +# (C) Pywikibot team, 2014-2023 # # Distributed under the terms of the MIT license. # @@ -168,8 +168,7 @@
if line_ref is None: if color in self.colors: - colored_line = '<<{color}>>{}<<default>>'.format( - line, color=self.colors[color]) + colored_line = f'<<{self.colors[color]}>>{line}<<default>>' return colored_line return line
@@ -185,8 +184,7 @@ apply_color = self.colors[color] else: apply_color = 'default;' + self.bg_colors[color] - char_tagged = '<<{color}>>{}'.format(char, - color=apply_color) + char_tagged = f'<<{apply_color}>>{char}' color_closed = False else: if char_ref == ' ': @@ -442,8 +440,8 @@ answers.append('?')
pywikibot.info(self._generate_diff(super_hunk)) - choice = pywikibot.input('Accept this hunk [{}]?'.format( - ','.join(answers))) + choice = pywikibot.input( + f"Accept this hunk [{','.join(answers)}]?") if choice not in answers: choice = '?'
diff --git a/pywikibot/exceptions.py b/pywikibot/exceptions.py index e5c1a6a..9b90924 100644 --- a/pywikibot/exceptions.py +++ b/pywikibot/exceptions.py @@ -697,8 +697,8 @@
:param entity: Wikibase entity """ - super().__init__("Entity '{}' doesn't exist on {}" - .format(entity.id, entity.repo)) + super().__init__( + f"Entity '{entity.id}' doesn't exist on {entity.repo}") self.entity = entity
diff --git a/pywikibot/family.py b/pywikibot/family.py index ed75706..0c87877 100644 --- a/pywikibot/family.py +++ b/pywikibot/family.py @@ -328,12 +328,10 @@ raise UnknownFamilyError(f'Family {fam} does not exist') cls = mod.Family.instance if cls.name != fam: - warnings.warn( - 'Family name {} does not match family module name {}' - .format(cls.name, fam), - FamilyMaintenanceWarning, - stacklevel=2, - ) + warnings.warn(f'Family name {cls.name} does not match family ' + f'module name {fam}', + FamilyMaintenanceWarning, + stacklevel=2) # Family 'name' and the 'langs' codes must be ascii letters and digits, # and codes must be lower-case due to the Site loading algorithm; # codes can accept also underscore/dash. @@ -423,8 +421,7 @@ return self.disambiguationTemplates[fallback]
raise KeyError( - 'ERROR: title for disambig template in language {} unknown' - .format(code)) + f'ERROR: title for disambig template in language {code} unknown')
# Methods def protocol(self, code: str) -> str: @@ -543,8 +540,7 @@ """Return interface to use for code.""" if code in self.interwiki_removals: if code in self.codes: - pywikibot.warn('Interwiki removal {} is in {} codes' - .format(code, self)) + pywikibot.warn(f'Interwiki removal {code} is in {self} codes') if code in self.closed_wikis: return 'ClosedSite' if code in self.removed_wikis: @@ -804,8 +800,7 @@ if cls.languages_by_size: return cls.languages_by_size raise NotImplementedError( - 'Family {} needs property "languages_by_size" or "codes"' - .format(cls.name)) + f'Family {cls.name} needs property "languages_by_size" or "codes"')
@classproperty def domains(cls): diff --git a/pywikibot/i18n.py b/pywikibot/i18n.py index 3f1da0b..f14d4e7 100644 --- a/pywikibot/i18n.py +++ b/pywikibot/i18n.py @@ -483,8 +483,7 @@ specific_entries[int(number)] = plural else: assert not specific_entries, ( - 'generic entries defined after specific in "{}"' - .format(variants)) + f'generic entries defined after specific in "{variants}"') plural_entries.append(plural)
if num in specific_entries: @@ -625,8 +624,8 @@ break else: if fallback is not False: - raise KeyError('No fallback key found in lookup dict for "{}"' - .format(code)) + raise KeyError( + f'No fallback key found in lookup dict for "{code}"') trans = None
if trans is None: diff --git a/pywikibot/interwiki_graph.py b/pywikibot/interwiki_graph.py index 96cd28b..041e9d7 100644 --- a/pywikibot/interwiki_graph.py +++ b/pywikibot/interwiki_graph.py @@ -162,8 +162,7 @@ # https://sourceforge.net/p/pywikipediabot/bugs/401/ elif self.graph.get_edge(sourceLabel, targetLabel): pywikibot.error( - 'Tried to create duplicate edge from {} to {}' - .format(refPage, page)) + f'Tried to create duplicate edge from {refPage} to {page}') # duplicate edges would be bad because then get_edge() would # give a list of edges, not a single edge when we handle the # opposite edge. @@ -194,8 +193,7 @@
For more info see https://meta.wikimedia.org/wiki/Interwiki_graphs """ - pywikibot.info('Preparing graph for {}' - .format(self.subject.origin.title())) + pywikibot.info(f'Preparing graph for {self.subject.origin.title()}') # create empty graph self.graph = pydot.Dot()
diff --git a/pywikibot/logentries.py b/pywikibot/logentries.py index 00aed03..33dcc3c 100644 --- a/pywikibot/logentries.py +++ b/pywikibot/logentries.py @@ -1,6 +1,6 @@ """Objects representing Mediawiki log entries.""" # -# (C) Pywikibot team, 2007-2022 +# (C) Pywikibot team, 2007-2023 # # Distributed under the terms of the MIT license. # @@ -64,13 +64,12 @@ "permission to view it due to '{}'" .format(self['type'], key, hidden_key))
- raise KeyError('Log entry ({}) has no {!r} key' - .format(self['type'], key)) + raise KeyError(f"Log entry ({self['type']}) has no {key!r} key")
def __repr__(self) -> str: """Return a string representation of LogEntry object.""" - return '<{}({}, logid={})>'.format(type(self).__name__, - self.site.sitename, self.logid()) + return (f'<{type(self).__name__}({self.site.sitename}, ' + f'logid={self.logid()})>')
def __hash__(self) -> int: """Combine site and logid as the hash.""" diff --git a/pywikibot/login.py b/pywikibot/login.py index 97d6dbc..54c57ee 100644 --- a/pywikibot/login.py +++ b/pywikibot/login.py @@ -378,9 +378,8 @@ if hasattr(self, '_waituntil') \ and datetime.datetime.now() < self._waituntil: diff = self._waituntil - datetime.datetime.now() - pywikibot.warning( - 'Too many tries, waiting {} seconds before retrying.' - .format(diff.seconds)) + pywikibot.warning(f'Too many tries, waiting {diff.seconds}' + ' seconds before retrying.') pywikibot.sleep(diff.seconds)
self.site._loginstatus = LoginStatus.IN_PROGRESS diff --git a/pywikibot/page/_basepage.py b/pywikibot/page/_basepage.py index 8a7374e..7c62214 100644 --- a/pywikibot/page/_basepage.py +++ b/pywikibot/page/_basepage.py @@ -1324,8 +1324,8 @@ return summary
old = self.text - pywikibot.log('Cosmetic changes for {}-{} enabled.' - .format(family, self.site.lang)) + pywikibot.log( + f'Cosmetic changes for {family}-{self.site.lang} enabled.') # cc depends on page directly and via several other imports cc_toolkit = CosmeticChangesToolkit(self, ignore=CANCEL.MATCH) self.text = cc_toolkit.change(old) @@ -1896,8 +1896,8 @@ answer = 'y' else: answer = pywikibot.input_choice( - "Can't delete {}; do you want to mark it for deletion instead?" - .format(self), + f"Can't delete {self};" + ' do you want to mark it for deletion instead?', [('Yes', 'y'), ('No', 'n'), ('All', 'a')], 'n', automatic_quit=False) if answer == 'a': @@ -1980,8 +1980,7 @@ self.loadDeletedRevisions() if timestamp not in self._deletedRevs: raise ValueError( - 'Timestamp {} is not a deleted revision' - .format(timestamp)) + f'Timestamp {timestamp} is not a deleted revision') self._deletedRevs[timestamp]['marked'] = undelete
def undelete(self, reason: Optional[str] = None) -> None: @@ -2106,8 +2105,8 @@
if old_cat not in cats: if self.namespace() != 10: - pywikibot.error('{} is not in category {}!' - .format(self, old_cat.title())) + pywikibot.error( + f'{self} is not in category {old_cat.title()}!') else: pywikibot.info('{} is not in category {}, skipping...' .format(self, old_cat.title())) diff --git a/pywikibot/page/_category.py b/pywikibot/page/_category.py index fb42b80..87313a5 100644 --- a/pywikibot/page/_category.py +++ b/pywikibot/page/_category.py @@ -1,6 +1,6 @@ """Object representing a MediaWiki category page.""" # -# (C) Pywikibot team, 2008-2022 +# (C) Pywikibot team, 2008-2023 # # Distributed under the terms of the MIT license. # @@ -28,8 +28,8 @@ self.sortKey = sort_key super().__init__(source, title, ns=14) if self.namespace() != 14: - raise ValueError("'{}' is not in the category namespace!" - .format(self.title())) + raise ValueError( + f"'{self.title()}' is not in the category namespace!")
def aslink(self, sort_key: Optional[str] = None) -> str: """Return a link to place a page in this Category. diff --git a/pywikibot/page/_collections.py b/pywikibot/page/_collections.py index 77d8ad8..6f40b30 100644 --- a/pywikibot/page/_collections.py +++ b/pywikibot/page/_collections.py @@ -525,8 +525,7 @@ try: index = self._by_key[index] except KeyError as e: - raise ValueError('No entity with id {} was found' - .format(index)) from e + raise ValueError(f'No entity with id {index} was found') from e return self._data[index]
def __setitem__(self, index, value): @@ -537,8 +536,7 @@ try: index = self._by_key[index] except KeyError as e: - raise ValueError('No entity with id {} was found' - .format(index)) from e + raise ValueError(f'No entity with id {index} was found') from e obj = self._data[index] del self._data[index] del self._by_key[obj.id] diff --git a/pywikibot/page/_decorators.py b/pywikibot/page/_decorators.py index ad3a32a..2e90b5d 100644 --- a/pywikibot/page/_decorators.py +++ b/pywikibot/page/_decorators.py @@ -37,8 +37,7 @@ err = edit_err # edit_err will be deleted in the end of the scope link = self.title(as_link=True) if do_async: - pywikibot.error('page {} not saved due to {}\n' - .format(link, err)) + pywikibot.error(f'page {link} not saved due to {err}\n') pywikibot.log(f'Error saving page {link} ({err})\n', exc_info=True) if not callback and not do_async: diff --git a/pywikibot/page/_filepage.py b/pywikibot/page/_filepage.py index a6c6473..2317fac 100644 --- a/pywikibot/page/_filepage.py +++ b/pywikibot/page/_filepage.py @@ -43,16 +43,15 @@ self._file_revisions = {} # dictionary to cache File history. super().__init__(source, title, 6) if self.namespace() != 6: - raise ValueError("'{}' is not in the file namespace!" - .format(self.title())) + raise ValueError(f"'{self.title()}' is not in the file namespace!")
def _load_file_revisions(self, imageinfo) -> None: for file_rev in imageinfo: # filemissing in API response indicates most fields are missing # see https://gerrit.wikimedia.org/r/c/mediawiki/core/+/533482/ if 'filemissing' in file_rev: - pywikibot.warning("File '{}' contains missing revisions" - .format(self.title())) + pywikibot.warning( + f"File '{self.title()}' contains missing revisions") continue file_revision = FileInfo(file_rev) self._file_revisions[file_revision.timestamp] = file_revision diff --git a/pywikibot/page/_links.py b/pywikibot/page/_links.py index 36ba716..fb156b8 100644 --- a/pywikibot/page/_links.py +++ b/pywikibot/page/_links.py @@ -6,7 +6,7 @@ its contents. """ # -# (C) Pywikibot team, 2008-2022 +# (C) Pywikibot team, 2008-2023 # # Distributed under the terms of the MIT license. # @@ -80,8 +80,7 @@ assert all(isinstance(item, str) for item in self._items)
attrs = (f'{getattr(self, attr)!r}' for attr in self._items) - return 'pywikibot.page.{}({})'.format(type(self).__name__, - ', '.join(attrs)) + return f"pywikibot.page.{type(self).__name__}({', '.join(attrs)})"
def lookup_namespace(self): """ @@ -105,9 +104,8 @@ ns = self.site.namespaces[default_nskey] return ns
- raise TypeError( - 'Invalid type "{}" for Page._nskey. Must be int or str.' - .format(type(self._nskey))) + raise TypeError(f'Invalid type "{type(self._nskey)}" for Page._nskey.' + ' Must be int or str.')
@property def site(self): @@ -135,8 +133,7 @@ """Return full page title, including localized namespace.""" # Avoid that ':' will be added to the title for Main ns. if self.namespace != Namespace.MAIN: - return '{}:{}'.format(self.site.namespace(self.namespace), - self.title) + return f'{self.site.namespace(self.namespace)}:{self.title}' return self.title
def ns_title(self, onsite=None): @@ -454,8 +451,7 @@ # Reject illegal characters. m = Link.illegal_titles_pattern.search(t) if m: - raise InvalidTitleError('{!r} contains illegal char(s) {!r}' - .format(t, m.group(0))) + raise InvalidTitleError(f'{t!r} contains illegal char(s) {m[0]!r}')
# Pages with "/./" or "/../" appearing in the URLs will # often be unreachable due to the way web browsers deal @@ -467,13 +463,11 @@ or '/../' in t or t.endswith(('/.', '/..'))): raise InvalidTitleError( - "(contains . / combinations): '{}'" - .format(self._text)) + f"(contains . / combinations): '{self._text}'")
# Magic tilde sequences? Nu-uh! if '~~~' in t: - raise InvalidTitleError("(contains ~~~): '{}'" - .format(self._text)) + raise InvalidTitleError(f"(contains ~~~): '{self._text}'")
if self._namespace != -1 and len(t) > 255: raise InvalidTitleError(f"(over 255 bytes): '{t}'") @@ -482,8 +476,7 @@ # with a fragment identifier. if not t.strip(' ') and not self._is_interwiki: # T197642 raise InvalidTitleError( - 'The link [[{}]] does not contain a page title' - .format(self._text)) + f'The link [[{self._text}]] does not contain a page title')
# MediaWiki uses uppercase IP addresses if self._namespace in (2, 3) and is_ip_address(t): @@ -550,7 +543,7 @@ onsite = self._source text = super().astext(onsite) if self.section: - text = '{}#{}]]'.format(text.rstrip(']'), self.section) + text = f"{text.rstrip(']')}#{self.section}]]"
return text
diff --git a/pywikibot/page/_user.py b/pywikibot/page/_user.py index c889dba..31c62d4 100644 --- a/pywikibot/page/_user.py +++ b/pywikibot/page/_user.py @@ -46,8 +46,7 @@ self._isAutoblock = False super().__init__(source, title, ns=2) if self.namespace() != 2: - raise ValueError("'{}' is not in the user namespace!" - .format(self.title())) + raise ValueError(f"'{self.title()}' is not in the user namespace!") if self._isAutoblock: # This user is probably being queried for purpose of lifting # an autoblock. @@ -275,8 +274,7 @@ self.site.blockuser(self, *args, **kwargs) except APIError as err: if err.code == 'invalidrange': - raise ValueError('{} is not a valid IP range.' - .format(self.username)) + raise ValueError(f'{self.username} is not a valid IP range.')
raise err
diff --git a/pywikibot/page/_wikibase.py b/pywikibot/page/_wikibase.py index 2083a3c..b3a347b 100644 --- a/pywikibot/page/_wikibase.py +++ b/pywikibot/page/_wikibase.py @@ -105,15 +105,13 @@ self.id = id_ if id_ is not None else '-1' if self.id != '-1' and not self.is_valid_id(self.id): raise InvalidTitleError( - "'{}' is not a valid {} page title" - .format(self.id, self.entity_type)) + f"'{self.id}' is not a valid {self.entity_type} page title")
def __repr__(self) -> str: if self.id != '-1': return 'pywikibot.page.{}({!r}, {!r})'.format( self.__class__.__name__, self.repo, self.id) - return 'pywikibot.page.{}({!r})'.format( - self.__class__.__name__, self.repo) + return f'pywikibot.page.{self.__class__.__name__}({self.repo!r})'
@classmethod def is_valid_id(cls, entity_id: str) -> bool: @@ -393,16 +391,14 @@ # state which needs to be raised as an exception, but also # logged in case an exception handler is catching # the generic Error - pywikibot.error('{} is in invalid state' - .format(self.__class__.__name__)) - raise Error('{} is in invalid state' - .format(self.__class__.__name__)) + msg = f'{self.__class__.__name__} is in invalid state' + pywikibot.error(msg) + raise Error(msg)
page_id = self.getID(numeric=True) result = list(self.repo.load_pages_from_pageids([page_id])) if not result: - raise Error('There is no existing page with id "{}"' - .format(page_id)) + raise Error(f'There is no existing page with id "{page_id}"')
page = result.pop() if page.namespace() != page.site.namespaces.FILE: @@ -501,8 +497,8 @@ elif site.property_namespace.id == ns: self._namespace = site.property_namespace else: - raise ValueError('{!r}: Namespace "{}" is not valid' - .format(site, int(ns))) + raise ValueError( + f'{site!r}: Namespace "{int(ns)}" is not valid')
if 'entity_type' in kwargs: entity_type = kwargs.pop('entity_type') @@ -510,8 +506,8 @@ entity_type_ns = site.get_namespace_for_entity_type( entity_type) except EntityTypeUnknownError: - raise ValueError('Wikibase entity type "{}" unknown' - .format(entity_type)) + raise ValueError( + f'Wikibase entity type "{entity_type}" unknown')
if self._namespace: if self._namespace != entity_type_ns: @@ -543,8 +539,8 @@ elif self.site.property_namespace.id == ns: self._namespace = self.site.property_namespace else: - raise ValueError('{!r}: Namespace "{!r}" is not valid' - .format(self.site, ns)) + raise ValueError( + f'{self.site!r}: Namespace "{ns!r}" is not valid')
WikibaseEntity.__init__( self, @@ -889,10 +885,8 @@ # if none of the above applies, this item is in an invalid state # which needs to be raise as an exception, but also logged in case # an exception handler is catching the generic Error. - pywikibot.error('{} is in invalid state' - .format(self.__class__.__name__)) - raise Error('{} is in invalid state' - .format(self.__class__.__name__)) + pywikibot.error(f'{self.__class__.__name__} is in invalid state') + raise Error(f'{self.__class__.__name__} is in invalid state')
return params
@@ -960,8 +954,7 @@ if hasattr(page, '_item'): return page._item if not page.site.has_data_repository: - raise WikiBaseError('{} has no data repository' - .format(page.site)) + raise WikiBaseError(f'{page.site} has no data repository') if not lazy_load and not page.exists(): raise NoPageError(page)
@@ -1709,8 +1702,7 @@ """ value_class = self.types[self.type] if not isinstance(value, value_class): - raise ValueError('{} is not type {}.' - .format(value, value_class)) + raise ValueError(f'{value} is not type {value_class}.') self.target = value
def changeTarget( diff --git a/pywikibot/pagegenerators/_factory.py b/pywikibot/pagegenerators/_factory.py index d965d26..4a6f5c4 100644 --- a/pywikibot/pagegenerators/_factory.py +++ b/pywikibot/pagegenerators/_factory.py @@ -1,6 +1,6 @@ """GeneratorFactory module wich handles pagegenerators options.""" # -# (C) Pywikibot team, 2008-2022 +# (C) Pywikibot team, 2008-2023 # # Distributed under the terms of the MIT license. # @@ -328,8 +328,7 @@ # Part before ":" might be interpreted as an interwiki prefix prefix = category.split(':', 1)[0] # whole word if ":" not present if prefix not in self.site.namespaces[14]: - category = '{}:{}'.format( - self.site.namespace(14), category) + category = f'{self.site.namespace(14)}:{category}' cat = pywikibot.Category(pywikibot.Link(category, source=self.site, default_namespace=14)) @@ -835,8 +834,8 @@ '{}: {}'.format(*i) for i in self.site.proofread_levels.items()] valid_ql = ', '.join(valid_ql_list) - pywikibot.warning('Acceptable values for -ql are:\n {}' - .format(valid_ql)) + pywikibot.warning( + f'Acceptable values for -ql are:\n {valid_ql}') self.qualityfilter_list = int_values return True
diff --git a/pywikibot/pagegenerators/_filters.py b/pywikibot/pagegenerators/_filters.py index 59cc30f..57d720c 100644 --- a/pywikibot/pagegenerators/_filters.py +++ b/pywikibot/pagegenerators/_filters.py @@ -232,8 +232,7 @@ else: if show_filtered: pywikibot.info( - 'Page {} is a subpage that is too deep. Skipping.' - .format(page)) + f'Page {page} is a subpage that is too deep. Skipping.')
class RegexFilter: diff --git a/pywikibot/pagegenerators/_generators.py b/pywikibot/pagegenerators/_generators.py index d3c1762..d73f499 100644 --- a/pywikibot/pagegenerators/_generators.py +++ b/pywikibot/pagegenerators/_generators.py @@ -458,8 +458,8 @@
user = pywikibot.User(site, username) if not (user.isAnonymous() or user.isRegistered()): - pywikibot.warning('User "{}" does not exist on site "{}".' - .format(user.username, site)) + pywikibot.warning( + f'User "{user.username}" does not exist on site "{site}".')
gen = (contrib[0] for contrib in user.contributions( namespaces=namespaces, total=total)) @@ -875,8 +875,7 @@ """ # restrict query to local site local_query = f'{self.query} site:{self.site.hostname()}' - base = 'http://%7B%7D%7B%7D%27.format(self.site.hostname(), - self.site.articlepath) + base = f'http://%7Bself.site.hostname()%7D%7Bself.site.articlepath%7D' pattern = base.replace('{}', '(.+)') for url in self.queryGoogle(local_query): m = re.search(pattern, url) diff --git a/pywikibot/proofreadpage.py b/pywikibot/proofreadpage.py index da052e2..8432fc5 100644 --- a/pywikibot/proofreadpage.py +++ b/pywikibot/proofreadpage.py @@ -517,8 +517,8 @@ try: num = int(right) except ValueError: - raise InvalidTitleError('{} contains invalid index {!r}' - .format(self, right)) + raise InvalidTitleError( + f'{self} contains invalid index {right!r}') else: base = right
@@ -562,24 +562,22 @@ index_page, others = self._index if others: pywikibot.warning(f'{self} linked to several Index pages.') - pywikibot.info('{}{!s}'.format(' ' * 9, [index_page] + others)) + pywikibot.info(f"{' ' * 9}{[index_page] + others!s}")
if index_page: pywikibot.info( - '{}Selected Index: {}'.format(' ' * 9, index_page)) - pywikibot.info('{}remaining: {!s}'.format(' ' * 9, others)) + f"{' ' * 9}Selected Index: {index_page}") + pywikibot.info(f"{' ' * 9}remaining: {others!s}")
if not index_page: - pywikibot.warning('Page {} is not linked to any Index page.' - .format(self)) + pywikibot.warning(f'Page {self} is not linked to any Index page.')
return index_page
@index.setter def index(self, value: 'IndexPage') -> None: if not isinstance(value, IndexPage): - raise TypeError('value {} must be an IndexPage object.' - .format(value)) + raise TypeError(f'value {value} must be an IndexPage object.') self._index = (value, [])
@index.deleter @@ -861,8 +859,7 @@ # if None raises TypeError. url_image = url_image['src'] except (TypeError, AttributeError): - raise ValueError('No prp-page-image src found for {}.' - .format(self)) + raise ValueError(f'No prp-page-image src found for {self}.') else: url_image = 'https:' + url_image
@@ -890,8 +887,7 @@
if ocr_tool not in self._OCR_METHODS: raise TypeError( - "ocr_tool must be in {}, not '{}'." - .format(self._OCR_METHODS, ocr_tool)) + f"ocr_tool must be in {self._OCR_METHODS}, not '{ocr_tool}'.")
# wrong link fail with Exceptions for retry in range(5, 30, 5): @@ -983,8 +979,7 @@ cmd_fmt = self._OCR_CMDS[ocr_tool] except KeyError: raise TypeError( - "ocr_tool must be in {}, not '{}'." - .format(self._OCR_METHODS, ocr_tool)) + f"ocr_tool must be in {self._OCR_METHODS}, not '{ocr_tool}'.")
params = { 'url_image': url_image, @@ -1016,16 +1011,15 @@
if ocr_tool not in self._OCR_METHODS: raise TypeError( - "ocr_tool must be in {}, not '{}'." - .format(self._OCR_METHODS, ocr_tool)) + f"ocr_tool must be in {self._OCR_METHODS}, not '{ocr_tool}'.")
# if _multi_page, try _do_hocr() first and fall back to _do_ocr() if ocr_tool == self._PHETOOLS and self._multi_page: error, text = self._do_hocr() if not error and isinstance(text, str): return text - pywikibot.warning('{}: phetools hocr failed, falling back to ocr.' - .format(self)) + pywikibot.warning( + f'{self}: phetools hocr failed, falling back to ocr.')
error, text = self._do_ocr(ocr_tool=ocr_tool)
@@ -1262,8 +1256,7 @@ # Sanity check if WS site use page convention name/number. if page._num is not None: assert page_cnt == int(page._num), ( - 'Page number {} not recognised as page {}.' - .format(page_cnt, title)) + f'Page number {page_cnt} not recognised as page {title}.')
# Mapping: numbers <-> pages. self._page_from_numbers[page_cnt] = page @@ -1362,8 +1355,7 @@ try: return self._labels_from_page_number[page_number] except KeyError: - raise KeyError('Page number ".../{}" not in range.' - .format(page_number)) + raise KeyError(f'Page number ".../{page_number}" not in range.')
@staticmethod def _get_from_label(mapping_dict: Dict[str, Any], diff --git a/pywikibot/scripts/generate_family_file.py b/pywikibot/scripts/generate_family_file.py index 874631b..f6afdd5 100755 --- a/pywikibot/scripts/generate_family_file.py +++ b/pywikibot/scripts/generate_family_file.py @@ -257,18 +257,16 @@ sys.exit(1)
code_hostname_pairs = '\n '.join( - "'{code}': '{hostname}',".format( - code=k, hostname=urlparse(w.server).netloc - ) for k, w in self.wikis.items()) + f"'{k}': '{urlparse(w.server).netloc}'," + for k, w in self.wikis.items())
code_path_pairs = '\n '.join( f"'{k}': '{w.scriptpath}'," for k, w in self.wikis.items())
code_protocol_pairs = '\n '.join( - "'{code}': '{protocol}',".format( - code=k, protocol=urlparse(w.server).scheme - ) for k, w in self.wikis.items()) + f"'{k}': '{urlparse(w.server).scheme}'," + for k, w in self.wikis.items())
content = family_template % { 'url': self.base_url, 'name': self.name, diff --git a/pywikibot/scripts/generate_user_files.py b/pywikibot/scripts/generate_user_files.py index fe5d618..2e25b55 100755 --- a/pywikibot/scripts/generate_user_files.py +++ b/pywikibot/scripts/generate_user_files.py @@ -8,7 +8,7 @@ Also EXTERNAL EDITOR SETTINGS section can be copied. """ # -# (C) Pywikibot team, 2010-2022 +# (C) Pywikibot team, 2010-2023 # # Distributed under the terms of the MIT license. # @@ -395,8 +395,8 @@ botpasswordpass))
if not userlist: # Show a sample - usernames = "# usernames['{}']['{}'] = 'MyUsername'".format( - main_family, main_code) + usernames = ( + f"# usernames['{main_family}']['{main_code}'] = 'MyUsername'") else: usernames = '\n'.join( "usernames['{user.family}']['{user.code}'] = '{user.name}'" diff --git a/pywikibot/scripts/preload_sites.py b/pywikibot/scripts/preload_sites.py index 47a66b6..afda243 100755 --- a/pywikibot/scripts/preload_sites.py +++ b/pywikibot/scripts/preload_sites.py @@ -18,7 +18,7 @@ script was moved to the framework scripts folder. """ # -# (C) Pywikibot team, 2021-2022 +# (C) Pywikibot team, 2021-2023 # # Distributed under the terms of the MIT license. # @@ -85,8 +85,8 @@ # to allow adding futures in preload_family the workers must be one # more than families are handled worker = max(len(families) * 2, worker) - pywikibot.info('Using {} workers to process {} families' - .format(worker, len(families))) + pywikibot.info( + f'Using {worker} workers to process {len(families)} families') with ThreadPoolExecutor(worker) as executor: futures = {executor.submit(preload_family, family, executor) for family in families} diff --git a/pywikibot/scripts/shell.py b/pywikibot/scripts/shell.py index bdfe181..1dafa12 100755 --- a/pywikibot/scripts/shell.py +++ b/pywikibot/scripts/shell.py @@ -59,8 +59,7 @@ if sys.platform == 'win32': import platform import subprocess - subprocess.run('title Python {} Shell' - .format(platform.python_version()), + subprocess.run(f'title Python {platform.python_version()} Shell', shell=True, check=True) del subprocess del platform diff --git a/pywikibot/scripts/version.py b/pywikibot/scripts/version.py index 12f542d..7a83797 100755 --- a/pywikibot/scripts/version.py +++ b/pywikibot/scripts/version.py @@ -63,8 +63,7 @@ or not callable(requests.certs.where)): pywikibot.info(' cacerts: not defined') elif not os.path.isfile(requests.certs.where()): - pywikibot.info(' cacerts: {} (missing)'.format( - requests.certs.where())) + pywikibot.info(f' cacerts: {requests.certs.where()} (missing)') else: pywikibot.info(' cacerts: ' + requests.certs.where())
diff --git a/pywikibot/scripts/wrapper.py b/pywikibot/scripts/wrapper.py index d253be4..cd22f39 100755 --- a/pywikibot/scripts/wrapper.py +++ b/pywikibot/scripts/wrapper.py @@ -213,8 +213,7 @@ .format('s' if len(requirements) > 1 else ''))
for requirement in requirements: - print(' pip install "{}"\n' - .format(str(requirement).partition(';')[0])) + print(f" pip install "{str(requirement).partition(';')[0]}"\n")
def check_modules(script=None): @@ -476,8 +475,8 @@ try: module = import_module(file_package) except ImportError as e: - warn('Parent module {} not found: {}' - .format(file_package, e), ImportWarning) + warn(f'Parent module {file_package} not found: {e}', + ImportWarning)
help_option = any(arg.startswith('-help:') or arg == '-help' for arg in script_args) diff --git a/pywikibot/site/_basesite.py b/pywikibot/site/_basesite.py index 97c8d9b..eec692a 100644 --- a/pywikibot/site/_basesite.py +++ b/pywikibot/site/_basesite.py @@ -48,12 +48,11 @@ if code.lower() != code: # Note the Site function in __init__ also emits a UserWarning # for this condition, showing the callers file and line no. - pywikibot.log('BaseSite: code "{}" converted to lowercase' - .format(code)) + pywikibot.log(f'BaseSite: code "{code}" converted to lowercase') code = code.lower() if not all(x in pywikibot.family.CODE_CHARACTERS for x in code): - pywikibot.log('BaseSite: code "{}" contains invalid characters' - .format(code)) + pywikibot.log( + f'BaseSite: code "{code}" contains invalid characters') self.__code = code if isinstance(fam, str) or fam is None: self.__family = pywikibot.family.Family.load(fam) @@ -67,8 +66,8 @@ self.__code = self.__family.obsolete[self.__code] # Note the Site function in __init__ emits a UserWarning # for this condition, showing the callers file and line no. - pywikibot.log('Site {} instantiated using aliases code of {}' - .format(self, code)) + pywikibot.log( + f'Site {self} instantiated using aliases code of {code}') else: # no such language anymore self.obsolete = True @@ -209,8 +208,7 @@
def __repr__(self) -> str: """Return internal representation.""" - return '{}("{}", "{}")'.format( - self.__class__.__name__, self.code, self.family) + return f'{self.__class__.__name__}("{self.code}", "{self.family}")'
def __hash__(self): """Return hash value of instance.""" @@ -321,8 +319,8 @@
else: # fallback for non WM sites try: - name = '{}:{}'.format(Namespace.CATEGORY, - self.family.disambcatname[self.code]) + name = (f'{Namespace.CATEGORY}:' + f'{self.family.disambcatname[self.code]}') except KeyError: raise Error(f'No disambiguation category name found in ' f'{self.family.name}_family for {self}') @@ -375,8 +373,7 @@ # delimiters like spaces and multiple combinations of them with # only one delimiter sep = self.family.title_delimiter_and_aliases[0] - pattern = re.compile('[{}]+' - .format(self.family.title_delimiter_and_aliases)) + pattern = re.compile(f'[{self.family.title_delimiter_and_aliases}]+') title1 = pattern.sub(sep, title1) title2 = pattern.sub(sep, title2) if title1 == title2: diff --git a/pywikibot/site/_datasite.py b/pywikibot/site/_datasite.py index 119d1eb..e14e4bb 100644 --- a/pywikibot/site/_datasite.py +++ b/pywikibot/site/_datasite.py @@ -777,8 +777,8 @@ raise
if 'results' not in data: - raise RuntimeError("Unexpected missing 'results' in query data\n{}" - .format(data)) + raise RuntimeError( + f"Unexpected missing 'results' in query data\n{data}")
results = [] for result_hash in data['results']: diff --git a/pywikibot/site/_extensions.py b/pywikibot/site/_extensions.py index f252c5d..f6e10e5 100644 --- a/pywikibot/site/_extensions.py +++ b/pywikibot/site/_extensions.py @@ -755,7 +755,7 @@ data = req.submit()['query']['pages'] if '-1' in data: msg = data['-1'].get('invalidreason', - 'Unknown exception:\n{}'.format(data['-1'])) + f"Unknown exception:\n{data['-1']}") raise Error(msg)
return data[str(page.pageid)]['extract'] diff --git a/pywikibot/site/_generators.py b/pywikibot/site/_generators.py index 04ded77..50b6089 100644 --- a/pywikibot/site/_generators.py +++ b/pywikibot/site/_generators.py @@ -1558,8 +1558,7 @@ :param prop: Requested props to check :raises UserRightsError: user cannot view a requested prop """ - err = '{}: User:{} not authorized to view '.format(msg_prefix, - self.user()) + err = f'{msg_prefix}: User:{self.user()} not authorized to view ' if not self.has_right('deletedhistory'): if self.mw_version < '1.34': raise UserRightsError(err + 'deleted revisions.') @@ -1901,7 +1900,7 @@ """ param = self._paraminfo.parameter('query+querypage', 'page') assert special_page in param['type'], ( - '{} not in {}'.format(special_page, param['type'])) + f"{special_page} not in {param['type']}")
return self._generator(api.PageGenerator, type_arg='querypage', gqppage=special_page, diff --git a/pywikibot/site/_obsoletesites.py b/pywikibot/site/_obsoletesites.py index cc16046..b5c1223 100644 --- a/pywikibot/site/_obsoletesites.py +++ b/pywikibot/site/_obsoletesites.py @@ -21,8 +21,7 @@
def _closed_error(self, notice: str = '') -> None: """An error instead of pointless API call.""" - pywikibot.error('Site {} has been closed. {}'.format(self.sitename, - notice)) + pywikibot.error(f'Site {self.sitename} has been closed. {notice}')
def page_restrictions( self, page: 'pywikibot.Page') -> Dict[str, Tuple[str, str]]: diff --git a/pywikibot/site/_upload.py b/pywikibot/site/_upload.py index 7239ead..0a0d0b6 100644 --- a/pywikibot/site/_upload.py +++ b/pywikibot/site/_upload.py @@ -1,6 +1,6 @@ """Objects representing API upload to MediaWiki site.""" # -# (C) Pywikibot team, 2009-2022 +# (C) Pywikibot team, 2009-2023 # # Distributed under the terms of the MIT license. # @@ -181,8 +181,7 @@ if os.path.isfile(self.filename): file_size = os.path.getsize(self.filename) elif offset is not False: - raise ValueError("File '{}' does not exist." - .format(self.filename)) + raise ValueError(f"File '{self.filename}' does not exist.")
# Verify the stash when a file key and offset is given: # requests the SHA1 and file size uploaded and compares it to @@ -242,8 +241,8 @@
data = {} if file_key and offset is False or offset == file_size: - pywikibot.log('Reused already upload file using filekey "{}"' - .format(file_key)) + pywikibot.log( + f'Reused already upload file using filekey "{file_key}"') # TODO: Use sessionkey instead of filekey if necessary final_request = self.site._request( parameters={ @@ -270,8 +269,7 @@ 'filename': file_page_title, 'comment': self.comment}) if chunked_upload: if offset > 0: - pywikibot.log('Continuing upload from byte {}' - .format(offset)) + pywikibot.log(f'Continuing upload from byte {offset}') poll = False while True:
@@ -522,5 +520,5 @@ self.filepage._load_file_revisions([result['imageinfo']]) return True
- raise Error('Unrecognized result: {}' - .format(data_result or result['result'])) + raise Error( + f"Unrecognized result: {data_result or result['result']}") diff --git a/pywikibot/specialbots/_upload.py b/pywikibot/specialbots/_upload.py index a577105..c3f1c7a 100644 --- a/pywikibot/specialbots/_upload.py +++ b/pywikibot/specialbots/_upload.py @@ -3,7 +3,7 @@ Do not import classes directly from here but from specialbots. """ # -# (C) Pywikibot team, 2003-2022 +# (C) Pywikibot team, 2003-2023 # # Distributed under the terms of the MIT license. # @@ -158,8 +158,8 @@ # raised from connection lost during response.iter_content() except requests.ConnectionError: fd.flush() - pywikibot.info('Connection closed at byte {}' - .format(path.stat().st_size)) + pywikibot.info( + f'Connection closed at byte {path.stat().st_size}') # raised from response.raise_for_status() except requests.HTTPError as e: # exit criteria if size is not available @@ -230,9 +230,8 @@ if self.filename_prefix: filename = self.filename_prefix + filename if not self.keep_filename: - pywikibot.info( - '\nThe filename on the target wiki will default to: {}\n' - .format(filename)) + pywikibot.info(f'\nThe filename on the target wiki will default ' + f'to: {filename}\n') assert not self.opt.always newfn = pywikibot.input( 'Enter a better name, or press enter to accept:') @@ -412,9 +411,8 @@ comment=self.summary) except APIError as error: if error.code == 'uploaddisabled': - pywikibot.error( - 'Upload error: Local file uploads are disabled on {}.' - .format(site)) + pywikibot.error(f'Upload error: Local file uploads are ' + f'disabled on {site}.') elif error.code == 'copyuploadbaddomain' and not download \ and '://' in file_url: pywikibot.error(error) @@ -464,8 +462,7 @@ self.upload_file(file_url) self.counter['read'] += 1 except QuitKeyboardInterrupt: - pywikibot.info('\nUser quit {} bot run...' - .format(self.__class__.__name__)) + pywikibot.info(f'\nUser quit {self.__class__.__name__} bot run...') except KeyboardInterrupt: if config.verbose_output: raise diff --git a/pywikibot/textlib.py b/pywikibot/textlib.py index 9078b3c..a1fee36 100644 --- a/pywikibot/textlib.py +++ b/pywikibot/textlib.py @@ -173,9 +173,7 @@ """ first = string[0] if first.isalpha() and case == 'first-letter': - pattern = '[{}{}]{}'.format(first.upper(), - first.lower(), - re.escape(string[1:])) + pattern = f'[{first.upper()}{first.lower()}]{re.escape(string[1:])}' else: pattern = re.escape(string) return pattern @@ -889,8 +887,8 @@ if must_piped: new_text = f'[[{new_title}|{new_label}]]' else: - new_text = '[[{}]]{}'.format(new_label[:len(new_title)], - new_label[len(new_title):]) + new_text = (f'[[{new_label[:len(new_title)]}]]' + f'{new_label[len(new_title):]}')
text = text[:start] + new_text + text[end:] # Make sure that next time around we will not find this same hit. @@ -1471,13 +1469,12 @@ title, sortKey = rest, None try: cat = pywikibot.Category(site, - '{}:{}'.format(match['namespace'], title), + f"{match['namespace']}:{title}", sort_key=sortKey) except InvalidTitleError: # Category title extracted contains invalid characters # Likely due to on-the-fly category name creation, see T154309 - pywikibot.warning('Invalid category title extracted: {}' - .format(title)) + pywikibot.warning(f'Invalid category title extracted: {title}') else: result.append(cat)
diff --git a/pywikibot/time.py b/pywikibot/time.py index da10ecb..8b2a494 100644 --- a/pywikibot/time.py +++ b/pywikibot/time.py @@ -445,10 +445,8 @@ key, duration = string[-1], string[:-1]
if key not in MW_KEYS: - raise ValueError('Time period qualifier is unrecognized: {}' - .format(string)) + raise ValueError(f'Time period qualifier is unrecognized: {string}') if not duration.isdigit(): - raise ValueError("Time period's duration should be numeric: {}" - .format(string)) + raise ValueError(f"Time period's duration should be numeric: {string}")
return key, int(duration) diff --git a/pywikibot/tools/__init__.py b/pywikibot/tools/__init__.py index 1c0cac5..a2a7802 100644 --- a/pywikibot/tools/__init__.py +++ b/pywikibot/tools/__init__.py @@ -646,8 +646,8 @@ stderr=subprocess.PIPE, bufsize=65535) except OSError: - raise ValueError('7za is not installed or cannot uncompress "{}"' - .format(filename)) + raise ValueError( + f'7za is not installed or cannot uncompress "{filename}"')
stderr = process.stderr.read() process.stderr.close() diff --git a/pywikibot/tools/_deprecate.py b/pywikibot/tools/_deprecate.py index ca6f1eb..cd4b4d9 100644 --- a/pywikibot/tools/_deprecate.py +++ b/pywikibot/tools/_deprecate.py @@ -62,8 +62,7 @@ frame = sys._getframe(stacklevel + 1) class_name = frame.f_code.co_name if class_name and class_name != '<module>': - obj.__full_name__ = '{}.{}.{}'.format(obj.__module__, - class_name, obj.__name__) + obj.__full_name__ = f'{obj.__module__}.{class_name}.{obj.__name__}' else: obj.__full_name__ = f'{obj.__module__}.{obj.__name__}'
@@ -462,8 +461,7 @@ depth = get_wrapper_depth(wrapper) + 1 args, varargs, kwargs, *_ = getfullargspec(wrapper.__wrapped__) if varargs is not None and kwargs is not None: - raise ValueError('{} may not have * or ** args.' - .format(name)) + raise ValueError(f'{name} may not have * or ** args.') deprecated = set(__kw) & set(arg_names) if len(__args) > len(args): deprecated.update(arg_names[:len(__args) - len(args)]) @@ -604,13 +602,12 @@ otherwise it provides a DeprecationWarning """ if '.' in name: - raise ValueError('Deprecated name "{}" may not contain ' - '".".'.format(name)) + raise ValueError(f'Deprecated name "{name}" may not contain ".".') if name in self._deprecated: raise ValueError(f'Name "{name}" is already deprecated.') if replacement is not None and hasattr(self._module, name): - raise ValueError('Module has already an attribute named ' - '"{}".'.format(name)) + raise ValueError( + f'Module has already an attribute named "{name}".')
if replacement_name is None: if hasattr(replacement, '__name__'): diff --git a/pywikibot/tools/chars.py b/pywikibot/tools/chars.py index c086f4d..c64c84d 100644 --- a/pywikibot/tools/chars.py +++ b/pywikibot/tools/chars.py @@ -18,7 +18,7 @@ # At the moment we've only added the characters from the Cf category _invisible_chars = _category_cf
-INVISIBLE_REGEX = re.compile('[{}]'.format(''.join(_invisible_chars))) +INVISIBLE_REGEX = re.compile(f"[{''.join(_invisible_chars)}]")
def contains_invisible(text): diff --git a/pywikibot/tools/djvu.py b/pywikibot/tools/djvu.py index 2c885c6..68d87e9 100644 --- a/pywikibot/tools/djvu.py +++ b/pywikibot/tools/djvu.py @@ -1,6 +1,6 @@ """Wrapper around djvulibre to access djvu files properties and content.""" # -# (C) Pywikibot team, 2015-2022 +# (C) Pywikibot team, 2015-2023 # # Distributed under the terms of the MIT license. # @@ -75,9 +75,7 @@
def __repr__(self) -> str: """Return a more complete string representation.""" - return "{}.{}('{}')".format(self.__module__, - self.__class__.__name__, - self._filename) + return f"{self.__module__}.{type(self).__name__}('{self._filename}')"
def __str__(self) -> str: """Return a string representation.""" @@ -223,8 +221,7 @@ :param force: if True, refresh the cached data """ if not self.has_text(force=force): - raise ValueError('Djvu file {} has no text layer.' - .format(self.file)) + raise ValueError(f'Djvu file {self.file} has no text layer.') res, stdoutdata = _call_cmd(['djvutxt', f'--page={n}', self.file]) if not res: diff --git a/pywikibot/tools/threading.py b/pywikibot/tools/threading.py index b076a5f..37635a8 100644 --- a/pywikibot/tools/threading.py +++ b/pywikibot/tools/threading.py @@ -198,8 +198,7 @@ super().__init__(*args) for item in self: if not isinstance(item, threading.Thread): - raise TypeError("Cannot add '{}' to ThreadList" - .format(type(item))) + raise TypeError(f"Cannot add '{type(item)}' to ThreadList")
def active_count(self): """Return the number of alive threads and delete all non-alive ones.""" @@ -214,13 +213,11 @@ def append(self, thd): """Add a thread to the pool and start it.""" if not isinstance(thd, threading.Thread): - raise TypeError("Cannot append '{}' to ThreadList" - .format(type(thd))) + raise TypeError(f"Cannot append '{type(thd)}' to ThreadList")
while self.active_count() >= self.limit: time.sleep(self.wait_time)
super().append(thd) thd.start() - pywikibot.logging.debug("thread {} ('{}') started" - .format(len(self), type(thd))) + pywikibot.logging.debug(f"thread {len(self)} ('{type(thd)}') started") diff --git a/pywikibot/userinterfaces/terminal_interface_base.py b/pywikibot/userinterfaces/terminal_interface_base.py index 0f6ecfa..9ba7545 100644 --- a/pywikibot/userinterfaces/terminal_interface_base.py +++ b/pywikibot/userinterfaces/terminal_interface_base.py @@ -515,8 +515,7 @@ # lock stream output with self.lock: if not force: - line_template = '{{0: >{}}}: {{1}}\n'.format( - len(str(len(answers)))) + line_template = f'{{0: >{len(str(len(answers)))}}}: {{1}}\n' for i, entry in enumerate(answers, start=1): self.stream_output(line_template.format(i, entry))
@@ -536,8 +535,7 @@
if force: raise ValueError( - 'Invalid value "{}" for default during force.' - .format(default)) + f'Invalid value "{default}" for default during force.')
self.stream_output('Error: Invalid response\n')
diff --git a/pywikibot/version.py b/pywikibot/version.py index 7dd4633..5a05047 100644 --- a/pywikibot/version.py +++ b/pywikibot/version.py @@ -107,8 +107,7 @@
# Git and SVN can silently fail, as it may be a nightly. if exceptions: - pywikibot.debug('version algorithm exceptions:\n{!r}' - .format(exceptions)) + pywikibot.debug(f'version algorithm exceptions:\n{exceptions!r}')
if isinstance(date, str): datestring = date @@ -252,7 +251,7 @@ e = tag.find('\n', s) tag = tag[(s + 6):e] t = tag.strip().split('/') - tag = '[{}] {}'.format(t[0][:-1], '-'.join(t[3:])) + tag = f"[{t[0][:-1]}] {'-'.join(t[3:])}" dp = subprocess.Popen([cmd, '--no-pager', 'log', '-1', '--pretty=format:"%ad|%an|%h|%H|%d"',