aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYilei Yang <yileiyang@google.com>2022-08-19 09:41:30 -0700
committerCopybara-Service <copybara-worker@google.com>2022-08-19 09:41:55 -0700
commit3e411327e50766b3108674f637fd8ad895fb6e62 (patch)
tree8e6c89850cf0a0d8bb5e092aa4dd053dc0a35be1
parent7301e45246607c72541ddcdbceaf3257c0c510d9 (diff)
downloadabsl-py-3e411327e50766b3108674f637fd8ad895fb6e62.tar.gz
Update some of the docstrings to be compatible with `reST`, in preparation to add generated docs by Sphinx/readthedocs.
This is just an initial pass, far from great documentations. PiperOrigin-RevId: 468721475 Change-Id: I5b4f9b2127add933d7eb56414b4fa1751cdacb3d
-rw-r--r--absl/app.py14
-rw-r--r--absl/flags/argparse_flags.py28
-rw-r--r--absl/logging/__init__.py84
-rw-r--r--absl/logging/converter.py51
-rw-r--r--absl/testing/absltest.py139
-rw-r--r--absl/testing/flagsaver.py76
-rw-r--r--absl/testing/parameterized.py214
7 files changed, 315 insertions, 291 deletions
diff --git a/absl/app.py b/absl/app.py
index 196a951..43d8ca3 100644
--- a/absl/app.py
+++ b/absl/app.py
@@ -14,8 +14,8 @@
"""Generic entry point for Abseil Python applications.
-To use this module, define a 'main' function with a single 'argv' argument and
-call app.run(main). For example:
+To use this module, define a ``main`` function with a single ``argv`` argument
+and call ``app.run(main)``. For example::
def main(argv):
if len(argv) > 1:
@@ -336,14 +336,14 @@ _init_callbacks = collections.deque()
def call_after_init(callback):
"""Calls the given callback only once ABSL has finished initialization.
- If ABSL has already finished initialization when `call_after_init` is
+ If ABSL has already finished initialization when ``call_after_init`` is
called then the callback is executed immediately, otherwise `callback` is
- stored to be executed after `app.run` has finished initializing (aka. just
+ stored to be executed after ``app.run`` has finished initializing (aka. just
before the main function is called).
- If called after `app.run`, this is equivalent to calling `callback()` in the
- caller thread. If called before `app.run`, callbacks are run sequentially (in
- an undefined order) in the same thread as `app.run`.
+ If called after ``app.run``, this is equivalent to calling ``callback()`` in
+ the caller thread. If called before ``app.run``, callbacks are run
+ sequentially (in an undefined order) in the same thread as ``app.run``.
Args:
callback: a callable to be called once ABSL has finished initialization.
diff --git a/absl/flags/argparse_flags.py b/absl/flags/argparse_flags.py
index 69e4c8a..dd8b505 100644
--- a/absl/flags/argparse_flags.py
+++ b/absl/flags/argparse_flags.py
@@ -14,11 +14,11 @@
"""This module provides argparse integration with absl.flags.
-argparse_flags.ArgumentParser is a drop-in replacement for
-argparse.ArgumentParser. It takes care of collecting and defining absl flags
-in argparse.
+``argparse_flags.ArgumentParser`` is a drop-in replacement for
+:class:`argparse.ArgumentParser`. It takes care of collecting and defining absl
+flags in :mod:`argparse`.
-Here is a simple example:
+Here is a simple example::
# Assume the following absl.flags is defined in another module:
#
@@ -40,7 +40,7 @@ Here is a simple example:
# ./program --header 'A header' --echo 'A message.'
-Here is another example demonstrates subparsers:
+Here is another example demonstrates subparsers::
parser = argparse_flags.ArgumentParser(description='A subcommands demo.')
parser.add_argument('--header', help='The header message to print.')
@@ -69,23 +69,25 @@ Here is another example demonstrates subparsers:
# ./program shuffle --echo='A message.' 1 2 3 4
-There are several differences between absl.flags and argparse_flags:
+There are several differences between :mod:`absl.flags` and
+:mod:`~absl.flags.argparse_flags`:
1. Flags defined with absl.flags are parsed differently when using the
argparse parser. Notably:
1) absl.flags allows both single-dash and double-dash for any flag, and
doesn't distinguish them; argparse_flags only allows double-dash for
- flag's regular name, and single-dash for flag's `short_name`.
- 2) Boolean flags in absl.flags can be specified with `--bool`, `--nobool`,
- as well as `--bool=true/false` (though not recommended);
- in argparse_flags, it only allows `--bool`, `--nobool`.
+ flag's regular name, and single-dash for flag's ``short_name``.
+ 2) Boolean flags in absl.flags can be specified with ``--bool``,
+ ``--nobool``, as well as ``--bool=true/false`` (though not recommended);
+ in argparse_flags, it only allows ``--bool``, ``--nobool``.
2. Help related flag differences:
+
1) absl.flags does not define help flags, absl.app does that; argparse_flags
- defines help flags unless passed with `add_help=False`.
- 2) absl.app supports `--helpxml`; argparse_flags does not.
- 3) argparse_flags supports `-h`; absl.app does not.
+ defines help flags unless passed with ``add_help=False``.
+ 2) absl.app supports ``--helpxml``; argparse_flags does not.
+ 3) argparse_flags supports ``-h``; absl.app does not.
"""
import argparse
diff --git a/absl/logging/__init__.py b/absl/logging/__init__.py
index 7745ba3..c0ba4b0 100644
--- a/absl/logging/__init__.py
+++ b/absl/logging/__init__.py
@@ -14,7 +14,7 @@
"""Abseil Python logging module implemented on top of standard logging.
-Simple usage:
+Simple usage::
from absl import logging
@@ -34,29 +34,34 @@ Usage note: Do not pre-format the strings in your program code.
Instead, let the logging module perform argument interpolation.
This saves cycles because strings that don't need to be printed
are never formatted. Note that this module does not attempt to
-interpolate arguments when no arguments are given. In other words
+interpolate arguments when no arguments are given. In other words::
logging.info('Interesting Stuff: %s')
does not raise an exception because logging.info() has only one
argument, the message string.
-"Lazy" evaluation for debugging:
+"Lazy" evaluation for debugging
+-------------------------------
+
+If you do something like this::
-If you do something like this:
logging.debug('Thing: %s', thing.ExpensiveOp())
+
then the ExpensiveOp will be evaluated even if nothing
-is printed to the log. To avoid this, use the level_debug() function:
+is printed to the log. To avoid this, use the level_debug() function::
+
if logging.level_debug():
logging.debug('Thing: %s', thing.ExpensiveOp())
Per file level logging is supported by logging.vlog() and
-logging.vlog_is_on(). For example:
+logging.vlog_is_on(). For example::
if logging.vlog_is_on(2):
logging.vlog(2, very_expensive_debug_message())
-Notes on Unicode:
+Notes on Unicode
+----------------
The log output is encoded as UTF-8. Don't pass data in other encodings in
bytes() instances -- instead pass unicode string instances when you need to
@@ -432,7 +437,7 @@ def _get_next_log_count_per_token(token):
def log_every_n(level, msg, n, *args):
- """Logs 'msg % args' at level 'level' once per 'n' times.
+ """Logs ``msg % args`` at level 'level' once per 'n' times.
Logs the 1st call, (N+1)st call, (2N+1)st call, etc.
Not threadsafe.
@@ -479,7 +484,7 @@ def _seconds_have_elapsed(token, num_seconds):
def log_every_n_seconds(level, msg, n_seconds, *args):
- """Logs 'msg % args' at level 'level' iff 'n_seconds' elapsed since last call.
+ """Logs ``msg % args`` at level ``level`` iff ``n_seconds`` elapsed since last call.
Logs the first call, logs subsequent calls if 'n' seconds have elapsed since
the last logging call from the same call site (file + line). Not thread-safe.
@@ -495,7 +500,7 @@ def log_every_n_seconds(level, msg, n_seconds, *args):
def log_first_n(level, msg, n, *args):
- """Logs 'msg % args' at level 'level' only first 'n' times.
+ """Logs ``msg % args`` at level ``level`` only first ``n`` times.
Not threadsafe.
@@ -510,13 +515,13 @@ def log_first_n(level, msg, n, *args):
def log_if(level, msg, condition, *args):
- """Logs 'msg % args' at level 'level' only if condition is fulfilled."""
+ """Logs ``msg % args`` at level ``level`` only if condition is fulfilled."""
if condition:
log(level, msg, *args)
def log(level, msg, *args, **kwargs):
- """Logs 'msg % args' at absl logging level 'level'.
+ """Logs ``msg % args`` at absl logging level ``level``.
If no args are given just print msg, ignoring any interpolation specifiers.
@@ -550,7 +555,7 @@ def log(level, msg, *args, **kwargs):
def vlog(level, msg, *args, **kwargs):
- """Log 'msg % args' at C++ vlog level 'level'.
+ """Log ``msg % args`` at C++ vlog level ``level``.
Args:
level: int, the C++ verbose logging level at which to log the message,
@@ -645,13 +650,13 @@ def find_log_dir_and_names(program_name=None, log_dir=None):
Args:
program_name: str|None, the filename part of the path to the program that
is running without its extension. e.g: if your program is called
- 'usr/bin/foobar.py' this method should probably be called with
- program_name='foobar' However, this is just a convention, you can
+ ``usr/bin/foobar.py`` this method should probably be called with
+ ``program_name='foobar`` However, this is just a convention, you can
pass in any string you want, and it will be used as part of the
log filename. If you don't pass in anything, the default behavior
is as described in the example. In python standard logging mode,
- the program_name will be prepended with py_ if it is the program_name
- argument is omitted.
+ the program_name will be prepended with ``py_`` if it is the
+ ``program_name`` argument is omitted.
log_dir: str|None, the desired log directory.
Returns:
@@ -753,10 +758,10 @@ def get_absl_log_prefix(record):
def skip_log_prefix(func):
- """Skips reporting the prefix of a given function or name by ABSLLogger.
+ """Skips reporting the prefix of a given function or name by :class:`~absl.logging.ABSLLogger`.
This is a convenience wrapper function / decorator for
- `ABSLLogger.register_frame_to_skip`.
+ :meth:`~absl.logging.ABSLLogger.register_frame_to_skip`.
If a callable function is provided, only that function will be skipped.
If a function name is provided, all functions with the same name in the
@@ -880,13 +885,13 @@ class PythonHandler(logging.StreamHandler):
def emit(self, record):
"""Prints a record out to some streams.
- If FLAGS.logtostderr is set, it will print to sys.stderr ONLY.
- If FLAGS.alsologtostderr is set, it will print to sys.stderr.
- If FLAGS.logtostderr is not set, it will log to the stream
- associated with the current thread.
+ 1. If ``FLAGS.logtostderr`` is set, it will print to ``sys.stderr`` ONLY.
+ 2. If ``FLAGS.alsologtostderr`` is set, it will print to ``sys.stderr``.
+ 3. If ``FLAGS.logtostderr`` is not set, it will log to the stream
+ associated with the current thread.
Args:
- record: logging.LogRecord, the record to emit.
+ record: :class:`logging.LogRecord`, the record to emit.
"""
# People occasionally call logging functions at import time before
# our flags may have even been defined yet, let alone even parsed, as we
@@ -987,7 +992,7 @@ class ABSLHandler(logging.Handler):
class PythonFormatter(logging.Formatter):
- """Formatter class used by PythonHandler."""
+ """Formatter class used by :class:`~absl.logging.PythonHandler`."""
def format(self, record):
"""Appends the message from the record to the results of the prefix.
@@ -1065,33 +1070,33 @@ class ABSLLogger(logging.getLoggerClass()):
frame = frame.f_back
def critical(self, msg, *args, **kwargs):
- """Logs 'msg % args' with severity 'CRITICAL'."""
+ """Logs ``msg % args`` with severity ``CRITICAL``."""
self.log(logging.CRITICAL, msg, *args, **kwargs)
def fatal(self, msg, *args, **kwargs):
- """Logs 'msg % args' with severity 'FATAL'."""
+ """Logs ``msg % args`` with severity ``FATAL``."""
self.log(logging.FATAL, msg, *args, **kwargs)
def error(self, msg, *args, **kwargs):
- """Logs 'msg % args' with severity 'ERROR'."""
+ """Logs ``msg % args`` with severity ``ERROR``."""
self.log(logging.ERROR, msg, *args, **kwargs)
def warn(self, msg, *args, **kwargs):
- """Logs 'msg % args' with severity 'WARN'."""
+ """Logs ``msg % args`` with severity ``WARN``."""
warnings.warn("The 'warn' method is deprecated, use 'warning' instead",
DeprecationWarning, 2)
self.log(logging.WARN, msg, *args, **kwargs)
def warning(self, msg, *args, **kwargs):
- """Logs 'msg % args' with severity 'WARNING'."""
+ """Logs ``msg % args`` with severity ``WARNING``."""
self.log(logging.WARNING, msg, *args, **kwargs)
def info(self, msg, *args, **kwargs):
- """Logs 'msg % args' with severity 'INFO'."""
+ """Logs ``msg % args`` with severity ``INFO``."""
self.log(logging.INFO, msg, *args, **kwargs)
def debug(self, msg, *args, **kwargs):
- """Logs 'msg % args' with severity 'DEBUG'."""
+ """Logs ``msg % args`` with severity ``DEBUG``."""
self.log(logging.DEBUG, msg, *args, **kwargs)
def log(self, level, msg, *args, **kwargs):
@@ -1114,12 +1119,12 @@ class ABSLLogger(logging.getLoggerClass()):
super(ABSLLogger, self).log(level, msg, *args, **kwargs)
def handle(self, record):
- """Calls handlers without checking Logger.disabled.
+ """Calls handlers without checking ``Logger.disabled``.
- Non-root loggers are set to disabled after setup with logging.config if
- it's not explicitly specified. Historically, absl logging will not be
+ Non-root loggers are set to disabled after setup with :func:`logging.config`
+ if it's not explicitly specified. Historically, absl logging will not be
disabled by that. To maintaining this behavior, this function skips
- checking the Logger.disabled bit.
+ checking the ``Logger.disabled`` bit.
This logger can still be disabled by adding a filter that filters out
everything.
@@ -1134,8 +1139,8 @@ class ABSLLogger(logging.getLoggerClass()):
def register_frame_to_skip(cls, file_name, function_name, line_number=None):
"""Registers a function name to skip when walking the stack.
- The ABSLLogger sometimes skips method calls on the stack
- to make the log messages meaningful in their appropriate context.
+ The :class:`~absl.logging.ABSLLogger` sometimes skips method calls on the
+ stack to make the log messages meaningful in their appropriate context.
This method registers a function from a particular file as one
which should be skipped.
@@ -1193,7 +1198,8 @@ _attempted_to_remove_stderr_stream_handlers = False
def use_absl_handler():
"""Uses the ABSL logging handler for logging.
- This method is called in app.run() so the absl handler is used in absl apps.
+ This method is called in :func:`app.run()<absl.app.run>` so the absl handler
+ is used in absl apps.
"""
global _attempted_to_remove_stderr_stream_handlers
if not _attempted_to_remove_stderr_stream_handlers:
diff --git a/absl/logging/converter.py b/absl/logging/converter.py
index 53dd46d..0239ab4 100644
--- a/absl/logging/converter.py
+++ b/absl/logging/converter.py
@@ -16,25 +16,26 @@
This converter has to convert (best effort) between three different
logging level schemes:
- cpp = The C++ logging level scheme used in Abseil C++.
- absl = The absl.logging level scheme used in Abseil Python.
- standard = The python standard library logging level scheme.
-
-Here is a handy ascii chart for easy mental mapping.
-
- LEVEL | cpp | absl | standard |
- ---------+-----+--------+----------+
- DEBUG | 0 | 1 | 10 |
- INFO | 0 | 0 | 20 |
- WARNING | 1 | -1 | 30 |
- ERROR | 2 | -2 | 40 |
- CRITICAL | 3 | -3 | 50 |
- FATAL | 3 | -3 | 50 |
-
-Note: standard logging CRITICAL is mapped to absl/cpp FATAL.
-However, only CRITICAL logs from the absl logger (or absl.logging.fatal) will
-terminate the program. CRITICAL logs from non-absl loggers are treated as
-error logs with a message prefix "CRITICAL - ".
+
+ * **cpp**: The C++ logging level scheme used in Abseil C++.
+ * **absl**: The absl.logging level scheme used in Abseil Python.
+ * **standard**: The python standard library logging level scheme.
+
+Here is a handy ascii chart for easy mental mapping::
+
+ LEVEL | cpp | absl | standard |
+ ---------+-----+--------+----------+
+ DEBUG | 0 | 1 | 10 |
+ INFO | 0 | 0 | 20 |
+ WARNING | 1 | -1 | 30 |
+ ERROR | 2 | -2 | 40 |
+ CRITICAL | 3 | -3 | 50 |
+ FATAL | 3 | -3 | 50 |
+
+Note: standard logging ``CRITICAL`` is mapped to absl/cpp ``FATAL``.
+However, only ``CRITICAL`` logs from the absl logger (or absl.logging.fatal)
+will terminate the program. ``CRITICAL`` logs from non-absl loggers are treated
+as error logs with a message prefix ``"CRITICAL - "``.
Converting from standard to absl or cpp is a lossy conversion.
Converting back to standard will lose granularity. For this reason,
@@ -89,10 +90,11 @@ def get_initial_for_level(level):
"""Gets the initial that should start the log line for the given level.
It returns:
- - 'I' when: level < STANDARD_WARNING.
- - 'W' when: STANDARD_WARNING <= level < STANDARD_ERROR.
- - 'E' when: STANDARD_ERROR <= level < STANDARD_CRITICAL.
- - 'F' when: level >= STANDARD_CRITICAL.
+
+ * ``'I'`` when: ``level < STANDARD_WARNING``.
+ * ``'W'`` when: ``STANDARD_WARNING <= level < STANDARD_ERROR``.
+ * ``'E'`` when: ``STANDARD_ERROR <= level < STANDARD_CRITICAL``.
+ * ``'F'`` when: ``level >= STANDARD_CRITICAL``.
Args:
level: int, a Python standard logging level.
@@ -157,7 +159,8 @@ def string_to_standard(level):
"""Converts a string level to standard logging level value.
Args:
- level: str, case-insensitive 'debug', 'info', 'warning', 'error', 'fatal'.
+ level: str, case-insensitive ``'debug'``, ``'info'``, ``'warning'``,
+ ``'error'``, ``'fatal'``.
Returns:
The corresponding integer level for use in standard logging.
diff --git a/absl/testing/absltest.py b/absl/testing/absltest.py
index b7a941b..0620504 100644
--- a/absl/testing/absltest.py
+++ b/absl/testing/absltest.py
@@ -103,10 +103,11 @@ __unittest = True # pylint: disable=invalid-name
def expectedFailureIf(condition, reason): # pylint: disable=invalid-name
"""Expects the test to fail if the run condition is True.
- Example usage:
- @expectedFailureIf(sys.version.major == 2, "Not yet working in py2")
- def test_foo(self):
- ...
+ Example usage::
+
+ @expectedFailureIf(sys.version.major == 2, "Not yet working in py2")
+ def test_foo(self):
+ ...
Args:
condition: bool, whether to expect failure or not.
@@ -623,7 +624,7 @@ class TestCase(unittest.TestCase):
This creates a named directory on disk that is isolated to this test, and
will be properly cleaned up by the test. This avoids several pitfalls of
creating temporary directories for test purposes, as well as makes it easier
- to setup directories and verify their contents. For example:
+ to setup directories and verify their contents. For example::
def test_foo(self):
out_dir = self.create_tempdir()
@@ -637,14 +638,14 @@ class TestCase(unittest.TestCase):
self.assertTrue(os.path.exists(expected_paths[1]))
self.assertEqual('foo', out_log.read_text())
- See also: `create_tempfile()` for creating temporary files.
+ See also: :meth:`create_tempdir` for creating temporary files.
Args:
name: Optional name of the directory. If not given, a unique
name will be generated and used.
cleanup: Optional cleanup policy on when/if to remove the directory (and
all its contents) at the end of the test. If None, then uses
- `self.tempfile_cleanup`.
+ :attr:`tempfile_cleanup`.
Returns:
A _TempDir representing the created directory; see _TempDir class docs
@@ -678,7 +679,7 @@ class TestCase(unittest.TestCase):
be properly cleaned up by the test. This avoids several pitfalls of
creating temporary files for test purposes, as well as makes it easier
to setup files, their data, read them back, and inspect them when
- a test fails. For example:
+ a test fails. For example::
def test_foo(self):
output = self.create_tempfile()
@@ -690,15 +691,15 @@ class TestCase(unittest.TestCase):
state.
NOTE: If the file already exists, it will be made writable and overwritten.
- See also: `create_tempdir()` for creating temporary directories, and
- `_TempDir.create_file` for creating files within a temporary directory.
+ See also: :meth:`create_tempdir` for creating temporary directories, and
+ ``_TempDir.create_file`` for creating files within a temporary directory.
Args:
file_path: Optional file path for the temp file. If not given, a unique
file name will be generated and used. Slashes are allowed in the name;
any missing intermediate directories will be created. NOTE: This path is
the path that will be cleaned up, including any directories in the path,
- e.g., 'foo/bar/baz.txt' will `rm -r foo`.
+ e.g., ``'foo/bar/baz.txt'`` will ``rm -r foo``.
content: Optional string or
bytes to initially write to the file. If not
specified, then an empty file is created.
@@ -710,7 +711,7 @@ class TestCase(unittest.TestCase):
`content` is text.
cleanup: Optional cleanup policy on when/if to remove the directory (and
all its contents) at the end of the test. If None, then uses
- `self.tempfile_cleanup`.
+ :attr:`tempfile_cleanup`.
Returns:
A _TempFile representing the created file; see _TempFile class docs for
@@ -1075,10 +1076,10 @@ class TestCase(unittest.TestCase):
"""Asserts that two sequences have the same elements (in any order).
This method, unlike assertCountEqual, doesn't care about any
- duplicates in the expected and actual sequences.
+ duplicates in the expected and actual sequences::
- >> assertSameElements([1, 1, 1, 0, 0, 0], [0, 1])
- # Doesn't raise an AssertionError
+ # Doesn't raise an AssertionError
+ assertSameElements([1, 1, 1, 0, 0, 0], [0, 1])
If possible, you should use assertCountEqual instead of
assertSameElements.
@@ -1174,6 +1175,7 @@ class TestCase(unittest.TestCase):
expression (a string or re compiled object) instead of a list.
Notes:
+
1. This function uses substring matching, i.e. the matching
succeeds if *any* substring of the error message matches *any*
regex in the list. This is more convenient for the user than
@@ -1523,39 +1525,39 @@ class TestCase(unittest.TestCase):
"""Asserts that total ordering has been implemented correctly.
For example, say you have a class A that compares only on its attribute x.
- Comparators other than __lt__ are omitted for brevity.
+ Comparators other than ``__lt__`` are omitted for brevity::
- class A(object):
- def __init__(self, x, y):
- self.x = x
- self.y = y
+ class A(object):
+ def __init__(self, x, y):
+ self.x = x
+ self.y = y
- def __hash__(self):
- return hash(self.x)
+ def __hash__(self):
+ return hash(self.x)
- def __lt__(self, other):
- try:
- return self.x < other.x
- except AttributeError:
- return NotImplemented
+ def __lt__(self, other):
+ try:
+ return self.x < other.x
+ except AttributeError:
+ return NotImplemented
assertTotallyOrdered will check that instances can be ordered correctly.
- For example,
+ For example::
- self.assertTotallyOrdered(
- [None], # None should come before everything else.
- [1], # Integers sort earlier.
- [A(1, 'a')],
- [A(2, 'b')], # 2 is after 1.
- [A(3, 'c'), A(3, 'd')], # The second argument is irrelevant.
- [A(4, 'z')],
- ['foo']) # Strings sort last.
+ self.assertTotallyOrdered(
+ [None], # None should come before everything else.
+ [1], # Integers sort earlier.
+ [A(1, 'a')],
+ [A(2, 'b')], # 2 is after 1.
+ [A(3, 'c'), A(3, 'd')], # The second argument is irrelevant.
+ [A(4, 'z')],
+ ['foo']) # Strings sort last.
Args:
- *groups: A list of groups of elements. Each group of elements is a list
- of objects that are equal. The elements in each group must be less
- than the elements in the group after it. For example, these groups are
- totally ordered: [None], [1], [2, 2], [3].
+ *groups: A list of groups of elements. Each group of elements is a list
+ of objects that are equal. The elements in each group must be less
+ than the elements in the group after it. For example, these groups are
+ totally ordered: ``[None]``, ``[1]``, ``[2, 2]``, ``[3]``.
**kwargs: optional msg keyword argument can be passed.
"""
@@ -2040,12 +2042,14 @@ def main(*args, **kwargs):
Usually this function is called without arguments, so the
unittest.TestProgram instance will get created with the default settings,
- so it will run all test methods of all TestCase classes in the __main__
+ so it will run all test methods of all TestCase classes in the ``__main__``
module.
Args:
- *args: Positional arguments passed through to unittest.TestProgram.__init__.
- **kwargs: Keyword arguments passed through to unittest.TestProgram.__init__.
+ *args: Positional arguments passed through to
+ ``unittest.TestProgram.__init__``.
+ **kwargs: Keyword arguments passed through to
+ ``unittest.TestProgram.__init__``.
"""
print_python_version()
_run_in_app(run_tests, args, kwargs)
@@ -2178,29 +2182,29 @@ def skipThisClass(reason):
implementations between a number of concrete testcase classes.
Example usage, showing how you can share some common test methods between
- subclasses. In this example, only 'BaseTest' will be marked as skipped, and
- not RealTest or SecondRealTest:
+ subclasses. In this example, only ``BaseTest`` will be marked as skipped, and
+ not RealTest or SecondRealTest::
- @absltest.skipThisClass("Shared functionality")
- class BaseTest(absltest.TestCase):
- def test_simple_functionality(self):
- self.assertEqual(self.system_under_test.method(), 1)
+ @absltest.skipThisClass("Shared functionality")
+ class BaseTest(absltest.TestCase):
+ def test_simple_functionality(self):
+ self.assertEqual(self.system_under_test.method(), 1)
- class RealTest(BaseTest):
- def setUp(self):
- super().setUp()
- self.system_under_test = MakeSystem(argument)
+ class RealTest(BaseTest):
+ def setUp(self):
+ super().setUp()
+ self.system_under_test = MakeSystem(argument)
- def test_specific_behavior(self):
- ...
+ def test_specific_behavior(self):
+ ...
- class SecondRealTest(BaseTest):
- def setUp(self):
- super().setUp()
- self.system_under_test = MakeSystem(other_arguments)
+ class SecondRealTest(BaseTest):
+ def setUp(self):
+ super().setUp()
+ self.system_under_test = MakeSystem(other_arguments)
- def test_other_behavior(self):
- ...
+ def test_other_behavior(self):
+ ...
Args:
reason: The reason we have a skip in place. For instance: 'shared test
@@ -2540,11 +2544,14 @@ def run_tests(argv, args, kwargs): # pylint: disable=line-too-long
Args:
argv: sys.argv with the command-line flags removed from the front, i.e. the
- argv with which app.run() has called __main__.main. It is passed to
- unittest.TestProgram.__init__(argv=), which does its own flag parsing. It
- is ignored if kwargs contains an argv entry.
- args: Positional arguments passed through to unittest.TestProgram.__init__.
- kwargs: Keyword arguments passed through to unittest.TestProgram.__init__.
+ argv with which :func:`app.run()<absl.app.run>` has called
+ ``__main__.main``. It is passed to
+ ``unittest.TestProgram.__init__(argv=)``, which does its own flag parsing.
+ It is ignored if kwargs contains an argv entry.
+ args: Positional arguments passed through to
+ ``unittest.TestProgram.__init__``.
+ kwargs: Keyword arguments passed through to
+ ``unittest.TestProgram.__init__``.
"""
result = _run_and_get_tests_result(
argv, args, kwargs, xml_reporter.TextAndXMLTestRunner)
diff --git a/absl/testing/flagsaver.py b/absl/testing/flagsaver.py
index f87dbb1..37926d7 100644
--- a/absl/testing/flagsaver.py
+++ b/absl/testing/flagsaver.py
@@ -17,40 +17,40 @@
There are many ways to save and restore. Always use the most convenient method
for a given use case.
-Here are examples of each method. They all call do_stuff() while FLAGS.someflag
-is temporarily set to 'foo'.
-
- from absl.testing import flagsaver
-
- # Use a decorator which can optionally override flags via arguments.
- @flagsaver.flagsaver(someflag='foo')
- def some_func():
- do_stuff()
-
- # Use a decorator which can optionally override flags with flagholders.
- @flagsaver.flagsaver((module.FOO_FLAG, 'foo'), (other_mod.BAR_FLAG, 23))
- def some_func():
- do_stuff()
-
- # Use a decorator which does not override flags itself.
- @flagsaver.flagsaver
- def some_func():
- FLAGS.someflag = 'foo'
- do_stuff()
-
- # Use a context manager which can optionally override flags via arguments.
- with flagsaver.flagsaver(someflag='foo'):
- do_stuff()
-
- # Save and restore the flag values yourself.
- saved_flag_values = flagsaver.save_flag_values()
- try:
- FLAGS.someflag = 'foo'
- do_stuff()
- finally:
- flagsaver.restore_flag_values(saved_flag_values)
-
-We save and restore a shallow copy of each Flag object's __dict__ attribute.
+Here are examples of each method. They all call ``do_stuff()`` while
+``FLAGS.someflag`` is temporarily set to ``'foo'``::
+
+ from absl.testing import flagsaver
+
+ # Use a decorator which can optionally override flags via arguments.
+ @flagsaver.flagsaver(someflag='foo')
+ def some_func():
+ do_stuff()
+
+ # Use a decorator which can optionally override flags with flagholders.
+ @flagsaver.flagsaver((module.FOO_FLAG, 'foo'), (other_mod.BAR_FLAG, 23))
+ def some_func():
+ do_stuff()
+
+ # Use a decorator which does not override flags itself.
+ @flagsaver.flagsaver
+ def some_func():
+ FLAGS.someflag = 'foo'
+ do_stuff()
+
+ # Use a context manager which can optionally override flags via arguments.
+ with flagsaver.flagsaver(someflag='foo'):
+ do_stuff()
+
+ # Save and restore the flag values yourself.
+ saved_flag_values = flagsaver.save_flag_values()
+ try:
+ FLAGS.someflag = 'foo'
+ do_stuff()
+ finally:
+ flagsaver.restore_flag_values(saved_flag_values)
+
+We save and restore a shallow copy of each Flag object's ``__dict__`` attribute.
This preserves all attributes of the flag, such as whether or not it was
overridden from its default value.
@@ -102,7 +102,7 @@ def save_flag_values(flag_values=FLAGS):
be saved. This should almost never need to be overridden.
Returns:
Dictionary mapping keys to values. Keys are flag names, values are
- corresponding __dict__ members. E.g. {'key': value_dict, ...}.
+ corresponding ``__dict__`` members. E.g. ``{'key': value_dict, ...}``.
"""
return {name: _copy_flag_dict(flag_values[name]) for name in flag_values}
@@ -177,16 +177,16 @@ class _FlagOverrider(object):
def _copy_flag_dict(flag):
- """Returns a copy of the flag object's __dict__.
+ """Returns a copy of the flag object's ``__dict__``.
- It's mostly a shallow copy of the __dict__, except it also does a shallow
+ It's mostly a shallow copy of the ``__dict__``, except it also does a shallow
copy of the validator list.
Args:
flag: flags.Flag, the flag to copy.
Returns:
- A copy of the flag object's __dict__.
+ A copy of the flag object's ``__dict__``.
"""
copy = flag.__dict__.copy()
copy['_value'] = flag.value # Ensure correct restore for C++ flags.
diff --git a/absl/testing/parameterized.py b/absl/testing/parameterized.py
index ec6a529..650d6cf 100644
--- a/absl/testing/parameterized.py
+++ b/absl/testing/parameterized.py
@@ -17,16 +17,15 @@
A parameterized test is a method in a test case that is invoked with different
argument tuples.
-A simple example:
-
- class AdditionExample(parameterized.TestCase):
- @parameterized.parameters(
- (1, 2, 3),
- (4, 5, 9),
- (1, 1, 3))
- def testAddition(self, op1, op2, result):
- self.assertEqual(result, op1 + op2)
+A simple example::
+ class AdditionExample(parameterized.TestCase):
+ @parameterized.parameters(
+ (1, 2, 3),
+ (4, 5, 9),
+ (1, 1, 3))
+ def testAddition(self, op1, op2, result):
+ self.assertEqual(result, op1 + op2)
Each invocation is a separate test case and properly isolated just
like a normal test method, with its own setUp/tearDown cycle. In the
@@ -34,15 +33,15 @@ example above, there are three separate testcases, one of which will
fail due to an assertion error (1 + 1 != 3).
Parameters for individual test cases can be tuples (with positional parameters)
-or dictionaries (with named parameters):
+or dictionaries (with named parameters)::
- class AdditionExample(parameterized.TestCase):
- @parameterized.parameters(
- {'op1': 1, 'op2': 2, 'result': 3},
- {'op1': 4, 'op2': 5, 'result': 9},
- )
- def testAddition(self, op1, op2, result):
- self.assertEqual(result, op1 + op2)
+ class AdditionExample(parameterized.TestCase):
+ @parameterized.parameters(
+ {'op1': 1, 'op2': 2, 'result': 3},
+ {'op1': 4, 'op2': 5, 'result': 9},
+ )
+ def testAddition(self, op1, op2, result):
+ self.assertEqual(result, op1 + op2)
If a parameterized test fails, the error message will show the
original test name and the parameters for that test.
@@ -50,129 +49,135 @@ original test name and the parameters for that test.
The id method of the test, used internally by the unittest framework, is also
modified to show the arguments (but note that the name reported by `id()`
doesn't match the actual test name, see below). To make sure that test names
-stay the same across several invocations, object representations like
+stay the same across several invocations, object representations like::
- >>> class Foo(object):
- ... pass
- >>> repr(Foo())
- '<__main__.Foo object at 0x23d8610>'
+ >>> class Foo(object):
+ ... pass
+ >>> repr(Foo())
+ '<__main__.Foo object at 0x23d8610>'
-are turned into '<__main__.Foo>'. When selecting a subset of test cases to run
+are turned into ``__main__.Foo``. When selecting a subset of test cases to run
on the command-line, the test cases contain an index suffix for each argument
-in the order they were passed to `parameters()` (eg. testAddition0,
+in the order they were passed to :func:`parameters` (eg. testAddition0,
testAddition1, etc.) This naming scheme is subject to change; for more reliable
-and stable names, especially in test logs, use `named_parameters()` instead.
+and stable names, especially in test logs, use :func:`named_parameters` instead.
-Tests using `named_parameters()` are similar to `parameters()`, except only
-tuples or dicts of args are supported. For tuples, the first parameter arg
+Tests using :func:`named_parameters` are similar to :func:`parameters`, except
+only tuples or dicts of args are supported. For tuples, the first parameter arg
has to be a string (or an object that returns an apt name when converted via
-str()). For dicts, a value for the key 'testcase_name' must be present and must
-be a string (or an object that returns an apt name when converted via str()):
-
- class NamedExample(parameterized.TestCase):
- @parameterized.named_parameters(
- ('Normal', 'aa', 'aaa', True),
- ('EmptyPrefix', '', 'abc', True),
- ('BothEmpty', '', '', True))
- def testStartsWith(self, prefix, string, result):
- self.assertEqual(result, string.startswith(prefix))
-
- class NamedExample(parameterized.TestCase):
- @parameterized.named_parameters(
- {'testcase_name': 'Normal',
- 'result': True, 'string': 'aaa', 'prefix': 'aa'},
- {'testcase_name': 'EmptyPrefix',
- 'result': True, 'string': 'abc', 'prefix': ''},
- {'testcase_name': 'BothEmpty',
- 'result': True, 'string': '', 'prefix': ''})
- def testStartsWith(self, prefix, string, result):
- self.assertEqual(result, string.startswith(prefix))
+``str()``). For dicts, a value for the key ``testcase_name`` must be present and
+must be a string (or an object that returns an apt name when converted via
+``str()``)::
+
+ class NamedExample(parameterized.TestCase):
+ @parameterized.named_parameters(
+ ('Normal', 'aa', 'aaa', True),
+ ('EmptyPrefix', '', 'abc', True),
+ ('BothEmpty', '', '', True))
+ def testStartsWith(self, prefix, string, result):
+ self.assertEqual(result, string.startswith(prefix))
+
+ class NamedExample(parameterized.TestCase):
+ @parameterized.named_parameters(
+ {'testcase_name': 'Normal',
+ 'result': True, 'string': 'aaa', 'prefix': 'aa'},
+ {'testcase_name': 'EmptyPrefix',
+ 'result': True, 'string': 'abc', 'prefix': ''},
+ {'testcase_name': 'BothEmpty',
+ 'result': True, 'string': '', 'prefix': ''})
+ def testStartsWith(self, prefix, string, result):
+ self.assertEqual(result, string.startswith(prefix))
Named tests also have the benefit that they can be run individually
-from the command line:
+from the command line::
- $ testmodule.py NamedExample.testStartsWithNormal
- .
- --------------------------------------------------------------------
- Ran 1 test in 0.000s
+ $ testmodule.py NamedExample.testStartsWithNormal
+ .
+ --------------------------------------------------------------------
+ Ran 1 test in 0.000s
- OK
+ OK
Parameterized Classes
=====================
+
If invocation arguments are shared across test methods in a single
TestCase class, instead of decorating all test methods
-individually, the class itself can be decorated:
+individually, the class itself can be decorated::
- @parameterized.parameters(
- (1, 2, 3),
- (4, 5, 9))
- class ArithmeticTest(parameterized.TestCase):
- def testAdd(self, arg1, arg2, result):
- self.assertEqual(arg1 + arg2, result)
+ @parameterized.parameters(
+ (1, 2, 3),
+ (4, 5, 9))
+ class ArithmeticTest(parameterized.TestCase):
+ def testAdd(self, arg1, arg2, result):
+ self.assertEqual(arg1 + arg2, result)
- def testSubtract(self, arg1, arg2, result):
- self.assertEqual(result - arg1, arg2)
+ def testSubtract(self, arg1, arg2, result):
+ self.assertEqual(result - arg1, arg2)
Inputs from Iterables
=====================
+
If parameters should be shared across several test cases, or are dynamically
created from other sources, a single non-tuple iterable can be passed into
-the decorator. This iterable will be used to obtain the test cases:
+the decorator. This iterable will be used to obtain the test cases::
- class AdditionExample(parameterized.TestCase):
- @parameterized.parameters(
- c.op1, c.op2, c.result for c in testcases
- )
- def testAddition(self, op1, op2, result):
- self.assertEqual(result, op1 + op2)
+ class AdditionExample(parameterized.TestCase):
+ @parameterized.parameters(
+ c.op1, c.op2, c.result for c in testcases
+ )
+ def testAddition(self, op1, op2, result):
+ self.assertEqual(result, op1 + op2)
Single-Argument Test Methods
============================
+
If a test method takes only one argument, the single arguments must not be
-wrapped into a tuple:
+wrapped into a tuple::
- class NegativeNumberExample(parameterized.TestCase):
- @parameterized.parameters(
- -1, -3, -4, -5
- )
- def testIsNegative(self, arg):
- self.assertTrue(IsNegative(arg))
+ class NegativeNumberExample(parameterized.TestCase):
+ @parameterized.parameters(
+ -1, -3, -4, -5
+ )
+ def testIsNegative(self, arg):
+ self.assertTrue(IsNegative(arg))
List/tuple as a Single Argument
===============================
+
If a test method takes a single argument of a list/tuple, it must be wrapped
-inside a tuple:
+inside a tuple::
- class ZeroSumExample(parameterized.TestCase):
- @parameterized.parameters(
- ([-1, 0, 1], ),
- ([-2, 0, 2], ),
- )
- def testSumIsZero(self, arg):
- self.assertEqual(0, sum(arg))
+ class ZeroSumExample(parameterized.TestCase):
+ @parameterized.parameters(
+ ([-1, 0, 1], ),
+ ([-2, 0, 2], ),
+ )
+ def testSumIsZero(self, arg):
+ self.assertEqual(0, sum(arg))
Cartesian product of Parameter Values as Parametrized Test Cases
-======================================================
+================================================================
+
If required to test method over a cartesian product of parameters,
`parameterized.product` may be used to facilitate generation of parameters
-test combinations:
+test combinations::
- class TestModuloExample(parameterized.TestCase):
- @parameterized.product(
- num=[0, 20, 80],
- modulo=[2, 4],
- expected=[0]
- )
- def testModuloResult(self, num, modulo, expected):
- self.assertEqual(expected, num % modulo)
+ class TestModuloExample(parameterized.TestCase):
+ @parameterized.product(
+ num=[0, 20, 80],
+ modulo=[2, 4],
+ expected=[0]
+ )
+ def testModuloResult(self, num, modulo, expected):
+ self.assertEqual(expected, num % modulo)
This results in 6 test cases being created - one for each combination of the
parameters. It is also possible to supply sequences of keyword argument dicts
-as elements of the cartesian product:
+as elements of the cartesian product::
@parameterized.product(
(dict(num=5, modulo=3, expected=2),
@@ -187,10 +192,11 @@ data (supplied as kwarg dicts) and for each of the two data types (supplied as
a named parameter). Multiple keyword argument dicts may be supplied if required.
Async Support
-===============================
+=============
+
If a test needs to call async functions, it can inherit from both
parameterized.TestCase and another TestCase that supports async calls, such
-as [asynctest](https://github.com/Martiusweb/asynctest):
+as [asynctest](https://github.com/Martiusweb/asynctest)::
import asynctest
@@ -674,16 +680,16 @@ def CoopTestCase(other_base_class): # pylint: disable=invalid-name
This enables the TestCase to be used in combination
with other base classes that have custom metaclasses, such as
- mox.MoxTestBase.
+ ``mox.MoxTestBase``.
- Only works with metaclasses that do not override type.__new__.
+ Only works with metaclasses that do not override ``type.__new__``.
- Example:
+ Example::
- from absl.testing import parameterized
+ from absl.testing import parameterized
- class ExampleTest(parameterized.CoopTestCase(OtherTestCase)):
- ...
+ class ExampleTest(parameterized.CoopTestCase(OtherTestCase)):
+ ...
Args:
other_base_class: (class) A test case base class.