Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ jobs:
- name: Type check with pyright
run: pyright

ty:
pyrefly:
runs-on: ubuntu-latest

steps:
Expand All @@ -99,8 +99,8 @@ jobs:
- name: Install dependencies
run: uv pip install -r pyproject.toml --group package --group test --group types
- run: echo "$(which python)"
- name: Type check with ty
run: ty check --color=always --python="$(which python)"
- name: Type check with pyrefly
run: pyrefly check

docs-lint:
runs-on: ubuntu-latest
Expand Down
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,8 @@ translations = [
]
types = [
"mypy==1.19.1",
"pyrefly",
"pyrefly==1.1.1",
"pyright==1.1.407",
"ty",
{ include-group = "type-stubs" },
]
type-stubs = [
Expand Down
19 changes: 19 additions & 0 deletions pyrefly.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ project_includes = [
project_excludes = [
"**/tests/roots*",
]
preset = "legacy"
python_version = "3.12"
replace_imports_with_any = [
"imagesize",
Expand All @@ -25,3 +26,21 @@ replace_imports_with_any = [
# https://pyrefly.org/en/docs/error-kinds/
[errors]
implicitly-defined-attribute = false # many false positives

[[sub_config]]
matches = "sphinx/domains/c/**"
[sub_config.errors]
bad-argument-type = false
bad-assignment = false
bad-return = false
missing-attribute = false
unsupported-operation = false

[[sub_config]]
matches = "sphinx/domains/cpp/**"
[sub_config.errors]
bad-argument-type = false
bad-assignment = false
bad-return = false
missing-attribute = false
unsupported-operation = false
2 changes: 2 additions & 0 deletions sphinx/_cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class _SubcommandModule(Protocol):

def _load_subcommand_descriptions() -> Iterator[tuple[str, str]]:
for command, module_name in _COMMANDS.items():
# pyrefly: ignore [bad-assignment]
module: _SubcommandModule = importlib.import_module(module_name)
try:
description = module.parser_description
Expand Down Expand Up @@ -269,6 +270,7 @@ def _parse_command(argv: Sequence[str] = ()) -> tuple[str, Sequence[str]]:

def _load_subcommand(command_name: str) -> tuple[str, _PARSER_SETUP, _RUNNER]:
try:
# pyrefly: ignore [bad-assignment]
module: _SubcommandModule = importlib.import_module(_COMMANDS[command_name])
Comment on lines +273 to 274

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the problem here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The exact error is

ModuleType is not assignable to _SubcommandModule
ModuleType.parser_description or _SubcommandModule.parser_description uses __getattr__, which cannot be checked for override compatibility

It comes from here: https://github.com/facebook/pyrefly/blob/ce5d522246ead87f02dc58fb28731fd9f51e2a16/pyrefly/lib/alt/attr.rs#L1456

Maybe it's a gap in Pyrefly that we can address (I've filed facebook/pyrefly#4077) but I don't think it's necessarily blocking here

except KeyError:
msg = f'invalid command name {command_name!r}.'
Expand Down
12 changes: 11 additions & 1 deletion sphinx/builders/html/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,7 @@ def prepare_writing(self, docnames: Set[str]) -> None:
for domain in self.env.domains.sorted():
for index_cls in domain.indices:
index_name = f'{domain.name}-{index_cls.name}'
# pyrefly: ignore [not-iterable]
if check_names and index_name not in indices_config:
continue
content, collapse = index_cls(domain).generate()
Expand Down Expand Up @@ -566,6 +567,7 @@ def get_doc_context(self, docname: str, body: str, metatags: str) -> dict[str, A
# find out relations
prev = next = None
parents = []
# pyrefly: ignore [bad-index, unsupported-operation]
rellinks = self.globalcontext['rellinks'][:]
related = self.relations.get(docname)
titles = self.env.titles
Expand All @@ -575,6 +577,7 @@ def get_doc_context(self, docname: str, body: str, metatags: str) -> dict[str, A
'link': self.get_relative_uri(docname, related[2]),
'title': self.render_partial(titles[related[2]])['title'],
}
# pyrefly: ignore [missing-attribute]
rellinks.append((related[2], next['title'], 'N', _('next')))
except KeyError:
next = None
Expand All @@ -584,6 +587,7 @@ def get_doc_context(self, docname: str, body: str, metatags: str) -> dict[str, A
'link': self.get_relative_uri(docname, related[1]),
'title': self.render_partial(titles[related[1]])['title'],
}
# pyrefly: ignore [missing-attribute]
rellinks.append((related[1], prev['title'], 'P', _('previous')))
except KeyError:
# the relation is (somehow) not in the TOC tree, handle
Expand Down Expand Up @@ -1190,7 +1194,9 @@ def js_tag(js: _JavaScript | str) -> str:
templatename = new_template

# sort JS/CSS before rendering HTML
# pyrefly: ignore [bad-assignment]
script_files: list[_JavaScript] = ctx['script_files']
# pyrefly: ignore [bad-assignment]
css_files: list[_CascadingStyleSheet] = ctx['css_files']

# Skip sorting if users modifies script_files directly (maybe via `html_context`).
Expand Down Expand Up @@ -1245,12 +1251,16 @@ def js_tag(js: _JavaScript | str) -> str:
ensuredir(output_path.parent)
try:
output_path.write_text(
output, encoding=ctx['encoding'], errors='xmlcharrefreplace'
output,
# pyrefly: ignore [bad-argument-type]
encoding=ctx['encoding'],
errors='xmlcharrefreplace',
)
except OSError as err:
logger.warning(__('error writing file %s: %s'), output_path, err)
if self.copysource and ctx.get('sourcename'):
# copy the source file for the "show source" link
# pyrefly: ignore [bad-assignment]
sourcename: str = ctx['sourcename']
source_file_path = self._sources_dir / sourcename
source_file_path.parent.mkdir(parents=True, exist_ok=True)
Expand Down
2 changes: 1 addition & 1 deletion sphinx/cmd/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ def _parse_logging(
warning = TeeStripANSI(warning, warnfp) # type: ignore[assignment]
error = warning

return status, warning, error, warnfp # ty: ignore[invalid-return-type]
return status, warning, error, warnfp # pyrefly: ignore[bad-return]


def _parse_confoverrides(
Expand Down
1 change: 1 addition & 0 deletions sphinx/domains/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ def directive(self, name: str) -> type[Directive] | None:
class DirectiveAdapter(BaseDirective): # type: ignore[valid-type,misc]
def run(self) -> list[Node]:
self.name = fullname
# pyrefly: ignore [bad-return]
return super().run()

self._directive_cache[name] = DirectiveAdapter
Expand Down
2 changes: 2 additions & 0 deletions sphinx/domains/python/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,7 @@ def resolve_xref(
# if not found, use contnode
children = [contnode]

# pyrefly: ignore [bad-argument-type]
return make_refnode(builder, fromdocname, obj[0], obj[1], children, name)

def resolve_any_xref(
Expand Down Expand Up @@ -1032,6 +1033,7 @@ def resolve_any_xref(
role = 'py:' + self.role_for_objtype(obj[2]) # type: ignore[operator]
results.append((
role,
# pyrefly: ignore [bad-argument-type]
make_refnode(builder, fromdocname, obj[0], obj[1], children, name),
))
return results
Expand Down
2 changes: 2 additions & 0 deletions sphinx/domains/python/_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ def make_xrefs(
results.append(contnode or innernode(sub_target, sub_target)) # type: ignore[call-arg]
else:
results.append(
# pyrefly: ignore [bad-argument-type]
self.make_xref(
rolename,
domain,
Expand All @@ -147,6 +148,7 @@ def make_xrefs(
if sub_target in {'Literal', 'typing.Literal', '~typing.Literal'}:
in_literal = True

# pyrefly: ignore [bad-return]
return results


Expand Down
2 changes: 2 additions & 0 deletions sphinx/environment/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ def setup(self, app: Sphinx) -> None:
# initialise domains
if self.domains is None:
# if we are unpickling an environment, we need to recreate the domains
# pyrefly: ignore [bad-assignment]
self.domains = _DomainsContainer._from_environment(
self, registry=app.registry
)
Expand Down Expand Up @@ -1184,4 +1185,5 @@ def update(self, other: Iterable[tuple[str, Any]] = (), /, **kwargs: Any) -> Non
other_dict = dict(other) if not isinstance(other, dict) else other
for dct in other_dict, kwargs:
for key, value in dct.items():
# pyrefly: ignore [unsupported-operation]
self[key] = value
1 change: 1 addition & 0 deletions sphinx/environment/adapters/toctree.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ def _resolve_toctree(
if hasattr(toctree, 'uid'):
# move uid to caption_node to translate it
caption_node.uid = toctree.uid # type: ignore[attr-defined]
# pyrefly: ignore [missing-attribute]
del toctree.uid
newnode.append(caption_node)
newnode.extend(tocentries)
Expand Down
1 change: 1 addition & 0 deletions sphinx/ext/autodoc/_dynamic/_member_finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ def _get_members_to_document(
and unmangled in wanted_members
):
if analyzer and (qualname, unmangled) in analyzer.attr_docs:
# pyrefly: ignore [bad-index]
attr_docstring = analyzer.attr_docs[qualname, unmangled]
else:
attr_docstring = None
Expand Down
2 changes: 2 additions & 0 deletions sphinx/ext/autodoc/_dynamic/_preserve_defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,11 @@ def update_default_value(obj: Any, bound_method: bool) -> None:
parameters[i] = param.replace(default=DefaultValue(value))
else:
default = kw_defaults.pop(0) # type: ignore[assignment]
# pyrefly: ignore [bad-argument-type]
value = get_default_value(lines, default)
if value is None:
value = ast_unparse(default)
# pyrefly: ignore [bad-argument-type]
parameters[i] = param.replace(default=DefaultValue(value))

sig = sig.replace(parameters=parameters)
Expand Down
1 change: 1 addition & 0 deletions sphinx/ext/autodoc/_legacy_class_based/_documenters.py
Original file line number Diff line number Diff line change
Expand Up @@ -2490,6 +2490,7 @@ def get_doc(self) -> list[list[str]] | None:
if parent___slots__ and (
docstring := parent___slots__.get(self.objpath[-1])
):
# pyrefly: ignore [bad-argument-type]
docstring = prepare_docstring(docstring)
return [docstring]
else:
Expand Down
5 changes: 3 additions & 2 deletions sphinx/ext/autodoc/_sentinels.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ def __setattr__(self, key: str, value: object) -> NoReturn:
def __or__(self, other: object) -> _SpecialForm:
from typing import Union

return Union[self, other] # NoQA: UP007 # ty: ignore[invalid-type-form, invalid-return-type]
return Union[self, other] # NoQA: UP007 # pyrefly: ignore

def __ror__(self, other: object) -> _SpecialForm:
from typing import Union

return Union[other, self] # NoQA: UP007 # ty: ignore[invalid-type-form, invalid-return-type]
return Union[other, self] # NoQA: UP007 # pyrefly: ignore

def __getstate__(self) -> NoReturn:
msg = f'Cannot pickle {self._name}'
Expand Down Expand Up @@ -64,6 +64,7 @@ class _AllTC(enum.Enum):
ALL = enum.auto()

def __contains__(self, item: object) -> Literal[True]: return True
# pyrefly: ignore [bad-return]
def __add__(self, other: object) -> Self: pass
type ALL_T = Literal[_AllTC.ALL]
ALL: Final[ALL_T] = _AllTC.ALL
Expand Down
1 change: 1 addition & 0 deletions sphinx/ext/autodoc/importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ def get_class_members(
unmangled = unmangle(cls, name)
if unmangled and unmangled not in members:
if analyzer and (qualname, unmangled) in analyzer.attr_docs:
# pyrefly: ignore [bad-index]
docstring = '\n'.join(analyzer.attr_docs[qualname, unmangled])
else:
docstring = None
Expand Down
2 changes: 2 additions & 0 deletions sphinx/ext/imgmath.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ def convert_dvi_to_png(dvipath: Path, out_path: Path, *, config: Config) -> int
command.append('--depth')
command.append(dvipath)

# pyrefly: ignore [bad-argument-type]
stdout, _stderr = convert_dvi_to_image(command, name)

depth = None
Expand All @@ -207,6 +208,7 @@ def convert_dvi_to_svg(dvipath: Path, out_path: Path, *, config: Config) -> int
command.extend(config.imgmath_dvisvgm_args)
command.append(dvipath)

# pyrefly: ignore [bad-argument-type]
_stdout, stderr = convert_dvi_to_image(command, name)

depth = None
Expand Down
21 changes: 21 additions & 0 deletions sphinx/ext/napoleon/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,27 @@ def __unicode__(self):

"""

napoleon_google_docstring: bool
napoleon_numpy_docstring: bool
napoleon_include_init_with_doc: bool
napoleon_include_private_with_doc: bool
napoleon_include_special_with_doc: bool
napoleon_use_admonition_for_examples: bool
napoleon_use_admonition_for_notes: bool
napoleon_use_admonition_for_references: bool
napoleon_use_ivar: bool
napoleon_use_param: bool
napoleon_use_rtype: bool
napoleon_use_keyword: bool
napoleon_preprocess_types: bool
napoleon_type_aliases: dict[str, str] | None
napoleon_custom_sections: list[str | tuple[str, str]] | None
napoleon_attr_annotations: bool

if TYPE_CHECKING:

def __getattr__(self, name: str) -> Any: ...

_config_values: Sequence[tuple[str, bool | None, _ConfigRebuild, Set[type]]] = (
('napoleon_google_docstring', True, 'env', frozenset({bool})),
('napoleon_numpy_docstring', True, 'env', frozenset({bool})),
Expand Down
3 changes: 3 additions & 0 deletions sphinx/jinja2glue.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,12 @@ def init(
self.environment.filters['toint'] = _toint
self.environment.filters['todim'] = _todim
self.environment.filters['slice_index'] = _slice_index
# pyrefly: ignore [unsupported-operation]
self.environment.globals['debug'] = pass_context(pformat)
self.environment.globals['warning'] = warning
# pyrefly: ignore [unsupported-operation]
self.environment.globals['accesskey'] = pass_context(accesskey)
# pyrefly: ignore [unsupported-operation]
self.environment.globals['idgen'] = idgen
if use_i18n:
# ``install_gettext_translations`` is injected by the ``jinja2.ext.i18n`` extension
Expand Down
2 changes: 2 additions & 0 deletions sphinx/search/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,10 +293,12 @@ def __init__(
# objtype index -> (domain, type, objname (localized))
self._objnames: dict[int, tuple[str, str, str]] = env._search_index_objnames
# add language-specific SearchLanguage instance
# pyrefly: ignore [bad-assignment]
lang_class = languages.get(lang)

# fallback; try again with language-code
if lang_class is None and '_' in lang:
# pyrefly: ignore [bad-assignment]
lang_class = languages.get(lang.partition('_')[0])

if lang_class is None:
Expand Down
1 change: 1 addition & 0 deletions sphinx/testing/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ def app_params(
# ##### prepare Application params

test_root = kwargs.pop('testroot', 'root')
# pyrefly: ignore [unsupported-operation]
kwargs['srcdir'] = srcdir = sphinx_test_tempdir / kwargs.get('srcdir', test_root)
copy_test_root = not {'srcdir', 'copy_test_root'}.isdisjoint(kwargs)

Expand Down
1 change: 1 addition & 0 deletions sphinx/transforms/references.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def apply(self, **kwargs: Any) -> None:
report_level = reporter.report_level

# suppress INFO level messages for a while
# pyrefly: ignore [bad-assignment]
reporter.report_level = max(reporter.WARNING_LEVEL, reporter.report_level)
super().apply()
finally:
Expand Down
1 change: 1 addition & 0 deletions sphinx/util/docfields.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,7 @@ def _transform_step_1(
# markup being written out
content = [n for n in content if isinstance(n, (nodes.Inline, nodes.Text))]
if content:
# pyrefly: ignore [unsupported-operation]
types.setdefault(typename, {})[fieldarg] = content
return

Expand Down
3 changes: 2 additions & 1 deletion sphinx/util/docutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ def directive(
language_module: ModuleType,
document: nodes.document,
) -> tuple[type[Directive] | None, list[system_message]]:
# pyrefly: ignore [bad-argument-type]
return self.directive_func(directive_name, language_module, document)

def role(
Expand All @@ -288,7 +289,7 @@ def role(
lineno: int,
reporter: Reporter,
) -> tuple[RoleFunction, list[system_message]]:
return self.role_func( # ty: ignore[invalid-return-type]
return self.role_func( # pyrefly: ignore[bad-return]
role_name,
language_module, # type: ignore[return-value]
lineno,
Expand Down
2 changes: 2 additions & 0 deletions sphinx/util/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -619,9 +619,11 @@ def __repr__(self) -> str:
def __or__(self, other: Any) -> Any:
# When evaluating type hints, our forward ref can appear in type expressions,
# i.e. `Alias | None`. This means it needs to support ``__or__`` and ``__ror__``.
# pyrefly: ignore [not-a-type]
return typing.Union[self, other] # NoQA: UP007

def __ror__(self, other: Any) -> Any:
# pyrefly: ignore [not-a-type]
return typing.Union[other, self] # NoQA: UP007


Expand Down
1 change: 1 addition & 0 deletions sphinx/writers/latex.py
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,7 @@ def generate(
for domain in self._domains.sorted():
for index_cls in domain.indices:
index_name = f'{domain.name}-{index_cls.name}'
# pyrefly: ignore [not-iterable]
if check_names and index_name not in indices_config:
continue
content, collapsed = index_cls(domain).generate(
Expand Down
Loading
Loading