Skip to content

PGN, Notation & Events API Reference

This module covers PGN options, numeric annotation glyphs (NAGs), move metadata, and event-driven movetext streaming.


PgnOptions

libscid.PgnOptions dataclass

PgnOptions(
    symbolic_nags: bool = False,
    supplemental_tags: bool = True,
    comments: bool = True,
    variations: bool = True,
    line_width: int | None = None,
)

Configuration options controlling PGN formatting and export serialisation.

Pass an instance of PgnOptions to Game.to_pgn() to customise the output representation of PGN tags, move annotations, commentary, variations, and line wrapping.

Attributes:

Name Type Description
symbolic_nags bool

If True, renders standard NAGs as typographical symbols (e.g. !, ?, !!, ??, !?, ?!). If False (the default), renders standard dollar notation ($1, $2, etc.).

supplemental_tags bool

If True (the default), includes non-standard supplemental header tags (e.g. WhiteElo, ECO, Annotator). If False, emits only the mandatory Seven Tag Roster (STR) and FEN.

comments bool

If True (the default), includes { comment } blocks in the exported movetext. If False, strips all commentary.

variations bool

If True (the default), recursively includes ( variation ) branches. If False, outputs only the mainline moves.

line_width int | None

Maximum column line width for movetext wrapping (default None uses the native default of 80 columns; 0 disables line wrapping for single-line movetext).

Examples:

>>> import libscid
>>> pgn_text = (
...     '[Event "World Championship"]\n'
...     '[Site "London"]\n'
...     '[Date "2018.11.09"]\n'
...     '[Round "1"]\n'
...     '[White "Carlsen, Magnus"]\n'
...     '[Black "Caruana, Fabiano"]\n'
...     '[Result "1/2-1/2"]\n'
...     '[ECO "B31"]\n'
...     '[Annotator "Expert"]\n\n'
...     '1. e4 $1 {King Pawn} (1. d4) 1... c5 *'
... )
>>> game = libscid.Game.from_pgn(pgn_text)
>>> # Export with symbolic NAGs (! instead of $1) and no variations:
>>> options = libscid.PgnOptions(
...     symbolic_nags=True,
...     variations=False,
...     supplemental_tags=False,
... )
>>> pgn_out = game.to_pgn(options)
>>> "1.e4 !" in pgn_out
True
>>> "(1.d4)" not in pgn_out
True
>>> '[Annotator "Expert"]' not in pgn_out
True

Nag

libscid.Nag dataclass

Nag(value: int | str | bytes)

Immutable Numeric Annotation Glyph (NAG) for chess move evaluation.

Encapsulates PGN standard annotation codes (0 to 255) representing move qualities (e.g. ! for good move, ? for mistake, !! for brilliant) and positional assessments (e.g. += for slight advantage).

Attributes:

Name Type Description
code int

Integer NAG code ranging from 0 to 255. A code of 0 denotes an absent or empty annotation.

Examples:

>>> import libscid
>>> nag = libscid.Nag("!")
>>> nag.code
1
>>> nag.text
'$1'
>>> nag.symbol
'!'
>>> libscid.Nag("$14").symbol
'+='
>>> str(libscid.Nag(3))
'$3'

Initialises a Nag instance from an integer code, text, or glyph symbol.

Parameters:

Name Type Description Default
value int | str | bytes

An integer code (0..255), dollar string/bytes (e.g. "$1", b"$1"), or typographical glyph (e.g. "!", "!?", "+=", "N"). Unrecognised string values default to code 0.

required

Raises:

Type Description
ValueError

If value is an integer outside the range 0..255.

LibScidError

If an underlying native conversion error occurs.

Source code in src/libscid/_nag.py
def __init__(self, value: int | str | bytes):
    """Initialises a Nag instance from an integer code, text, or glyph symbol.

    Args:
        value: An integer code (0..255), dollar string/bytes (e.g. `"$1"`, `b"$1"`),
            or typographical glyph (e.g. `"!"`, `"!?"`, `"+="`, `"N"`). Unrecognised
            string values default to code 0.

    Raises:
        ValueError: If `value` is an integer outside the range 0..255.
        LibScidError: If an underlying native conversion error occurs.
    """
    code = (
        load_library().nag_from_string(value)
        if isinstance(value, str | bytes)
        else value
    )
    if code < 0 or code > 255:
        raise ValueError("Nag code must be between 0 and 255")
    object.__setattr__(self, "code", code)

Attributes

text property

text: str

Standard PGN dollar notation string for the NAG.

Returns:

Type Description
str

The dollar-formatted string (e.g. "$1", "$14"), or an empty string "" when code is 0.

symbol property

symbol: str

Typographical glyph or symbolic representation of the NAG.

Returns:

Type Description
str

The typographical symbol (e.g. "!", "?", "+=", "N"), or an empty string "" when no symbolic representation exists or code is 0.


MoveMetadata

libscid.MoveMetadata

Bases: Flag

Bitwise flag enumeration representing characteristics of a chess move.

Classifies structural, tactical, and rule-based properties of a move such as check, checkmate, castling, and pawn promotion. Flags may be combined using bitwise operators (|, &, ^, ~).

Attributes:

Name Type Description
NONE

No special move characteristics present.

CHECK

Move delivers a check to the opponent's king (appends +).

CHECKMATE

Move delivers checkmate, concluding the game (appends #).

CASTLING

Move is a kingside (O-O) or queenside (O-O-O) castling move.

PROMOTION

Move is a pawn promotion to Queen, Rook, Bishop, or Knight.

Examples:

>>> import libscid
>>> pos = libscid.Position.from_fen("4k3/1P6/8/8/8/8/8/4K3 w - - 0 1")
>>> meta = pos.get_move_metadata("b7b8q")
>>> libscid.MoveMetadata.PROMOTION in meta
True
>>> bool(meta & libscid.MoveMetadata.CHECK)
True

iter_movetext

libscid.iter_movetext

iter_movetext(
    cursor: Cursor, *, variations: bool = True
) -> Iterator[MovetextEvent]

Iterate over movetext events starting from a given cursor position.

Recursively traverses the movetext tree from cursor, yielding structured MovetextLineStart, MovetextMove, and MovetextLineEnd events. When variations is True, sub-variation branches are explored in depth-first order immediately after the parent move from which they branch.

Parameters:

Name Type Description Default
cursor Cursor

Starting Cursor location for the traversal.

required
variations bool

Whether to recursively traverse nested variation branches. Defaults to True.

True

Yields:

Type Description
MovetextEvent

Sequential MovetextEvent instances.

Raises:

Type Description
TypeError

If cursor is not a Cursor or variations is not a boolean.

RuntimeError

If cursor navigation encounters an unexpected state.

Examples:

>>> import libscid
>>> game = libscid.Game.from_pgn("1. e4 (1. d4 d5) e5 *")
>>> moves = [
...     event.san for event in game.iter_movetext()
...     if isinstance(event, libscid.MovetextMove)
... ]
>>> moves
['e4', 'd4', 'd5', 'e5']
Source code in src/libscid/_domain_support/_movetext_iteration.py
def iter_movetext(
    cursor: Cursor,
    *,
    variations: bool = True,
) -> Iterator[MovetextEvent]:
    """Iterate over movetext events starting from a given cursor position.

    Recursively traverses the movetext tree from `cursor`, yielding structured
    [`MovetextLineStart`][libscid.MovetextLineStart],
    [`MovetextMove`][libscid.MovetextMove], and
    [`MovetextLineEnd`][libscid.MovetextLineEnd] events. When `variations` is
    True, sub-variation branches are explored in depth-first order immediately
    after the parent move from which they branch.

    Args:
        cursor: Starting [`Cursor`][libscid.Cursor] location for the traversal.
        variations: Whether to recursively traverse nested variation branches.
            Defaults to True.

    Yields:
        Sequential [`MovetextEvent`][libscid.MovetextEvent] instances.

    Raises:
        TypeError: If `cursor` is not a `Cursor` or `variations` is not a boolean.
        RuntimeError: If cursor navigation encounters an unexpected state.

    Examples:
        >>> import libscid
        >>> game = libscid.Game.from_pgn("1. e4 (1. d4 d5) e5 *")
        >>> moves = [
        ...     event.san for event in game.iter_movetext()
        ...     if isinstance(event, libscid.MovetextMove)
        ... ]
        >>> moves
        ['e4', 'd4', 'd5', 'e5']
    """
    if not isinstance(cursor, Cursor):
        raise TypeError("cursor must be a Cursor")
    if not isinstance(variations, bool):
        raise TypeError("variations must be bool")

    yield from _iter_line(cursor, variations=variations)

MovetextEvent

libscid.MovetextEvent module-attribute

Union of all movetext iteration event types.


MovetextLineStart

libscid.MovetextLineStart dataclass

MovetextLineStart(
    cursor: Cursor,
    preceding_comment: str | None,
    variation_depth: int,
    variation_index: int,
)

Event emitted at the beginning of a line or sub-variation branch.

Attributes:

Name Type Description
cursor Cursor

Cursor positioned at the start of this line or variation.

preceding_comment str | None

Optional introductory commentary text preceding the first move of the line, or None if absent.

variation_depth int

Variation nesting level (0 for the mainline, 1 for first-level sub-variations, etc.).

variation_index int

Sibling index among variations branching at this fork point (0 for mainline or first alternative).

Examples:

>>> import libscid
>>> game = libscid.Game.from_pgn("{Intro} 1. e4 e5 *")
>>> start_event = next(game.iter_movetext())
>>> isinstance(start_event, libscid.MovetextLineStart)
True
>>> start_event.preceding_comment
'Intro'
>>> start_event.variation_depth
0

MovetextMove

libscid.MovetextMove dataclass

MovetextMove(
    before: Cursor,
    after: Cursor,
    san: str,
    uci: str,
    nags: tuple[Nag, ...],
    comment: str,
    variation_depth: int,
    variation_index: int,
    position_before: Position,
    position_after: Position,
)

Event emitted for each move transition in the movetext tree.

Encapsulates the move in both Standard Algebraic Notation (SAN) and Universal Chess Interface (UCI) formats, associated NAG annotations, trailing commentary, variation nesting hierarchy, and board position snapshots immediately before and after the move.

Attributes:

Name Type Description
before Cursor

Cursor positioned immediately prior to this move.

after Cursor

Cursor positioned immediately following this move.

san str

Standard Algebraic Notation string (e.g. "e4", "Nf3", "O-O").

uci str

Universal Chess Interface coordinate string (e.g. "e2e4", "g1f3").

nags tuple[Nag, ...]

Tuple of Nag glyphs attached to this move.

comment str

Commentary text attached to this move, or empty string.

variation_depth int

Variation nesting level (0 for mainline).

variation_index int

Sibling variation index at the parent fork.

position_before Position

Position snapshot before the move.

position_after Position

Position snapshot after the move.

Examples:

>>> import libscid
>>> game = libscid.Game.from_pgn("1. e4 $1 {King pawn} e5 *")
>>> move = next(
...     e for e in game.iter_movetext()
...     if isinstance(e, libscid.MovetextMove)
... )
>>> move.san
'e4'
>>> move.uci
'e2e4'
>>> move.comment
'King pawn'

MovetextLineEnd

libscid.MovetextLineEnd dataclass

MovetextLineEnd(
    cursor: Cursor,
    variation_depth: int,
    variation_index: int,
)

Event emitted at the terminal end of a line or sub-variation branch.

Attributes:

Name Type Description
cursor Cursor

Cursor positioned at the end of this line or variation.

variation_depth int

Variation nesting level (0 for mainline).

variation_index int

Sibling variation index at the parent fork.

Examples:

>>> import libscid
>>> game = libscid.Game.from_pgn("1. e4 e5 *")
>>> events = list(game.iter_movetext())
>>> end_event = events[-1]
>>> isinstance(end_event, libscid.MovetextLineEnd)
True
>>> end_event.cursor.is_line_end
True