Make docstring style consistent

Use PEP 257 indented docstring style, mostly: always with """, with the
terminating """ on a separate line if the docstring is more than one
line, and with all lines indented to the opening """.

This commit does not change the text to keep the first paragraph single-line.
This commit is contained in:
Gilles Peskine 2019-06-03 11:23:56 +02:00
parent 54f544581a
commit a3b93ff893
2 changed files with 62 additions and 44 deletions

View file

@ -187,9 +187,9 @@ BIT_TEST_TEMPLATE = '''\
class MacroCollector: class MacroCollector:
"""Collect PSA crypto macro definitions from C header files. """Collect PSA crypto macro definitions from C header files.
1. Call `read_file` on the input header file(s). 1. Call `read_file` on the input header file(s).
2. Call `write_file` to write ``psa_constant_names_generated.c``. 2. Call `write_file` to write ``psa_constant_names_generated.c``.
""" """
def __init__(self): def __init__(self):
self.statuses = set() self.statuses = set()
@ -212,7 +212,8 @@ class MacroCollector:
def read_line(self, line): def read_line(self, line):
"""Parse a C header line and record the PSA identifier it defines if any. """Parse a C header line and record the PSA identifier it defines if any.
This function analyzes lines that start with "#define PSA_" This function analyzes lines that start with "#define PSA_"
(up to non-significant whitespace) and skips all non-matching lines.""" (up to non-significant whitespace) and skips all non-matching lines.
"""
# pylint: disable=too-many-branches # pylint: disable=too-many-branches
m = re.match(self.definition_re, line) m = re.match(self.definition_re, line)
if not m: if not m:
@ -356,7 +357,8 @@ class MacroCollector:
def write_file(self, output_file): def write_file(self, output_file):
"""Generate the pretty-printer function code from the gathered """Generate the pretty-printer function code from the gathered
constant definitions.""" constant definitions.
"""
data = {} data = {}
data['status_cases'] = self._make_status_cases() data['status_cases'] = self._make_status_cases()
data['ecc_curve_cases'] = self._make_ecc_curve_cases() data['ecc_curve_cases'] = self._make_ecc_curve_cases()

View file

@ -1,10 +1,11 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
'''Test the program psa_constant_names. """Test the program psa_constant_names.
Gather constant names from header files and test cases. Compile a C program Gather constant names from header files and test cases. Compile a C program
to print out their numerical values, feed these numerical values to to print out their numerical values, feed these numerical values to
psa_constant_names, and check that the output is the original name. psa_constant_names, and check that the output is the original name.
Return 0 if all test cases pass, 1 if the output was not always as expected, Return 0 if all test cases pass, 1 if the output was not always as expected,
or 1 (with a Python backtrace) if there was an operational error.''' or 1 (with a Python backtrace) if there was an operational error.
"""
import argparse import argparse
import itertools import itertools
@ -25,16 +26,22 @@ class ReadFileLineException(Exception):
class read_file_lines: class read_file_lines:
# Dear Pylint, conventionally, a context manager class name is lowercase. # Dear Pylint, conventionally, a context manager class name is lowercase.
# pylint: disable=invalid-name,too-few-public-methods # pylint: disable=invalid-name,too-few-public-methods
'''Context manager to read a text file line by line. """Context manager to read a text file line by line.
with read_file_lines(filename) as lines:
for line in lines: ```
process(line) with read_file_lines(filename) as lines:
is equivalent to for line in lines:
with open(filename, 'r') as input_file: process(line)
for line in input_file: ```
process(line) is equivalent to
except that if process(line) raises an exception, then the read_file_lines ```
snippet annotates the exception with the file name and line number.''' with open(filename, 'r') as input_file:
for line in input_file:
process(line)
```
except that if process(line) raises an exception, then the read_file_lines
snippet annotates the exception with the file name and line number.
"""
def __init__(self, filename): def __init__(self, filename):
self.filename = filename self.filename = filename
self.line_number = 'entry' self.line_number = 'entry'
@ -53,9 +60,11 @@ snippet annotates the exception with the file name and line number.'''
from exc_value from exc_value
class Inputs: class Inputs:
'''Accumulate information about macros to test. """Accumulate information about macros to test.
This includes macro names as well as information about their arguments This includes macro names as well as information about their arguments
when applicable.''' when applicable.
"""
def __init__(self): def __init__(self):
# Sets of names per type # Sets of names per type
self.statuses = set(['PSA_SUCCESS']) self.statuses = set(['PSA_SUCCESS'])
@ -91,8 +100,9 @@ when applicable.'''
} }
def gather_arguments(self): def gather_arguments(self):
'''Populate the list of values for macro arguments. """Populate the list of values for macro arguments.
Call this after parsing all the inputs.''' Call this after parsing all the inputs.
"""
self.arguments_for['hash_alg'] = sorted(self.hash_algorithms) self.arguments_for['hash_alg'] = sorted(self.hash_algorithms)
self.arguments_for['mac_alg'] = sorted(self.mac_algorithms) self.arguments_for['mac_alg'] = sorted(self.mac_algorithms)
self.arguments_for['ka_alg'] = sorted(self.ka_algorithms) self.arguments_for['ka_alg'] = sorted(self.ka_algorithms)
@ -103,14 +113,16 @@ Call this after parsing all the inputs.'''
@staticmethod @staticmethod
def _format_arguments(name, arguments): def _format_arguments(name, arguments):
'''Format a macro call with arguments..''' """Format a macro call with arguments.."""
return name + '(' + ', '.join(arguments) + ')' return name + '(' + ', '.join(arguments) + ')'
def distribute_arguments(self, name): def distribute_arguments(self, name):
'''Generate macro calls with each tested argument set. """Generate macro calls with each tested argument set.
If name is a macro without arguments, just yield "name". If name is a macro without arguments, just yield "name".
If name is a macro with arguments, yield a series of "name(arg1,...,argN)" If name is a macro with arguments, yield a series of
where each argument takes each possible value at least once.''' "name(arg1,...,argN)" where each argument takes each possible
value at least once.
"""
try: try:
if name not in self.argspecs: if name not in self.argspecs:
yield name yield name
@ -160,7 +172,7 @@ where each argument takes each possible value at least once.'''
]) ])
def parse_header_line(self, line): def parse_header_line(self, line):
'''Parse a C header line, looking for "#define PSA_xxx".''' """Parse a C header line, looking for "#define PSA_xxx"."""
m = re.match(self._header_line_re, line) m = re.match(self._header_line_re, line)
if not m: if not m:
return return
@ -176,13 +188,13 @@ where each argument takes each possible value at least once.'''
self.argspecs[name] = self._argument_split(m.group(3)) self.argspecs[name] = self._argument_split(m.group(3))
def parse_header(self, filename): def parse_header(self, filename):
'''Parse a C header file, looking for "#define PSA_xxx".''' """Parse a C header file, looking for "#define PSA_xxx"."""
with read_file_lines(filename) as lines: with read_file_lines(filename) as lines:
for line in lines: for line in lines:
self.parse_header_line(line) self.parse_header_line(line)
def add_test_case_line(self, function, argument): def add_test_case_line(self, function, argument):
'''Parse a test case data line, looking for algorithm metadata tests.''' """Parse a test case data line, looking for algorithm metadata tests."""
if function.endswith('_algorithm'): if function.endswith('_algorithm'):
# As above, ECDH and FFDH algorithms are excluded for now. # As above, ECDH and FFDH algorithms are excluded for now.
# Support for them will be added in the future. # Support for them will be added in the future.
@ -207,7 +219,7 @@ where each argument takes each possible value at least once.'''
# regex is good enough in practice. # regex is good enough in practice.
_test_case_line_re = re.compile(r'(?!depends_on:)(\w+):([^\n :][^:\n]*)') _test_case_line_re = re.compile(r'(?!depends_on:)(\w+):([^\n :][^:\n]*)')
def parse_test_cases(self, filename): def parse_test_cases(self, filename):
'''Parse a test case file (*.data), looking for algorithm metadata tests.''' """Parse a test case file (*.data), looking for algorithm metadata tests."""
with read_file_lines(filename) as lines: with read_file_lines(filename) as lines:
for line in lines: for line in lines:
m = re.match(self._test_case_line_re, line) m = re.match(self._test_case_line_re, line)
@ -215,7 +227,7 @@ where each argument takes each possible value at least once.'''
self.add_test_case_line(m.group(1), m.group(2)) self.add_test_case_line(m.group(1), m.group(2))
def gather_inputs(headers, test_suites): def gather_inputs(headers, test_suites):
'''Read the list of inputs to test psa_constant_names with.''' """Read the list of inputs to test psa_constant_names with."""
inputs = Inputs() inputs = Inputs()
for header in headers: for header in headers:
inputs.parse_header(header) inputs.parse_header(header)
@ -225,7 +237,7 @@ def gather_inputs(headers, test_suites):
return inputs return inputs
def remove_file_if_exists(filename): def remove_file_if_exists(filename):
'''Remove the specified file, ignoring errors.''' """Remove the specified file, ignoring errors."""
if not filename: if not filename:
return return
try: try:
@ -234,7 +246,7 @@ def remove_file_if_exists(filename):
pass pass
def run_c(options, type_word, names): def run_c(options, type_word, names):
'''Generate and run a program to print out numerical values for names.''' """Generate and run a program to print out numerical values for names."""
if type_word == 'status': if type_word == 'status':
cast_to = 'long' cast_to = 'long'
printf_format = '%ld' printf_format = '%ld'
@ -282,15 +294,18 @@ int main(void)
NORMALIZE_STRIP_RE = re.compile(r'\s+') NORMALIZE_STRIP_RE = re.compile(r'\s+')
def normalize(expr): def normalize(expr):
'''Normalize the C expression so as not to care about trivial differences. """Normalize the C expression so as not to care about trivial differences.
Currently "trivial differences" means whitespace.''' Currently "trivial differences" means whitespace.
"""
expr = re.sub(NORMALIZE_STRIP_RE, '', expr, len(expr)) expr = re.sub(NORMALIZE_STRIP_RE, '', expr, len(expr))
return expr.strip().split('\n') return expr.strip().split('\n')
def do_test(options, inputs, type_word, names): def do_test(options, inputs, type_word, names):
'''Test psa_constant_names for the specified type. """Test psa_constant_names for the specified type.
Run program on names. Run program on names.
Use inputs to figure out what arguments to pass to macros that take arguments.''' Use inputs to figure out what arguments to pass to macros that
take arguments.
"""
names = sorted(itertools.chain(*map(inputs.distribute_arguments, names))) names = sorted(itertools.chain(*map(inputs.distribute_arguments, names)))
values = run_c(options, type_word, names) values = run_c(options, type_word, names)
output = subprocess.check_output([options.program, type_word] + values) output = subprocess.check_output([options.program, type_word] + values)
@ -301,16 +316,17 @@ Use inputs to figure out what arguments to pass to macros that take arguments.''
return len(names), errors return len(names), errors
def report_errors(errors): def report_errors(errors):
'''Describe each case where the output is not as expected.''' """Describe each case where the output is not as expected."""
for type_word, name, value, output in errors: for type_word, name, value, output in errors:
print('For {} "{}", got "{}" (value: {})' print('For {} "{}", got "{}" (value: {})'
.format(type_word, name, output, value)) .format(type_word, name, output, value))
def run_tests(options, inputs): def run_tests(options, inputs):
'''Run psa_constant_names on all the gathered inputs. """Run psa_constant_names on all the gathered inputs.
Return a tuple (count, errors) where count is the total number of inputs Return a tuple (count, errors) where count is the total number of inputs
that were tested and errors is the list of cases where the output was that were tested and errors is the list of cases where the output was
not as expected.''' not as expected.
"""
count = 0 count = 0
errors = [] errors = []
for type_word, names in [('status', inputs.statuses), for type_word, names in [('status', inputs.statuses),