Notices

The point of this plugin is ultimately to compare the output of some static type checking tool against some known input and assert it matches some expected output.

That output can be thought of as a sequence of what this plugin calls “notices” that represent information about specific lines in specific files.

This plugin has a hierarchy to represent all those notices;

Note

There are notice_changers for modifying notices instead of directly accessing the api on the notices themselves

The pytest_typing_runner.protocols.ProgramNotice has on it the file location, the line number, an optional column number, a severity, and a message.

Severities are currently modelled as a pytest_typing_runner.protocols.Severity object with three default implementations:

class pytest_typing_runner.notices.NoteSeverity

Represents a “note” severity

Implements pytest_typing_runner.protocols.Severity

class pytest_typing_runner.notices.WarningSeverity

Represents a “warning” severity

Implements pytest_typing_runner.protocols.Severity

class pytest_typing_runner.notices.ErrorSeverity(error_type: str)

Represents an “error” severity with an error type

The display will be error[ERROR_TYPE] and comparisons/ordering will take the error_type into account.

Note that if the error_type is an empty string that comparisons with other severities will match any error_type.

Implements pytest_typing_runner.protocols.Severity

Parameters:

error_type – The specific type of error. For example arg-type or assignment

These are the default implementations of the different layers of the notices:

class pytest_typing_runner.notices.ProgramNotice(*, location: Path, line_number: int, col: int | None, severity: Severity, msg: NoticeMsg)

Represents a single notice from the static type checker

Implements pytest_typing_runner.protocols.ProgramNotice

Parameters:
  • location – The full path to the file this notice is for

  • line_number – the line this notice appears on

  • col – optional line representing the column the error relates to

  • severity – The severity of the notice (either note or error with a specific error type)

  • msg – The message in the notice

classmethod reveal_msg(revealed: str, /) str

Helper to get a string that represents the msg on a note for a reveal_type(...) instruction

property is_type_reveal: bool

Returns whether this notice represents output from a reveal_type(…) instruction

clone(*, msg: str | NoticeMsg | None = None, **kwargs: Unpack[ProgramNoticeCloneKwargs]) Self

Return a copy of this notice with certain values replaced.

display() str

Return a string for displaying this notice.

If col is None then it’s not displayed.

Will return “col=COL severity=SEVERITY:: MSG”

matches(other: ProgramNotice) bool

Compare against another program notice.

If col is None on either notice then that is not compared.

class pytest_typing_runner.notices.LineNotices(location: Path, line_number: int)

This represents the notices at a specific line in a specific file

Implements pytest_typing_runner.protocols.LineNotices

Parameters:
  • location – The path these notices are for

  • line_number – The specific line number for these notices

classmethod msg_maker(*, pattern: str) Self

Used to construct a GlobMsg

Implements NoticeMsgMaker

property has_notices: bool

Return whether this contains any notices

set_notices(notices: Sequence[ProgramNotice | None], allow_empty: Literal[True]) Self
set_notices(notices: Sequence[ProgramNotice | None], allow_empty: Literal[False] = False) Self | None

Return a copy where the chosen notice(s) are replaced

Parameters:
  • notices – The notices the clone should have. Any None entries are dropped

  • allow_empty – If False then None is returned instead of a copy with an empty list

generate_notice(*, msg: str | NoticeMsg, msg_maker: NoticeMsgMaker | None = None, severity: Severity | None = None, col: int | None = None) ProgramNotice

Return an object that satisfies pytest_typing_runner.protocols.ProgramNotice

Parameters:
  • severity – optional severity, defaults to “note”

  • msg – optional msg, defaults to an empty string

  • col – optional column, defaults to None

class pytest_typing_runner.notices.FileNotices(location: Path)

Used to represent the notices for a file

Implements pytest_typing_runner.protocols.FileNotices

Parameters:

location – The location of the file the notices are in

classmethod msg_maker(*, pattern: str) Self

Used to construct a GlobMsg

Implements NoticeMsgMaker

property has_notices: bool

Return True if there are there any notices for this file

known_line_numbers() Iterator[int]

Yield the line numbers that have line notices

property known_names: Mapping[str, int]

Return the registered names

get_line_number(name_or_line: str | int, /) int | None

Normalise a name or line number to a line number.

The result has no relation to whether there are any notices for this file

Parameters:

name_or_line

When this is an integer it is returned as is regardless of whether it is named or has associated notices.

When it is a string, it will see if it is a registered name and either return None if it is not registered, else return the associated line number.

notices_at_line(line_number: int) LineNotices | None

Return None if there are no line notices for that line number, else return the found line notices.

Note that if there is an empty line notices that will be returned instead of None.

generate_notices_for_line(line_number: int, *, msg_maker: NoticeMsgMaker | None = None) LineNotices

Return an object that satisfies pytest_typing_runner.protocols.LineNotices for the location of this file at the specified line number.

This object is not added to this file notices

set_name(name: str, line_number: int) Self

Return a copy of the file notices with this name registered for the specified line_number. If the name is already registered it will be overridden.

set_lines(notices: Mapping[int, LineNotices | None]) Self

Return a copy of this file notices with the notices replaced by those provided.

When the value is None any notices for that line number will be removed.

clear(*, clear_names: bool) Self

Return a modified file notices with all notices removed

class pytest_typing_runner.notices.ProgramNotices

Represents all the notices for a run of a static type checker

Implements pytest_typing_runner.protocols.ProgramNotices

classmethod msg_maker(*, pattern: str) Self

Used to construct a GlobMsg

Implements NoticeMsgMaker

property has_notices: bool

Return whether there are any notices

known_locations() Iterator[Path]

Yield locations that have associated file notices

diff(root_dir: Path, other: ProgramNotices) DiffNotices

Produce a diff where this program notices is on the left, and the notices passed in is on the right.

All locations across both will appear in the diff as strings relative to the passed in root_dir.

Parameters:
  • root_dir – All locations will be represented as a string relative to the root_dir except for paths outside of root_dir which will be a string of the full path

  • other – The right side of the diff

notices_at_location(location: Path) FileNotices | None

Return the FileNotices for the specified location or None if no existing notices for that location.

Note if there is an empty FileNotices for that location it will be returned instead of None

Parameters:

location – The location to return notices for

generate_notices_for_location(location: Path, *, msg_maker: NoticeMsgMaker | None = None) FileNotices

Return an object that satisfies pytest_typing_runner.protocols.FileNotices

Note the file notices are not added to this ProgramNotices.

Parameters:

location – The value set on the FileNotices for location

set_files(notices: Mapping[Path, FileNotices | None]) Self

Return a copy of this ProgramNotices overriding the notices with those passed in

Note that None values will result in that location being removed.

Locations that exist but aren’t in the passed in notices will be preserved

Parameters:

notices – A map of location to either FileNotices to set for that location or None when that location should be removed.

The msg on a ProgramNotice

The msg property on a pytest_typing_runner.notices.ProgramNotice is an object that has individual control over how it’s compared to other messages.

This means that one msg may compare itself to another using a regular expression, whereas one may compare itself using a glob. The most common will be a plain equality check.

The default implementations are:

class pytest_typing_runner.notices.PlainMsg(raw: str, /)

A notice msg that compares by treating the msg as a plain string

from pytest_typing_runner import notices

msg = notices.PlainMsg.create(pattern='Revealed type is "stuff"')
classmethod create(*, pattern: str) Self

Used to construct a GlobMsg

Implements NoticeMsgMaker

match(*, want: str) bool

Returns whether this msg exactly matches the wanted string.

clone(*, pattern: str) Self

Returns an instance of PlainMsg using the provided pattern

class pytest_typing_runner.notices.RegexMsg(raw: str, /)

A notice msg that compares by treating the msg as a regex pattern

from pytest_typing_runner import notices

msg = notices.RegexMsg.create(pattern='Revealed type is "[^"]+"')
classmethod create(*, pattern: str) Self

Used to construct a RegexMsg

Implements NoticeMsgMaker

match(*, want: str) bool

Returns whether this msg successfully performs a regex match on the wanted string.

clone(*, pattern: str) Self

Returns an instance of RegexMsg using the provided pattern

class pytest_typing_runner.notices.GlobMsg(raw: str, /)

A notice msg that compares by treating the msg as a glob pattern

from pytest_typing_runner import notices

msg = notices.RegexMsg.create(pattern='Revealed type is "*"')
classmethod create(*, pattern: str) Self

Used to construct a GlobMsg

Implements NoticeMsgMaker

match(*, want: str) bool

Returns whether this msg successfully performs a glob match on the wanted string.

clone(*, pattern: str) Self

Returns an instance of GlobMsg using the provided pattern

Most things should be using pytest_typing_runner.protocols.ScenarioRunner.generate_program_notices to create a pytest_typing_runner.protocols.ProgramNotices which in turn uses the default_msg_maker on the ScenarioRunner and so that’s a sensible place to change the default:

from pytest_typing_runner import scenarios, protocols
import pytest


class MyScenarioRunner(scenarios.ScenarioRunner[protocols.T_Scenario]):
    default_msg_maker: protocols.NoticeMsgMaker = notices.RegexMsg.create


@pytest.fixture
def typing_scenario_runner_maker(
    typing_scenario_maker: protocols.ScenarioMaker[protocols.T_Scenario],
) -> protocols.ScenarioRunnerMaker[protocols.T_Scenario]:
    return MyScenarioRunner.create

The msg_maker will be passed down from ProgramNotices to FileNotices to LineNotices when the generiate_* methods are used on these containers.

When generate_notice is used on a LineNotices it will use msg_maker if msg is passed in as a string. Otherwise it will use the msg as is.