expression.py


Overview

The [expression.py](/projects/286/67471) module provides functionality to parse, compile, and evaluate logical match expressions following a custom grammar. These expressions are primarily used in command-line options such as `-k` and `-m` in testing frameworks (e.g., pytest) to filter tests or items based on complex boolean conditions.

The module transforms textual match expressions into a Python Abstract Syntax Tree (AST), compiles them into executable code objects, and evaluates them against user-provided matcher functions. This enables flexible, dynamic filtering logic combining identifiers, boolean operators (`and`, `or`, `not`), and optional keyword arguments.


Detailed Description

Grammar and Semantics

The match expressions are composed from identifiers with optional keyword arguments combined using boolean operators:

expression: expr? EOF
expr:       and_expr ('or' and_expr)*
and_expr:   not_expr ('and' not_expr)*
not_expr:   'not' not_expr | '(' expr ')' | ident kwargs?

ident:      (\w|:|\+|-|\.|\[|\]|\\|/)+
kwargs:     ('(' name '=' value ( ', ' name '=' value )*  ')')
name:       a valid ident, but not a reserved keyword
value:      (unescaped) string literal | (-)?[0-9]+ | 'False' | 'True' | 'None'

Classes and Functions

Enum: TokenType

Defines token types used by the lexical scanner.

Member

Description

`LPAREN`

Left parenthesis `(`

`RPAREN`

Right parenthesis `)`

`OR`

Logical OR operator

`AND`

Logical AND operator

`NOT`

Logical NOT operator

`IDENT`

Identifier token

`EOF`

End of input

`EQUAL`

Equals sign `=`

`STRING`

String literal

`COMMA`

Comma `,`


Dataclass: Token

Represents a lexical token with type, value, and position.


Exception: ParseError

Raised when invalid syntax is encountered during parsing.


Class: Scanner

Lexical analyzer that tokenizes the input expression string.


Parsing Functions

All parsing functions take a `Scanner` instance, consume tokens, and produce Python AST nodes (`ast.expr`) representing the logical structure.


Protocol: MatcherCall

Describes the callable matcher interface used during evaluation.

def __call__(self, name: str, /, **kwargs: str | int | bool | None) -> bool: ...

Dataclass: MatcherNameAdapter

Wraps a matcher callable for a single identifier.


Class: MatcherAdapter

Adapts a matcher callable into a mapping interface expected by Python's `eval()` locals.


Class: Expression

Represents a compiled match expression.


Important Implementation Details


Interactions With Other System Components


Visual Diagram

classDiagram
    class Scanner {
        +__init__(input: str)
        +lex(input: str) Iterator~Token~
        +accept(type: TokenType, reject: bool = False) Token|None
        +reject(expected: Sequence~TokenType~)
    }
    class Token {
        +type: TokenType
        +value: str
        +pos: int
    }
    class ParseError {
        +__init__(column: int, message: str)
        +__str__()
    }
    class Expression {
        +code: CodeType
        +compile(input: str) Expression
        +evaluate(matcher: MatcherCall) bool
    }
    class MatcherAdapter {
        +__init__(matcher: MatcherCall)
        +__getitem__(key: str) MatcherNameAdapter
    }
    class MatcherNameAdapter {
        +__init__(matcher: MatcherCall, name: str)
        +__bool__() bool
        +__call__(**kwargs) bool
    }

    Scanner --> Token
    Expression --> MatcherAdapter
    MatcherAdapter --> MatcherNameAdapter
    MatcherNameAdapter ..> MatcherCall : uses
    Scanner ..> ParseError : raises
    Expression ..> ParseError : raises

Summary

The [expression.py](/projects/286/67471) module is a self-contained parser and evaluator for user-defined boolean match expressions. It converts textual input into executable code, enabling complex filtering logic for testing frameworks or similar applications. The design cleanly separates parsing, tokenization, AST construction, and evaluation, facilitating extensibility and integration with various matcher implementations.