mirror of
https://github.com/python/cpython.git
synced 2025-08-03 16:39:00 +00:00
bpo-37390: Add audit event table to documentations (GH-14406)
Also updates some (unreleased) event names to be consistent with the others.
This commit is contained in:
parent
21cfae107e
commit
44f91c388a
34 changed files with 266 additions and 113 deletions
|
@ -23,7 +23,7 @@ from docutils import nodes, utils
|
|||
from sphinx import addnodes
|
||||
from sphinx.builders import Builder
|
||||
from sphinx.locale import translators
|
||||
from sphinx.util import status_iterator
|
||||
from sphinx.util import status_iterator, logging
|
||||
from sphinx.util.nodes import split_explicit_title
|
||||
from sphinx.writers.html import HTMLTranslator
|
||||
from sphinx.writers.text import TextWriter, TextTranslator
|
||||
|
@ -157,7 +157,7 @@ class AuditEvent(Directive):
|
|||
|
||||
has_content = True
|
||||
required_arguments = 1
|
||||
optional_arguments = 1
|
||||
optional_arguments = 2
|
||||
final_argument_whitespace = True
|
||||
|
||||
_label = [
|
||||
|
@ -166,21 +166,49 @@ class AuditEvent(Directive):
|
|||
"Raises an :ref:`auditing event <auditing>` {name} with arguments {args}.",
|
||||
]
|
||||
|
||||
@property
|
||||
def logger(self):
|
||||
cls = type(self)
|
||||
return logging.getLogger(cls.__module__ + "." + cls.__name__)
|
||||
|
||||
def run(self):
|
||||
name = self.arguments[0]
|
||||
if len(self.arguments) >= 2 and self.arguments[1]:
|
||||
args = [
|
||||
"``{}``".format(a.strip())
|
||||
for a in self.arguments[1].strip("'\"").split()
|
||||
if a.strip()
|
||||
]
|
||||
args = (a.strip() for a in self.arguments[1].strip("'\"").split(","))
|
||||
args = [a for a in args if a]
|
||||
else:
|
||||
args = []
|
||||
|
||||
label = translators['sphinx'].gettext(self._label[min(2, len(args))])
|
||||
text = label.format(name="``{}``".format(self.arguments[0]),
|
||||
args=", ".join(args))
|
||||
text = label.format(name="``{}``".format(name),
|
||||
args=", ".join("``{}``".format(a) for a in args if a))
|
||||
|
||||
pnode = nodes.paragraph(text, classes=["audit-hook"])
|
||||
env = self.state.document.settings.env
|
||||
if not hasattr(env, 'all_audit_events'):
|
||||
env.all_audit_events = {}
|
||||
|
||||
new_info = {
|
||||
'source': [],
|
||||
'args': args
|
||||
}
|
||||
info = env.all_audit_events.setdefault(name, new_info)
|
||||
if info is not new_info:
|
||||
if not self._do_args_match(info['args'], new_info['args']):
|
||||
self.logger.warn(
|
||||
"Mismatched arguments for audit-event {}: {!r} != {!r}"
|
||||
.format(name, info['args'], new_info['args'])
|
||||
)
|
||||
|
||||
if len(self.arguments) >= 3 and self.arguments[2]:
|
||||
target = self.arguments[2]
|
||||
ids = []
|
||||
else:
|
||||
target = "audit_event_{}_{}".format(name, len(info['source']))
|
||||
target = re.sub(r'\W', '_', label)
|
||||
ids = [target]
|
||||
info['source'].append((env.docname, target))
|
||||
|
||||
pnode = nodes.paragraph(text, classes=["audit-hook"], ids=ids)
|
||||
if self.content:
|
||||
self.state.nested_parse(self.content, self.content_offset, pnode)
|
||||
else:
|
||||
|
@ -189,6 +217,37 @@ class AuditEvent(Directive):
|
|||
|
||||
return [pnode]
|
||||
|
||||
# This list of sets are allowable synonyms for event argument names.
|
||||
# If two names are in the same set, they are treated as equal for the
|
||||
# purposes of warning. This won't help if number of arguments is
|
||||
# different!
|
||||
_SYNONYMS = [
|
||||
{"file", "path", "fd"},
|
||||
]
|
||||
|
||||
def _do_args_match(self, args1, args2):
|
||||
if args1 == args2:
|
||||
return True
|
||||
if len(args1) != len(args2):
|
||||
return False
|
||||
for a1, a2 in zip(args1, args2):
|
||||
if a1 == a2:
|
||||
continue
|
||||
if any(a1 in s and a2 in s for s in self._SYNONYMS):
|
||||
continue
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class audit_event_list(nodes.General, nodes.Element):
|
||||
pass
|
||||
|
||||
|
||||
class AuditEventListDirective(Directive):
|
||||
|
||||
def run(self):
|
||||
return [audit_event_list('')]
|
||||
|
||||
|
||||
# Support for documenting decorators
|
||||
|
||||
|
@ -394,7 +453,7 @@ class PydocTopicsBuilder(Builder):
|
|||
'building topics... ',
|
||||
length=len(pydoc_topic_labels)):
|
||||
if label not in self.env.domaindata['std']['labels']:
|
||||
self.warn('label %r not in documentation' % label)
|
||||
self.env.logger.warn('label %r not in documentation' % label)
|
||||
continue
|
||||
docname, labelid, sectname = self.env.domaindata['std']['labels'][label]
|
||||
doctree = self.env.get_and_resolve_doctree(docname, self)
|
||||
|
@ -458,12 +517,72 @@ def parse_pdb_command(env, sig, signode):
|
|||
return fullname
|
||||
|
||||
|
||||
def process_audit_events(app, doctree, fromdocname):
|
||||
for node in doctree.traverse(audit_event_list):
|
||||
break
|
||||
else:
|
||||
return
|
||||
|
||||
env = app.builder.env
|
||||
|
||||
table = nodes.table(cols=3)
|
||||
group = nodes.tgroup(
|
||||
'',
|
||||
nodes.colspec(colwidth=30),
|
||||
nodes.colspec(colwidth=55),
|
||||
nodes.colspec(colwidth=15),
|
||||
)
|
||||
head = nodes.thead()
|
||||
body = nodes.tbody()
|
||||
|
||||
table += group
|
||||
group += head
|
||||
group += body
|
||||
|
||||
row = nodes.row()
|
||||
row += nodes.entry('', nodes.paragraph('', nodes.Text('Audit event')))
|
||||
row += nodes.entry('', nodes.paragraph('', nodes.Text('Arguments')))
|
||||
row += nodes.entry('', nodes.paragraph('', nodes.Text('References')))
|
||||
head += row
|
||||
|
||||
for name in sorted(getattr(env, "all_audit_events", ())):
|
||||
audit_event = env.all_audit_events[name]
|
||||
|
||||
row = nodes.row()
|
||||
node = nodes.paragraph('', nodes.Text(name))
|
||||
row += nodes.entry('', node)
|
||||
|
||||
node = nodes.paragraph()
|
||||
for i, a in enumerate(audit_event['args']):
|
||||
if i:
|
||||
node += nodes.Text(", ")
|
||||
node += nodes.literal(a, nodes.Text(a))
|
||||
row += nodes.entry('', node)
|
||||
|
||||
node = nodes.paragraph()
|
||||
for i, (doc, label) in enumerate(audit_event['source'], start=1):
|
||||
if isinstance(label, str):
|
||||
ref = nodes.reference("", nodes.Text("[{}]".format(i)), internal=True)
|
||||
ref['refuri'] = "{}#{}".format(
|
||||
app.builder.get_relative_uri(fromdocname, doc),
|
||||
label,
|
||||
)
|
||||
node += ref
|
||||
row += nodes.entry('', node)
|
||||
|
||||
body += row
|
||||
|
||||
for node in doctree.traverse(audit_event_list):
|
||||
node.replace_self(table)
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_role('issue', issue_role)
|
||||
app.add_role('source', source_role)
|
||||
app.add_directive('impl-detail', ImplementationDetail)
|
||||
app.add_directive('availability', Availability)
|
||||
app.add_directive('audit-event', AuditEvent)
|
||||
app.add_directive('audit-event-table', AuditEventListDirective)
|
||||
app.add_directive('deprecated-removed', DeprecatedRemoved)
|
||||
app.add_builder(PydocTopicsBuilder)
|
||||
app.add_builder(suspicious.CheckSuspiciousMarkupBuilder)
|
||||
|
@ -478,4 +597,5 @@ def setup(app):
|
|||
app.add_directive_to_domain('py', 'awaitablemethod', PyAwaitableMethod)
|
||||
app.add_directive_to_domain('py', 'abstractmethod', PyAbstractMethod)
|
||||
app.add_directive('miscnews', MiscNews)
|
||||
app.connect('doctree-resolved', process_audit_events)
|
||||
return {'version': '1.0', 'parallel_read_safe': True}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue