Skip to content

Commit 77a0d66

Browse files
authored
Extract nested function definitions (#13625)
1 parent 43c298b commit 77a0d66

File tree

12 files changed

+365
-334
lines changed

12 files changed

+365
-334
lines changed

doc/conf.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -297,14 +297,12 @@ def linkify_issues_in_changelog(
297297
) -> None:
298298
"""Linkify issue references like #123 in changelog to GitHub."""
299299
if docname == 'changes':
300+
linkified_changelog = re.sub(r'(?:PR)?#([0-9]+)\b', _linkify, source[0])
301+
source[0] = linkified_changelog
300302

301-
def linkify(match: re.Match[str]) -> str:
302-
url = 'https://github.com/sphinx-doc/sphinx/issues/' + match[1]
303-
return f'`{match[0]} <{url}>`_'
304-
305-
linkified_changelog = re.sub(r'(?:PR)?#([0-9]+)\b', linkify, source[0])
306303

307-
source[0] = linkified_changelog
304+
def _linkify(match: re.Match[str], /) -> str:
305+
return f'`{match[0]} <https://github.com/sphinx-doc/sphinx/issues/{match[1]}>`__'
308306

309307

310308
REDIRECT_TEMPLATE = """

sphinx/builders/_epub_base.py

Lines changed: 57 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -279,16 +279,6 @@ def fix_ids(self, tree: nodes.document) -> None:
279279
Some readers crash because they interpret the part as a
280280
transport protocol specification.
281281
"""
282-
283-
def update_node_id(node: Element) -> None:
284-
"""Update IDs of given *node*."""
285-
new_ids: list[str] = []
286-
for node_id in node['ids']:
287-
new_id = self.fix_fragment('', node_id)
288-
if new_id not in new_ids:
289-
new_ids.append(new_id)
290-
node['ids'] = new_ids
291-
292282
for reference in tree.findall(nodes.reference):
293283
if 'refuri' in reference:
294284
m = self.refuri_re.match(reference['refuri'])
@@ -298,66 +288,75 @@ def update_node_id(node: Element) -> None:
298288
reference['refid'] = self.fix_fragment('', reference['refid'])
299289

300290
for target in tree.findall(nodes.target):
301-
update_node_id(target)
291+
self._update_node_id(target)
302292

303293
next_node: Node = target.next_node(ascend=True)
304294
if isinstance(next_node, nodes.Element):
305-
update_node_id(next_node)
295+
self._update_node_id(next_node)
306296

307297
for desc_signature in tree.findall(addnodes.desc_signature):
308-
update_node_id(desc_signature)
298+
self._update_node_id(desc_signature)
299+
300+
def _update_node_id(self, node: Element, /) -> None:
301+
"""Update IDs of given *node*."""
302+
new_ids: list[str] = []
303+
for node_id in node['ids']:
304+
new_id = self.fix_fragment('', node_id)
305+
if new_id not in new_ids:
306+
new_ids.append(new_id)
307+
node['ids'] = new_ids
308+
309+
@staticmethod
310+
def _make_footnote_ref(doc: nodes.document, label: str) -> nodes.footnote_reference:
311+
"""Create a footnote_reference node with children"""
312+
footnote_ref = nodes.footnote_reference('[#]_')
313+
footnote_ref.append(nodes.Text(label))
314+
doc.note_autofootnote_ref(footnote_ref)
315+
return footnote_ref
316+
317+
@staticmethod
318+
def _make_footnote(doc: nodes.document, label: str, uri: str) -> nodes.footnote:
319+
"""Create a footnote node with children"""
320+
footnote = nodes.footnote(uri)
321+
para = nodes.paragraph()
322+
para.append(nodes.Text(uri))
323+
footnote.append(para)
324+
footnote.insert(0, nodes.label('', label))
325+
doc.note_autofootnote(footnote)
326+
return footnote
327+
328+
@staticmethod
329+
def _footnote_spot(tree: nodes.document) -> tuple[Element, int]:
330+
"""Find or create a spot to place footnotes.
331+
332+
The function returns the tuple (parent, index).
333+
"""
334+
# The code uses the following heuristic:
335+
# a) place them after the last existing footnote
336+
# b) place them after an (empty) Footnotes rubric
337+
# c) create an empty Footnotes rubric at the end of the document
338+
fns = list(tree.findall(nodes.footnote))
339+
if fns:
340+
fn = fns[-1]
341+
return fn.parent, fn.parent.index(fn) + 1
342+
for node in tree.findall(nodes.rubric):
343+
if len(node) == 1 and node.astext() == FOOTNOTES_RUBRIC_NAME:
344+
return node.parent, node.parent.index(node) + 1
345+
doc = next(tree.findall(nodes.document))
346+
rub = nodes.rubric()
347+
rub.append(nodes.Text(FOOTNOTES_RUBRIC_NAME))
348+
doc.append(rub)
349+
return doc, doc.index(rub) + 1
309350

310351
def add_visible_links(
311352
self, tree: nodes.document, show_urls: str = 'inline'
312353
) -> None:
313354
"""Add visible link targets for external links"""
314-
315-
def make_footnote_ref(
316-
doc: nodes.document, label: str
317-
) -> nodes.footnote_reference:
318-
"""Create a footnote_reference node with children"""
319-
footnote_ref = nodes.footnote_reference('[#]_')
320-
footnote_ref.append(nodes.Text(label))
321-
doc.note_autofootnote_ref(footnote_ref)
322-
return footnote_ref
323-
324-
def make_footnote(doc: nodes.document, label: str, uri: str) -> nodes.footnote:
325-
"""Create a footnote node with children"""
326-
footnote = nodes.footnote(uri)
327-
para = nodes.paragraph()
328-
para.append(nodes.Text(uri))
329-
footnote.append(para)
330-
footnote.insert(0, nodes.label('', label))
331-
doc.note_autofootnote(footnote)
332-
return footnote
333-
334-
def footnote_spot(tree: nodes.document) -> tuple[Element, int]:
335-
"""Find or create a spot to place footnotes.
336-
337-
The function returns the tuple (parent, index).
338-
"""
339-
# The code uses the following heuristic:
340-
# a) place them after the last existing footnote
341-
# b) place them after an (empty) Footnotes rubric
342-
# c) create an empty Footnotes rubric at the end of the document
343-
fns = list(tree.findall(nodes.footnote))
344-
if fns:
345-
fn = fns[-1]
346-
return fn.parent, fn.parent.index(fn) + 1
347-
for node in tree.findall(nodes.rubric):
348-
if len(node) == 1 and node.astext() == FOOTNOTES_RUBRIC_NAME:
349-
return node.parent, node.parent.index(node) + 1
350-
doc = next(tree.findall(nodes.document))
351-
rub = nodes.rubric()
352-
rub.append(nodes.Text(FOOTNOTES_RUBRIC_NAME))
353-
doc.append(rub)
354-
return doc, doc.index(rub) + 1
355-
356355
if show_urls == 'no':
357356
return
358357
if show_urls == 'footnote':
359358
doc = next(tree.findall(nodes.document))
360-
fn_spot, fn_idx = footnote_spot(tree)
359+
fn_spot, fn_idx = self._footnote_spot(tree)
361360
nr = 1
362361
for node in list(tree.findall(nodes.reference)):
363362
uri = node.get('refuri', '')
@@ -371,9 +370,9 @@ def footnote_spot(tree: nodes.document) -> tuple[Element, int]:
371370
elif show_urls == 'footnote':
372371
label = FOOTNOTE_LABEL_TEMPLATE % nr
373372
nr += 1
374-
footnote_ref = make_footnote_ref(doc, label)
373+
footnote_ref = self._make_footnote_ref(doc, label)
375374
node.parent.insert(idx, footnote_ref)
376-
footnote = make_footnote(doc, label, uri)
375+
footnote = self._make_footnote(doc, label, uri)
377376
fn_spot.insert(fn_idx, footnote)
378377
footnote_ref['refid'] = footnote['ids'][0]
379378
footnote.add_backref(footnote_ref['ids'][0])

sphinx/domains/c/_parser.py

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -369,10 +369,7 @@ def _parse_logical_or_expression(self) -> ASTExpression:
369369
# pm = cast .*, ->*
370370
def _parse_bin_op_expr(self: DefinitionParser, op_id: int) -> ASTExpression:
371371
if op_id + 1 == len(_expression_bin_ops):
372-
373-
def parser() -> ASTExpression:
374-
return self._parse_cast_expression()
375-
372+
parser = self._parse_cast_expression
376373
else:
377374

378375
def parser() -> ASTExpression:
@@ -760,10 +757,7 @@ def _parse_declarator_name_suffix(
760757
if self.skip_string(']'):
761758
size = None
762759
else:
763-
764-
def parser() -> ASTExpression:
765-
return self._parse_expression()
766-
760+
parser = self._parse_expression
767761
size = self._parse_expression_fallback([']'], parser)
768762
self.skip_ws()
769763
if not self.skip_string(']'):
@@ -1025,10 +1019,7 @@ def _parse_enumerator(self) -> ASTEnumerator:
10251019
init = None
10261020
if self.skip_string('='):
10271021
self.skip_ws()
1028-
1029-
def parser() -> ASTExpression:
1030-
return self._parse_constant_expression()
1031-
1022+
parser = self._parse_constant_expression
10321023
init_val = self._parse_expression_fallback([], parser)
10331024
init = ASTInitializer(init_val)
10341025
return ASTEnumerator(name, init, attrs)

sphinx/domains/c/_symbol.py

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -445,43 +445,19 @@ def on_missing_qualified_symbol(
445445
# First check if one of those with a declaration matches.
446446
# If it's a function, we need to compare IDs,
447447
# otherwise there should be only one symbol with a declaration.
448-
def make_cand_symbol() -> Symbol:
449-
if Symbol.debug_lookup:
450-
Symbol.debug_print('begin: creating candidate symbol')
451-
symbol = Symbol(
452-
parent=lookup_result.parent_symbol,
453-
ident=lookup_result.ident,
454-
declaration=declaration,
455-
docname=docname,
456-
line=line,
457-
)
458-
if Symbol.debug_lookup:
459-
Symbol.debug_print('end: creating candidate symbol')
460-
return symbol
461448

462449
if len(with_decl) == 0:
463450
cand_symbol = None
464451
else:
465-
cand_symbol = make_cand_symbol()
466-
467-
def handle_duplicate_declaration(
468-
symbol: Symbol, cand_symbol: Symbol
469-
) -> None:
470-
if Symbol.debug_lookup:
471-
Symbol.debug_indent += 1
472-
Symbol.debug_print('redeclaration')
473-
Symbol.debug_indent -= 1
474-
Symbol.debug_indent -= 2
475-
# Redeclaration of the same symbol.
476-
# Let the new one be there, but raise an error to the client
477-
# so it can use the real symbol as subscope.
478-
# This will probably result in a duplicate id warning.
479-
cand_symbol.isRedeclaration = True
480-
raise _DuplicateSymbolError(symbol, declaration)
452+
cand_symbol = self._make_cand_symbol(
453+
lookup_result, declaration, docname, line
454+
)
481455

482456
if declaration.objectType != 'function':
483457
assert len(with_decl) <= 1
484-
handle_duplicate_declaration(with_decl[0], cand_symbol)
458+
self._handle_duplicate_declaration(
459+
with_decl[0], cand_symbol, declaration
460+
)
485461
# (not reachable)
486462

487463
# a function, so compare IDs
@@ -493,7 +469,7 @@ def handle_duplicate_declaration(
493469
if Symbol.debug_lookup:
494470
Symbol.debug_print('old_id: ', old_id)
495471
if cand_id == old_id:
496-
handle_duplicate_declaration(symbol, cand_symbol)
472+
self._handle_duplicate_declaration(symbol, cand_symbol, declaration)
497473
# (not reachable)
498474
# no candidate symbol found with matching ID
499475
# if there is an empty symbol, fill that one
@@ -507,7 +483,7 @@ def handle_duplicate_declaration(
507483
if cand_symbol is not None:
508484
return cand_symbol
509485
else:
510-
return make_cand_symbol()
486+
return self._make_cand_symbol(lookup_result, declaration, docname, line)
511487
else:
512488
if Symbol.debug_lookup:
513489
Symbol.debug_print(
@@ -529,6 +505,42 @@ def handle_duplicate_declaration(
529505
symbol._fill_empty(declaration, docname, line)
530506
return symbol
531507

508+
@staticmethod
509+
def _make_cand_symbol(
510+
lookup_result: SymbolLookupResult,
511+
declaration: ASTDeclaration | None,
512+
docname: str | None,
513+
line: int | None,
514+
) -> Symbol:
515+
if Symbol.debug_lookup:
516+
Symbol.debug_print('begin: creating candidate symbol')
517+
symbol = Symbol(
518+
parent=lookup_result.parent_symbol,
519+
ident=lookup_result.ident,
520+
declaration=declaration,
521+
docname=docname,
522+
line=line,
523+
)
524+
if Symbol.debug_lookup:
525+
Symbol.debug_print('end: creating candidate symbol')
526+
return symbol
527+
528+
@staticmethod
529+
def _handle_duplicate_declaration(
530+
symbol: Symbol, cand_symbol: Symbol, declaration: ASTDeclaration
531+
) -> None:
532+
if Symbol.debug_lookup:
533+
Symbol.debug_indent += 1
534+
Symbol.debug_print('redeclaration')
535+
Symbol.debug_indent -= 1
536+
Symbol.debug_indent -= 2
537+
# Redeclaration of the same symbol.
538+
# Let the new one be there, but raise an error to the client
539+
# so it can use the real symbol as subscope.
540+
# This will probably result in a duplicate id warning.
541+
cand_symbol.isRedeclaration = True
542+
raise _DuplicateSymbolError(symbol, declaration)
543+
532544
def merge_with(
533545
self, other: Symbol, docnames: list[str], env: BuildEnvironment
534546
) -> None:

sphinx/domains/cpp/__init__.py

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,6 +1056,15 @@ def merge_domaindata(self, docnames: Set[str], otherdata: dict[str, Any]) -> Non
10561056
logger.debug('\tresult end')
10571057
logger.debug('merge_domaindata end')
10581058

1059+
def _check_type(self, typ: str, decl_typ: str) -> bool:
1060+
if typ == 'any':
1061+
return True
1062+
objtypes = self.objtypes_for_role(typ)
1063+
if objtypes:
1064+
return decl_typ in objtypes
1065+
logger.debug(f'Type is {typ}, declaration type is {decl_typ}') # NoQA: G004
1066+
raise AssertionError
1067+
10591068
def _resolve_xref_inner(
10601069
self,
10611070
env: BuildEnvironment,
@@ -1150,16 +1159,7 @@ def _resolve_xref_inner(
11501159
typ = typ.removeprefix('cpp:')
11511160
decl_typ = s.declaration.objectType
11521161

1153-
def check_type() -> bool:
1154-
if typ == 'any':
1155-
return True
1156-
objtypes = self.objtypes_for_role(typ)
1157-
if objtypes:
1158-
return decl_typ in objtypes
1159-
logger.debug(f'Type is {typ}, declaration type is {decl_typ}') # NoQA: G004
1160-
raise AssertionError
1161-
1162-
if not check_type():
1162+
if not self._check_type(typ, decl_typ):
11631163
logger.warning(
11641164
'cpp:%s targets a %s (%s).',
11651165
typ,
@@ -1299,6 +1299,12 @@ def get_full_qualified_name(self, node: Element) -> str | None:
12991299
return f'{parent_name}::{target}'
13001300

13011301

1302+
def _init_stuff(app: Sphinx) -> None:
1303+
Symbol.debug_lookup = app.config.cpp_debug_lookup
1304+
Symbol.debug_show_tree = app.config.cpp_debug_show_tree
1305+
app.config.cpp_index_common_prefix.sort(reverse=True)
1306+
1307+
13021308
def setup(app: Sphinx) -> ExtensionMetadata:
13031309
app.add_domain(CPPDomain)
13041310
app.add_config_value('cpp_index_common_prefix', [], 'env', types=frozenset({list}))
@@ -1318,12 +1324,7 @@ def setup(app: Sphinx) -> ExtensionMetadata:
13181324
app.add_config_value('cpp_debug_lookup', False, '', types=frozenset({bool}))
13191325
app.add_config_value('cpp_debug_show_tree', False, '', types=frozenset({bool}))
13201326

1321-
def init_stuff(app: Sphinx) -> None:
1322-
Symbol.debug_lookup = app.config.cpp_debug_lookup
1323-
Symbol.debug_show_tree = app.config.cpp_debug_show_tree
1324-
app.config.cpp_index_common_prefix.sort(reverse=True)
1325-
1326-
app.connect('builder-inited', init_stuff)
1327+
app.connect('builder-inited', _init_stuff)
13271328

13281329
return {
13291330
'version': 'builtin',

0 commit comments

Comments
 (0)