Fix B006 (Do not use mutable data structures for argument defaults)

This commit is contained in:
Adam Turner 2023-07-29 01:42:06 +01:00 committed by Adam Turner
parent 4791b544dc
commit 1dcc112608
20 changed files with 86 additions and 58 deletions

View File

@ -163,7 +163,6 @@ ignore = [
"ARG003", # unused class method argument
"ARG005", # unused lambda argument
# flake8-bugbear
"B006", # do not use mutable data structures for argument default
"B023", # function definition does not bind loop variable
"B026", # keyword argument ... must come after starred arguments
"B028", # no explicit `stacklevel` keyword argument found

View File

@ -9,6 +9,7 @@ import os
import pickle
import sys
from collections import deque
from collections.abc import Sequence # NoQA: TCH003
from io import StringIO
from os import path
from pathlib import Path
@ -736,7 +737,7 @@ class Sphinx:
logger.warning(__('role %r is already registered, it will be overridden'),
name, type='app', subtype='add_generic_role')
role = roles.GenericRole(name, nodeclass)
docutils.register_role(name, role)
docutils.register_role(name, role) # type: ignore[arg-type]
def add_domain(self, domain: type[Domain], override: bool = False) -> None:
"""Register a domain.
@ -813,7 +814,8 @@ class Sphinx:
def add_object_type(self, directivename: str, rolename: str, indextemplate: str = '',
parse_node: Callable | None = None,
ref_nodeclass: type[TextElement] | None = None,
objname: str = '', doc_field_types: list = [], override: bool = False,
objname: str = '', doc_field_types: Sequence = (),
override: bool = False,
) -> None:
"""Register a new object type.

View File

@ -52,7 +52,7 @@ from sphinx.writers.html import HTMLWriter
from sphinx.writers.html5 import HTML5Translator
if TYPE_CHECKING:
from collections.abc import Iterable, Iterator
from collections.abc import Iterable, Iterator, Sequence
#: the filename for the inventory of objects
INVENTORY_FILENAME = 'objects.inv'
@ -122,7 +122,7 @@ class BuildInfo:
self,
config: Config | None = None,
tags: Tags | None = None,
config_categories: list[str] = [],
config_categories: Sequence[str] = (),
) -> None:
self.config_hash = ''
self.tags_hash = ''

View File

@ -155,8 +155,10 @@ class Config:
'option_emphasise_placeholders': (False, 'env', []),
}
def __init__(self, config: dict[str, Any] = {}, overrides: dict[str, Any] = {}) -> None:
self.overrides = dict(overrides)
def __init__(self, config: dict[str, Any] | None = None,
overrides: dict[str, Any] | None = None) -> None:
config = config or {}
self.overrides = dict(overrides) if overrides is not None else {}
self.values = Config.config_values.copy()
self._raw_config = config
self.setup: Callable | None = config.get('setup', None)
@ -310,7 +312,7 @@ class Config:
raise ExtensionError(__('Config value %r already present') % name)
self.values[name] = (default, rebuild, types)
def filter(self, rebuild: str | list[str]) -> Iterator[ConfigValue]:
def filter(self, rebuild: str | Sequence[str]) -> Iterator[ConfigValue]:
if isinstance(rebuild, str):
rebuild = [rebuild]
return (value for value in self if value.rebuild in rebuild)

View File

@ -319,7 +319,7 @@ class DefaultRole(SphinxDirective):
role, messages = roles.role(role_name, self.state_machine.language,
self.lineno, self.state.reporter)
if role: # type: ignore[truthy-function]
docutils.register_role('', role)
docutils.register_role('', role) # type: ignore[arg-type]
self.env.temp_data['default_role'] = role_name
else:
literal_block = nodes.literal_block(self.block_text, self.block_text)

View File

@ -21,7 +21,7 @@ from sphinx.roles import XRefRole
from sphinx.util.typing import RoleFunction
if TYPE_CHECKING:
from collections.abc import Iterable
from collections.abc import Iterable, Sequence
from docutils.parsers.rst import Directive
@ -264,10 +264,11 @@ class Domain:
fullname = f'{self.name}:{name}'
def role_adapter(typ: str, rawtext: str, text: str, lineno: int,
inliner: Inliner, options: dict = {}, content: list[str] = [],
inliner: Inliner, options: dict | None = None,
content: Sequence[str] = (),
) -> tuple[list[Node], list[system_message]]:
return self.roles[name](fullname, rawtext, text, lineno,
inliner, options, content)
inliner, options or {}, content)
self._role_cache[name] = role_adapter
return role_adapter

View File

@ -628,7 +628,7 @@ def get_import_prefixes_from_env(env: BuildEnvironment) -> list[str | None]:
def import_by_name(
name: str, prefixes: list[str | None] = [None],
name: str, prefixes: Sequence[str | None] = (None,),
) -> tuple[str, Any, Any, str]:
"""Import a Python object that has the given *name*, under one of the
*prefixes*. The first name that succeeds is used.
@ -700,7 +700,7 @@ def _import_by_name(name: str, grouped_exception: bool = True) -> tuple[Any, Any
raise ImportError(*exc.args) from exc
def import_ivar_by_name(name: str, prefixes: list[str | None] = [None],
def import_ivar_by_name(name: str, prefixes: Sequence[str | None] = (None,),
grouped_exception: bool = True) -> tuple[str, Any, Any, str]:
"""Import an instance variable that has the given *name*, under one of the
*prefixes*. The first name that succeeds is used.

View File

@ -20,7 +20,7 @@ Both, the url string and the caption string must escape ``%`` as ``%%``.
from __future__ import annotations
import re
from typing import Any
from typing import TYPE_CHECKING, Any
from docutils import nodes, utils
from docutils.nodes import Node, system_message
@ -34,6 +34,9 @@ from sphinx.util import logging, rst
from sphinx.util.nodes import split_explicit_title
from sphinx.util.typing import RoleFunction
if TYPE_CHECKING:
from collections.abc import Sequence
logger = logging.getLogger(__name__)
@ -91,7 +94,7 @@ def make_link_role(name: str, base_url: str, caption: str) -> RoleFunction:
# Remark: It is an implementation detail that we use Pythons %-formatting.
# So far we only expose ``%s`` and require quoting of ``%`` using ``%%``.
def role(typ: str, rawtext: str, text: str, lineno: int,
inliner: Inliner, options: dict = {}, content: list[str] = [],
inliner: Inliner, options: dict | None = None, content: Sequence[str] = (),
) -> tuple[list[Node], list[system_message]]:
text = utils.unescape(text)
has_explicit_title, title, part = split_explicit_title(text)

View File

@ -34,7 +34,7 @@ import builtins
import hashlib
import inspect
import re
from collections.abc import Iterable
from collections.abc import Iterable, Sequence
from importlib import import_module
from typing import Any, cast
@ -140,7 +140,7 @@ class InheritanceGraph:
"""
def __init__(self, class_names: list[str], currmodule: str, show_builtins: bool = False,
private_bases: bool = False, parts: int = 0,
aliases: dict[str, str] | None = None, top_classes: list[Any] = [],
aliases: dict[str, str] | None = None, top_classes: Sequence[Any] = (),
) -> None:
"""*class_names* is a list of child classes to show bases from.
@ -163,7 +163,7 @@ class InheritanceGraph:
return classes
def _class_info(self, classes: list[Any], show_builtins: bool, private_bases: bool,
parts: int, aliases: dict[str, str] | None, top_classes: list[Any],
parts: int, aliases: dict[str, str] | None, top_classes: Sequence[Any],
) -> list[tuple[str, str, list[str], str]]:
"""Return name and bases for all classes that are ancestors of
*classes*.
@ -273,9 +273,11 @@ class InheritanceGraph:
def _format_graph_attrs(self, attrs: dict[str, Any]) -> str:
return ''.join(['%s=%s;\n' % x for x in sorted(attrs.items())])
def generate_dot(self, name: str, urls: dict[str, str] = {},
def generate_dot(self, name: str, urls: dict[str, str] | None = None,
env: BuildEnvironment | None = None,
graph_attrs: dict = {}, node_attrs: dict = {}, edge_attrs: dict = {},
graph_attrs: dict | None = None,
node_attrs: dict | None = None,
edge_attrs: dict | None = None,
) -> str:
"""Generate a graphviz dot graph from the classes that were passed in
to __init__.
@ -287,11 +289,16 @@ class InheritanceGraph:
*graph_attrs*, *node_attrs*, *edge_attrs* are dictionaries containing
key/value pairs to pass on as graphviz properties.
"""
if urls is None:
urls = {}
g_attrs = self.default_graph_attrs.copy()
n_attrs = self.default_node_attrs.copy()
e_attrs = self.default_edge_attrs.copy()
if graph_attrs is not None:
g_attrs.update(graph_attrs)
if node_attrs is not None:
n_attrs.update(node_attrs)
if edge_attrs is not None:
e_attrs.update(edge_attrs)
if env:
g_attrs.update(env.config.inheritance_graph_attrs)

View File

@ -69,17 +69,13 @@ class Deque(collections.deque):
raise StopIteration
def _convert_type_spec(_type: str, translations: dict[str, str] = {}) -> str:
def _convert_type_spec(_type: str, translations: dict[str, str] | None = None) -> str:
"""Convert type specification to reference in reST."""
if _type in translations:
if translations is not None and _type in translations:
return translations[_type]
else:
if _type == 'None':
return ':obj:`None`'
else:
return ':class:`%s`' % _type
return _type
return ':py:obj:`None`'
return f':py:class:`{_type}`'
class GoogleDocstring:
@ -1023,8 +1019,11 @@ def _token_type(token: str, location: str | None = None) -> str:
def _convert_numpy_type_spec(
_type: str, location: str | None = None, translations: dict = {},
_type: str, location: str | None = None, translations: dict | None = None,
) -> str:
if translations is None:
translations = {}
def convert_obj(obj, translations, default_translation):
translation = translations.get(obj, obj)

View File

@ -36,7 +36,7 @@ from sphinx.util.logging import prefixed_warnings
from sphinx.util.typing import RoleFunction, TitleGetter
if TYPE_CHECKING:
from collections.abc import Iterator
from collections.abc import Iterator, Sequence
from sphinx.application import Sphinx
from sphinx.ext.autodoc import Documenter
@ -224,7 +224,7 @@ class SphinxComponentRegistry:
parse_node: Callable | None = None,
ref_nodeclass: type[TextElement] | None = None,
objname: str = '',
doc_field_types: list = [],
doc_field_types: Sequence = (),
override: bool = False,
) -> None:
logger.debug('[app] adding object type: %r',

View File

@ -18,6 +18,8 @@ from sphinx.util.docutils import ReferenceRole, SphinxRole
from sphinx.util.typing import RoleFunction
if TYPE_CHECKING:
from collections.abc import Sequence
from sphinx.application import Sphinx
from sphinx.environment import BuildEnvironment
@ -365,8 +367,10 @@ class Abbreviation(SphinxRole):
# TODO: Change to use `SphinxRole` once SphinxRole is fixed to support options.
def code_role(name: str, rawtext: str, text: str, lineno: int,
inliner: docutils.parsers.rst.states.Inliner,
options: dict = {}, content: list[str] = [],
options: dict | None = None, content: Sequence[str] = (),
) -> tuple[list[Node], list[system_message]]:
if options is None:
options = {}
options = options.copy()
docutils.parsers.rst.roles.set_classes(options)
language = options.get('language', '')

View File

@ -107,8 +107,11 @@ class Theme:
'searched theme configs') % (section, name)) from exc
return default
def get_options(self, overrides: dict[str, Any] = {}) -> dict[str, Any]:
def get_options(self, overrides: dict[str, Any] | None = None) -> dict[str, Any]:
"""Return a dictionary of theme options and their values."""
if overrides is None:
overrides = {}
if self.base:
options = self.base.get_options()
else:

View File

@ -4,6 +4,7 @@ from __future__ import annotations
import os
import re
from collections.abc import Sequence # NoQA: TCH003
from contextlib import contextmanager
from copy import copy
from os import path
@ -276,7 +277,8 @@ class CustomReSTDispatcher:
def role(
self, role_name: str, language_module: ModuleType, lineno: int, reporter: Reporter,
) -> tuple[RoleFunction, list[system_message]]:
return self.role_func(role_name, language_module, lineno, reporter)
return self.role_func(role_name, language_module, # type: ignore[return-value]
lineno, reporter)
class ElementLookupError(Exception):
@ -455,17 +457,17 @@ class SphinxRole:
inliner: Inliner #: The ``docutils.parsers.rst.states.Inliner`` object.
options: dict #: A dictionary of directive options for customization
#: (from the "role" directive).
content: list[str] #: A list of strings, the directive content for customization
content: Sequence[str] #: A list of strings, the directive content for customization
#: (from the "role" directive).
def __call__(self, name: str, rawtext: str, text: str, lineno: int,
inliner: Inliner, options: dict = {}, content: list[str] = [],
inliner: Inliner, options: dict | None = None, content: Sequence[str] = (),
) -> tuple[list[Node], list[system_message]]:
self.rawtext = rawtext
self.text = unescape(text)
self.lineno = lineno
self.inliner = inliner
self.options = options
self.options = options if options is not None else {}
self.content = content
# guess role type
@ -522,8 +524,11 @@ class ReferenceRole(SphinxRole):
explicit_title_re = re.compile(r'^(.+?)\s*(?<!\x00)<(.*?)>$', re.DOTALL)
def __call__(self, name: str, rawtext: str, text: str, lineno: int,
inliner: Inliner, options: dict = {}, content: list[str] = [],
inliner: Inliner, options: dict | None = None, content: Sequence[str] = (),
) -> tuple[list[Node], list[system_message]]:
if options is None:
options = {}
# if the first character is a bang, don't cross-reference at all
self.disabled = text.startswith('!')

View File

@ -537,12 +537,14 @@ def _should_unwrap(subject: Callable) -> bool:
return False
def signature(subject: Callable, bound_method: bool = False, type_aliases: dict = {},
def signature(subject: Callable, bound_method: bool = False, type_aliases: dict | None = None,
) -> inspect.Signature:
"""Return a Signature object for the given *subject*.
:param bound_method: Specify *subject* is a bound method or not
"""
if type_aliases is None:
type_aliases = {}
try:
if _should_unwrap(subject):

View File

@ -64,7 +64,7 @@ def default_role(docname: str, name: str) -> Generator[None, None, None]:
dummy_reporter = Reporter('', 4, 4)
role_fn, _ = roles.role(name, english, 0, dummy_reporter)
if role_fn: # type: ignore[truthy-function]
docutils.register_role('', role_fn)
docutils.register_role('', role_fn) # type: ignore[arg-type]
else:
logger.warning(__('default role %s not found'), name, location=docname)

View File

@ -4,6 +4,7 @@ from __future__ import annotations
import sys
import typing
from collections.abc import Sequence
from struct import Struct
from types import TracebackType
from typing import Any, Callable, ForwardRef, TypeVar, Union
@ -41,7 +42,7 @@ NoneType = type(None)
PathMatcher = Callable[[str], bool]
# common role functions
RoleFunction = Callable[[str, str, str, int, Inliner, dict[str, Any], list[str]],
RoleFunction = Callable[[str, str, str, int, Inliner, dict[str, Any], Sequence[str]],
tuple[list[nodes.Node], list[nodes.system_message]]]
# A option spec for directive

View File

@ -5,7 +5,7 @@ import math
import os
import re
import textwrap
from collections.abc import Generator, Iterable
from collections.abc import Generator, Iterable, Sequence
from itertools import chain, groupby
from typing import TYPE_CHECKING, Any, cast
@ -416,7 +416,7 @@ class TextTranslator(SphinxTranslator):
self.stateindent.append(indent)
def end_state(
self, wrap: bool = True, end: list[str] | None = [''], first: str | None = None,
self, wrap: bool = True, end: Sequence[str] | None = ('',), first: str | None = None,
) -> None:
content = self.states.pop()
maxindent = sum(self.stateindent)

View File

@ -1222,7 +1222,7 @@ Do as you please
expected = """\
Do as you please
:Yields: :class:`str` -- Extended
:Yields: :py:class:`str` -- Extended
"""
assert expected == actual

View File

@ -39,7 +39,7 @@ def f6(x: int, *args, y: str, z: str) -> None:
pass
def f7(x: int = None, y: dict = {}) -> None:
def f7(x: int = None, y: dict = {}) -> None: # NoQA: B006
pass