_pytest.assertion.rewrite module

Rewrite assertion AST to produce nice error messages

class AssertionRewritingHook(config)[source]

Bases: importlib.abc.MetaPathFinder

PEP302/PEP451 import hook which rewrites asserts.

set_session(session)[source]
_find_spec(fullname, path=None, target=None)

Try to find a spec for ‘fullname’ on sys.path or ‘path’.

The search is based on sys.path_hooks and sys.path_importer_cache.

find_spec(name, path=None, target=None)[source]
create_module(spec)[source]
exec_module(module)[source]
_early_rewrite_bailout(name, state)[source]

This is a fast way to get out of rewriting modules.

Profiling has shown that the call to PathFinder.find_spec (inside of the find_spec from this class) is a major slowdown, so, this method tries to filter what we’re sure won’t be rewritten before getting to it.

_should_rewrite(name, fn, state)[source]
_is_marked_for_rewrite(name: str, state)[source]
mark_rewrite(*names: str)None[source]

Mark import names as needing to be rewritten.

The named module or package as well as any nested modules will be rewritten on import.

_warn_already_imported(name)[source]
get_data(pathname)[source]

Optional PEP302 get_data API.

_abc_impl = <_abc_data object>
_write_pyc_fp(fp, source_stat, co)[source]
_write_pyc(state, co, source_stat, pyc)[source]
_rewrite_test(fn, config)[source]

read and rewrite fn and return the code object.

_read_pyc(source, pyc, trace=<function <lambda>>)[source]

Possibly read a pytest pyc containing rewritten code.

Return rewritten code if successful or None if not.

rewrite_asserts(mod, source, module_path=None, config=None)[source]

Rewrite the assert statements in mod.

_saferepr(obj)[source]

Get a safe repr of an object for assertion error messages.

The assertion formatting (util.format_explanation()) requires newlines to be escaped since they are a special character for it. Normally assertion.util.format_explanation() does this but for a custom repr it is possible to contain one of the special escape sequences, especially ‘

{‘ and ‘ }’ are likely to be present in

JSON reprs.

_format_assertmsg(obj)[source]

Format the custom assertion message given.

For strings this simply replaces newlines with ‘

~’ so that

util.format_explanation() will preserve them instead of escaping newlines. For other objects safeformat() is used first.

_should_repr_global_name(obj)[source]
_format_boolop(explanations, is_or)[source]
_call_reprcompare(ops: Tuple[str, …], results: Tuple[bool, …], expls: Tuple[str, …], each_obj: Tuple[object, …])str[source]
_call_assertion_pass(lineno: int, orig: str, expl: str)None[source]
_check_if_assertion_pass_impl()bool[source]

Checks if any plugins implement the pytest_assertion_pass hook in order not to generate explanation unecessarily (might be expensive)

set_location(node, lineno, col_offset)[source]

Set node location information recursively.

_get_assertion_exprs(src: bytes) → Dict[int, str][source]

Returns a mapping from {lineno: “assertion test expression”}

class AssertionRewriter(module_path, config, source)[source]

Bases: ast.NodeVisitor

Assertion rewriting implementation.

The main entrypoint is to call .run() with an ast.Module instance, this will then find all the assert statements and rewrite them to provide intermediate values and a detailed assertion error. See http://pybites.blogspot.be/2011/07/behind-scenes-of-pytests-new-assertion.html for an overview of how this works.

The entry point here is .run() which will iterate over all the statements in an ast.Module and for each ast.Assert statement it finds call .visit() with it. Then .visit_Assert() takes over and is responsible for creating new ast statements to replace the original assert statement: it rewrites the test of an assertion to provide intermediate values and replace it with an if statement which raises an assertion error with a detailed explanation in case the expression is false and calls pytest_assertion_pass hook if expression is true.

For this .visit_Assert() uses the visitor pattern to visit all the AST nodes of the ast.Assert.test field, each visit call returning an AST node and the corresponding explanation string. During this state is kept in several instance attributes:

Statements

All the AST statements which will replace the assert statement.

Variables

This is populated by .variable() with each variable used by the statements so that they can all be set to None at the end of the statements.

Variable_counter

Counter to create new unique variables needed by statements. Variables are created using .variable() and have the form of “@py_assert0”.

Expl_stmts

The AST statements which will be executed to get data from the assertion. This is the code which will construct the detailed assertion message that is used in the AssertionError or for the pytest_assertion_pass hook.

Explanation_specifiers

A dict filled by .explanation_param() with %-formatting placeholders and their corresponding expressions to use in the building of an assertion message. This is used by .pop_format_context() to build a message.

Stack

A stack of the explanation_specifiers dicts maintained by .push_format_context() and .pop_format_context() which allows to build another %-formatted string while already building one.

This state is reset on every new assert statement visited and used by the other visitors.

_assert_expr_to_lineno()[source]
run(mod: _ast.Module)None[source]

Find all assert statements in mod and rewrite them.

staticmethod is_rewrite_disabled(docstring)[source]
variable()[source]

Get a new variable.

assign(expr)[source]

Give expr a name.

display(expr)[source]

Call saferepr on the expression.

helper(name, *args)[source]

Call a helper in this module.

builtin(name)[source]

Return the builtin called name.

explanation_param(expr)[source]

Return a new named %-formatting placeholder for expr.

This creates a %-formatting placeholder for expr in the current formatting context, e.g. %(py0)s. The placeholder and expr are placed in the current format context so that it can be used on the next call to .pop_format_context().

push_format_context()[source]

Create a new formatting context.

The format context is used for when an explanation wants to have a variable value formatted in the assertion message. In this case the value required can be added using .explanation_param(). Finally .pop_format_context() is used to format a string of %-formatted values as added by .explanation_param().

pop_format_context(expl_expr)[source]

Format the %-formatted string with current format context.

The expl_expr should be an ast.Str instance constructed from the %-placeholders created by .explanation_param(). This will add the required code to format said string to .expl_stmts and return the ast.Name instance of the formatted string.

generic_visit(node)[source]

Handle expressions we don’t have custom code for.

visit_Assert(assert_)[source]

Return the AST statements to replace the ast.Assert instance.

This rewrites the test of an assertion to provide intermediate values and replace it with an if statement which raises an assertion error with a detailed explanation in case the expression is false.

visit_Name(name)[source]
visit_BoolOp(boolop)[source]
visit_UnaryOp(unary)[source]
visit_BinOp(binop)[source]
visit_Call(call)[source]

visit ast.Call nodes

visit_Starred(starred)[source]
visit_Attribute(attr)[source]
visit_Compare(comp: _ast.Compare)[source]
try_makedirs(cache_dir)bool[source]

Attempts to create the given directory and sub-directories exist, returns True if successful or it already exists

get_cache_dir(file_path: pathlib.Path)pathlib.Path[source]

Returns the cache directory to write .pyc files for the given .py file path