Reader

koskari.reader

Koskari s-expression reader and layout block parsing.

class koskari.reader.Atom

Bases: str

A raw atom string that has not yet been processed

exception koskari.reader.FormatStringSyntaxError(message, location=None, filename=None, text=None)

Bases: ReaderSyntaxError

Errors in format string (f-string) syntax

class koskari.reader.IndentAwareStream(stream, filename=None, auto_skip_exec=True, line_no=1, col_no=0, max_indent=None)

Bases: SourceStream

Source stream with indent-bounded skip_whitespace

Extends SourceStream with a max_indent threshold. When max_indent is set, skip_whitespace inspects the indent of the next content line without consuming it. If that indent is at or below max_indent, skip_whitespace sets self.dedented = True and returns without consuming any characters, leaving the stream positioned at the newline so the outer reader can handle the boundary.

When max_indent is None, skip_whitespace delegates to the native C implementation (BaseSourceStream.skip_whitespace) for maximum speed.

The dedented attribute is reset to False at the start of each skip_whitespace call. Callers read it immediately after skip_whitespace returns; it is never guarded by getattr.

classmethod from_source(source_stream, max_indent=None)

The layout stream over a plain SourceStream, continuing from its current position. Made once where a stream is created for layout reading; the plain stream itself is not read afterwards

Parameters:
  • source_stream – a SourceStream, its #! line already handled

  • max_indent – as for the constructor

indent_limit(max_indent)

Bound skip_whitespace by max_indent for the duration

While active, skip_whitespace sets dedented and stops without consuming when the next content line is at or below max_indent. The previous limit is restored on exit.

Parameters:

max_indent – column that ends a block body on dedent

skip_whitespace()

Skip whitespace, stopping at dedent when max_indent is set

When max_indent is None, delegates to the native C skip_whitespace.

Otherwise, speculatively scans the upcoming whitespace and blank lines to find the next content character. Blank lines are skipped, and a line holding only whitespace and a comment is a blank line: a comment is never content and its column is never an indent. The first non-blank content line is measured:

  • If the content is on the current line (no newline was crossed), the leading horizontal whitespace is consumed and the stream is left at the content.

  • If a newline was crossed and the content line’s indent is <= max_indent, sets self.dedented = True and consumes nothing, leaving the stream at the boundary.

  • If a newline was crossed and the indent is > max_indent, consumes the newline(s) and indent, leaving the stream at the content.

The scan is speculative (mark/rewind) so its lookahead is unbounded regardless of how many blank lines separate the content.

suspend_indent()

Temporarily disable indent-bounded whitespace skipping

Indentation has no meaning inside a balanced delimited form, so a pair read suspends the indent guard for the duration of its body. While suspended, max_indent is None and skip_whitespace consumes vertical whitespace freely (native behavior), letting a form span dedented lines up to its closing delimiter. The previous max_indent is restored on exit.

class koskari.reader.LayoutReader(namespace, nodefaults=False, layout_enabled=True, layout_implicit_calls=True, bracket_pairs=True)

Bases: Reader

Reader that supports layout-based block syntax

Subclasses Reader and adds indent-aware block reading. Resolves layout heads via namespace (layout/<head>). Requires a namespace for layout resolution, like the compiler.

Three default surfaces: (1) self.inner is a plain Reader with full s-expr defaults—balanced forms and prefix reads there use only inner state. (2) Bridge macros on this coordinator forward ( ) [ ] { } " ; f r b # into inner so delimiter and string/comment/prefix parsing matches s-expr mode; an index bracket attached to a value is the exception, read by this coordinator’s own suffix macro in header mode. (3) Layout-only registrations: structural :, _layout_macros (e.g. @), and stacked ' / backtick (predicated raw block then normal quote).

Top-level layout reads are line-oriented: header-mode values on a logical line are collected until LINE_END or structural COLON. Empty lines are skipped. Multiple values on one line fold to an implicit cons chain or raise per layout_implicit_calls. A structural colon ends the block header (head plus args); the body is read until dedent and passed to the layout handler (non-symbol heads use the layout/block fallback when no named layout matches). Structural : rules still allow keyword atoms such as as: inside header arguments at delimiter depth 0.

Stream interface contract: LayoutReader expects its stream arguments to be SourceStream instances (for pushback support). Body reads always use an IndentAwareStream so that dedent detection is available through the stream.dedented attribute rather than getattr guards.

Subclasses may override _add_default_layout_macros to register or omit layout-mode-only prefix handlers (_layout_macros). Raw block quote and quasiquote use predicated reader macros on ' and backtick when defaults are enabled.

Layout header tails use LINE_END and COLON events with horizontal-only whitespace between tokens on a logical line.

add_event_macro(char, macro_fn, terminating=False, predicate=None)

Add a character event macro to the reader

Each call prepends (macro_fn, terminating, predicate) to that character’s macro stack (newest first).

Parameters:
  • char – the character(s) to trigger on

  • macro_fn – function(stream, char) -> (event, value). The macro may return (DEFER, None) or (DEFER, repl) to decline handling and try the next handler from _event_macro_chain_for(); the chain always ends with _read_default. The opening character was already consumed; pass c through to _read_default. On DEFER, do not advance the stream beyond peek-only inspection.

  • terminating – if True, this character terminates atoms

  • predicate – optional (stream, char, atom_prefix) -> bool. If false before macro_fn runs at read dispatch, that entry is skipped (early DEFER). For terminating registrations, also participates in atom boundary decisions (see _atom_boundary_split()).

add_inner_event_macro(char, macro_fn, terminating=False, predicate=None)
add_layout_event_macro(char, macro_fn, terminating=False, predicate=None)
find_layout(name)
fold_elements(elements, position=None)

The form the elements of one line denote, by the line rule

Parameters:
  • elements – proper cons list of the line’s elements, never empty

  • position – the line’s position, stamped on a folded call

Returns:

the form

Raises:

LayoutError – several elements with implicit calls off

fold_line(entry)

The form one body entry denotes

A line is (:#line: ELEMENT ...) over the elements the reader saw on it. One element is that element; several fold to the call of the first, or raise when layout_implicit_calls is off. A block entry lowers through its layout to its form.

Parameters:

entry – a :#line: cons, never without elements, or a :#block: cons

Returns:

the entry’s form

Raises:

LayoutError – several elements with implicit calls off

fold_lines(entries)

Fold every entry of a body with fold_line()

Parameters:

entries – proper cons list of :#line: and :#block: conses

Returns:

proper cons list of forms

classmethod from_pragma(namespace, pragma=None)

Build a reader from a module pragma (a default pragma when omitted), apply its reader settings, and subscribe for later pragma changes.

property inner

The dedicated s-expression reader used inside bridge macros.

Attach s-expr-only extensions with reader.inner.add_event_macro.

property namespace

The namespace layout handlers resolve against

process_atom(atom)

Process a raw atom string into a typed value

Tries each atom pattern in order. If none match, returns a symbol.

Parameters:

atom – the raw atom string

Returns:

the processed value

read(reader_stream)

Read a single layout form from the stream

The stream must be an IndentAwareStream, made once for the whole source with IndentAwareStream.from_source(), so successive reads continue one position track and stream.dedented is always available to the layout read. A plain SourceStream is refused: a wrapper made per read would restart its line count at the stream’s stale position on every call.

A top-level form must start at column 0; layout_indent decides whether anything else is an error, a warning, or read as-is.

At EOF returns READ_EOF (same as Reader.read), not None, so callers can distinguish end-of-stream from a parsed Python None literal.

A LINE folds through fold_line(); BLOCK values are expanded via apply_layout() and BLOCKS spines via apply_layouts() (returning the expanded car), each with this reader.

Raises:

TypeError – when reader_stream is not an IndentAwareStream

read_and_position(reader_stream)

Read a single layout form and return it with the position it started at, as Reader.read_and_position does: a form that is an atom carries no position of its own, and this is where the compiler learns its line

Returns:

a tuple of (value, position) where position is (line, col); value is READ_EOF at EOF

Raises:

TypeError – when reader_stream is not an IndentAwareStream

read_block(stream, outer_indent, head=None, args=None)

Read a block as a :#block: cons without invoking layout handlers

Used when _block_mode is set (quote/quasiquote inner blocks). Sets _block_mode for the duration of the body read so nested colons also produce raw :#block: conses.

Parameters:
  • stream – the source stream, positioned after the header colon and its trailing whitespace

  • outer_indent – column of the block head line

  • head – block head symbol if already read by the caller

  • args – header argument forms if already read by the caller

Returns:

(:#block: (head . args) entry…) cons

read_block_body(stream, outer_indent)

Read body elements until dedent or EOF

stream must be an IndentAwareStream (guaranteed by LayoutReader.read wrapping the top-level stream, and by all internal callers using the same stream throughout). Temporarily installs outer_indent as the max_indent so that skip_whitespace stops and sets stream.dedented when the next content line is at or below that column. Restores the previous max_indent on exit.

Parameters:
  • stream – an IndentAwareStream

  • outer_indent – column that ends the block body on dedent

Returns:

list of body entries: a line, the proper cons list of the elements the reader saw on it, or a nested :#block: cons

read_block_header(stream)

Read the block head and arguments up to a structural header ‘:’.

Uses header-mode atom parsing so peer heads like else: are read as symbols, not keyword atoms.

Parameters:

stream – the source stream positioned at the first header token

Returns:

(head, args, opener) where head is the first form, args are the remaining forms before the colon, opener is symbol(‘:’)

Raises:

ReaderSyntaxError – on EOF or missing colon

read_form(stream)

Read one layout form using a line-oriented header-mode loop

When layout is disabled, delegates to Reader._read() on inner.

When layout is enabled: c in _layout_macros runs the layout-only prefix handler (e.g. @), provided its gate is open (see _layout_macro_enabled()). Otherwise collects header-mode values on the current logical line until LINE_END, EOF, or structural COLON (horizontal whitespace only between tokens). Empty lines are skipped. At LINE_END returns LINE with the elements as a proper list, unfolded. At COLON, returns BLOCK for a single :#block: cons, or BLOCKS for a primary :#block: plus stream-collected peer :#block: conses on a spine. Expansion is deferred to read().

Parameters:

stream – source stream positioned at the start of the next form

Returns:

(event, position, value)

reload_pragma(pragma)

Apply reader settings from a module pragma

Extends Reader.reload_pragma() with the layout switches (reader/layout, reader/implicit-calls, reader/decorators, and reader/indent) and mirrors every setting onto the inner s-expression reader, so both surfaces agree.

Parameters:

pragma – a module pragma, or None for a no-op

set_atom_pattern(namesym, match_fn, conversion_fn)

Add or update an atom pattern

Parameters:
  • namesym – symbol name for this pattern

  • match_fn – function(string) -> bool

  • conversion_fn – function(string) -> value

set_layout_modes(*, layout_enabled=None, layout_implicit_calls=None, layout_decorators=None, layout_indent=None)

Update layout-mode feature switches on this reader.

reload_pragma calls this with the pragma’s layout switches on every pragma change, so a direct call holds only until the next pragma change that sets the same switch. Any None parameter leaves the current setting unchanged.

temporary_event_macro(char, macro_fn, terminating=False, predicate=None)

Context manager to temporarily apply a character event macro

Applies the triplet to both the layout coordinator (via the base implementation) and the inner s-expr reader for the duration, so delimited reads inside the block see it too. Each side removes only its own triplet on exit.

Parameters:
  • char – the character(s) to trigger on

  • macro_fn – function(stream, char) -> (event, value); may return (DEFER, None) like other event macros (see add_event_macro).

  • terminating – if True, this character terminates atoms

  • predicate – optional predicate (see add_event_macro())

Yields:

self

class koskari.reader.Reader(nodefaults=False, bracket_pairs=True)

Bases: object

S-expression reader with configurable reader macros and atom patterns

The reader parses textual input into Koskari data structures. It supports reader macros for special character handling and atom patterns for converting atomic tokens into typed values.

Each character maps to a newest-first stack of (macro_fn, terminating, predicate_fn) triplets (see add_event_macro()); predicate_fn may be None. Optional predicates gate macro dispatch (early DEFER) and soft atom boundaries for terminating registrations. Event macros may return DEFER so the next handler in _event_macro_chain_for runs; the leading-character chain ends with _read_default.

Suffix macros are a second, parallel registry keyed the same way (see add_suffix_macro()). Where an event macro dispatches on the character that opens a token, a suffix macro dispatches on the character following a completed value, and wraps that value. Each consumes exactly one suffix and the driver re-enters, so chains across differing suffix kinds compose without any one macro knowing about the others.

static abandon_forms(forms, prefixes)

Release what the open forms and waiting prefixes hold, innermost first; after a completed read there is nothing left to release

add_event_macro(char, macro_fn, terminating=False, predicate=None)

Add a character event macro to the reader

Each call prepends (macro_fn, terminating, predicate) to that character’s macro stack (newest first).

Parameters:
  • char – the character(s) to trigger on

  • macro_fn – function(stream, char) -> (event, value). The macro may return (DEFER, None) or (DEFER, repl) to decline handling and try the next handler from _event_macro_chain_for(); the chain always ends with _read_default. The opening character was already consumed; pass c through to _read_default. On DEFER, do not advance the stream beyond peek-only inspection.

  • terminating – if True, this character terminates atoms

  • predicate – optional (stream, char, atom_prefix) -> bool. If false before macro_fn runs at read dispatch, that entry is skipped (early DEFER). For terminating registrations, also participates in atom boundary decisions (see _atom_boundary_split()).

add_macro_character(char, macro_fn, terminating=False, predicate=None)

Add a reader macro to the parser

Unlike add_event_macro, this wraps the macro function to always return a VALUE event; wrapped macros cannot DEFER.

Parameters:
  • char – the character to trigger on

  • macro_fn – function(stream, char) -> value

  • terminating – if True, this character terminates atoms

  • predicate – optional predicate (see add_event_macro())

add_suffix_macro(char, macro_fn, predicate=None)

Add a suffix macro to the reader

Suffix macros are the reader’s post macros: after any token is read as a VALUE, the driver peeks the next character and dispatches to that character’s macro stack. This is how attached-bracket indexing turns foo[x] into (item foo x).

Each call prepends (macro_fn, predicate) to that character’s macro stack (newest first), mirroring add_event_macro().

A macro consumes exactly one suffix and returns the value to carry forward; the driver re-enters with that value, so chains such as foo[a][b] fall out of dispatch rather than out of any one macro.

Parameters:
  • char – the character(s) to trigger on

  • macro_fn – function(stream, value, position) -> value. The trigger character has not been consumed. May return DEFER to decline, leaving the stream untouched beyond peek-only inspection, in which case the next handler from that character’s stack runs. A macro that has consumed input owes a reader error rather than a late DEFER, and one that claims a suffix without consuming anything is an error.

  • predicate – optional (stream, char, value) -> bool. If false, that entry is skipped without calling macro_fn (early DEFER). Feature gating belongs here, where the value the suffix would attach to is available.

apply_prefixes(stream, pending, position, value)

Wrap a completed value in the prefixes waiting for it, innermost first, each wrapped form taking its prefix’s position and its own suffix macros

Returns:

(position, value) of the outermost wrapped form

clear_atom_pattern(namesym)

Remove an atom pattern by name

Parameters:

namesym – symbol name to remove

clear_event_macro(char)

Remove all stacked event macros for each character in char

Parameters:

char – the character(s) to clear

clear_suffix_macro(char)

Remove all stacked suffix macros for each character in char

Parameters:

char – the character(s) to clear

close_form(stream, form, position, closechar)

Close an open list at its closing delimiter and hand back the finished value with the position of its opener

drive_forms(stream, forms, prefixes, raw, vertical_ws)

The form driver’s loop: one event at a time until the outermost form completes or an event the caller must judge arrives

Parameters:
  • forms – the stack of open forms, innermost last

  • prefixes – the prefixes waiting at the outermost level

Returns:

tuple of (event, position, value)

eof_error(stream, forms, pending)

The error for input ending with a prefix or a form still open: the innermost one reports

form_append(stream, form, value, position)

A completed value inside an open list

form_dot(stream, form, position)

A dot inside an open list: the next value is the tail

form_eof(stream, form)

The error for input ending inside an open list: EOF at the opener whatever the form’s state, since a list that has read its dot, or its dot and its tail, is completed by more input as any open list is. A misplaced dot is an error where it sits, raised by form_dot and the value step, never here.

get_atom_pattern(namesym)

Get an atom pattern by name

Parameters:

namesym – symbol name to look up

Returns:

the pattern list [name, match, conv] or None

get_event_macro(char)

Get the newest character event macro from the reader

Parameters:

char – the character to look up

Returns:

tuple of (macro_fn, terminating, predicate_fn) or None

get_event_macros(char)

Return stacked event macros for one character

Parameters:

char – single character to look up

Returns:

list of (macro_fn, terminating, predicate_fn), newest first; empty if none

get_suffix_macro(char)

Get the newest suffix macro from the reader

Parameters:

char – the character to look up

Returns:

tuple of (macro_fn, predicate) or None

get_suffix_macros(char)

Return stacked suffix macros for one character

Parameters:

char – single character to look up

Returns:

list of (macro_fn, predicate), newest first; empty if none

nesting_guard(stream)

Report recursion that runs out as a positioned reader error, at the point the read had reached

open_form(stream, open_, position)

Enter an open list: its own cell kind, the base terminators, indentation suspended, and the macro its opener holds

process_atom(atom)

Process a raw atom string into a typed value

Tries each atom pattern in order. If none match, returns a symbol.

Parameters:

atom – the raw atom string

Returns:

the processed value

read(reader_stream)

Read a single s-expression from the stream

Returns a cons cell, symbol, or numeric value. Returns READ_EOF if no data remains in the stream.

Parameters:

reader_stream – the source stream to read from

Returns:

the parsed value, or READ_EOF at EOF

Raises:

ReaderSyntaxError – on syntax errors

read_and_position(reader_stream)

Read a single s-expression and return its position

Parameters:

reader_stream – the source stream to read from

Returns:

a tuple of (value, position) where position is (line, col); value is READ_EOF at EOF

Raises:

ReaderSyntaxError – on syntax errors

read_atom(reader_stream)

Read a single s-expression, returning atoms as unprocessed strings

Returns a cons cell, or an unprocessed atomic value

Parameters:

reader_stream – the source stream to read from

Returns:

the parsed value, or READ_EOF at EOF

Raises:

ReaderSyntaxError – on syntax errors

read_event(stream, raw=False, vertical_ws=True)

Read one event: a complete atom or value, or what a delimiter or prefix macro reports for the driver to carry on with

Parameters:
  • stream – the source stream

  • raw – if True, return atoms without processing

  • vertical_ws – if True (default), skip all whitespace including newlines before each token; if False, skip only horizontal whitespace (layout header tails and logical-line reads)

Returns:

tuple of (event, position, value)

read_pair(stream, openchar=None, closechar=None)

Read a pair (list) from the stream

Parameters:
  • stream – the source stream

  • openchar – the opening character (read if None)

  • closechar – the closing character (inferred if None)

Returns:

the parsed pair

static ref_called(child, pos)
reload_pragma(pragma)

Apply reader settings from a module pragma to this reader

Covers every setting a plain reader carries: reader/brackets, reader/index-brackets, reader/dot-attrs, reader/string-segments, reader/unary, and reader/fences.

Raw reader defaults are deliberately bare, so this is how a directly-constructed reader is brought up to the pragma’s baseline. Tooling that builds a reader and skips this step sees none of the pragma-gated syntax, which is rarely what it wants.

Parameters:

pragma – a module pragma, or None for a no-op

set_atom_pattern(namesym, match_fn, conversion_fn)

Add or update an atom pattern

Parameters:
  • namesym – symbol name for this pattern

  • match_fn – function(string) -> bool

  • conversion_fn – function(string) -> value

set_atom_regex(namesym, regexstr, conversion_fn)

Add an atom pattern using a regex string

Parameters:
  • namesym – symbol name for this pattern

  • regexstr – regex pattern string

  • conversion_fn – function(string) -> value

temporary_event_macro(char, macro_fn, terminating=False, predicate=None)

Context manager to temporarily apply a character event macro

Prepends one (macro_fn, terminating, predicate) triplet shared across char. On exit, removes only that triplet from the head of each affected list when it still matches by identity.

Parameters:
  • char – the character(s) to trigger on

  • macro_fn – function(stream, char) -> (event, value); may return (DEFER, None) like other event macros (see add_event_macro).

  • terminating – if True, this character terminates atoms

  • predicate – optional predicate (see add_event_macro())

Yields:

self

temporary_macro_character(char, macro_fn, terminating=False, predicate=None)

Context manager to temporarily apply a reader macro

Parameters:
  • char – the character to trigger on

  • macro_fn – function(stream, char) -> value

  • terminating – if True, this character terminates atoms

  • predicate – optional predicate (see add_event_macro())

Returns:

context manager

temporary_suffix_macro(char, macro_fn, predicate=None)

Context manager to temporarily apply a suffix macro

Prepends one (macro_fn, predicate) entry shared across char. On exit, removes only that entry from the head of each affected list when it still matches by identity.

Parameters:
  • char – the character(s) to trigger on

  • macro_fn – function(stream, value, position) -> value; may return DEFER like other suffix macros (see add_suffix_macro()).

  • predicate – optional predicate (see add_suffix_macro())

Yields:

self

terminators_extended(chars)

Read with extra atom terminators in force at this level: the atom collector takes the terminator string at each call, so an atom read under this context stops at chars too, while a delimited form read inside it (_read_pair) reads its contents with the base terminators. Nests, restoring the previous set on exit

Parameters:

chars – the characters to add

exception koskari.reader.ReaderEOF(message, location=None, filename=None, text=None)

Bases: ReaderError

Stream ended before a delimited or quoted form finished reading.

Distinct from top-level READ_EOF (no form started). REPL callers may treat this as incomplete input; file loaders should surface it as an error.

exception koskari.reader.ReaderSyntaxError(message, location=None, filename=None, text=None)

Bases: ReaderError

Low-level parse errors during s-expression or layout reading

class koskari.reader.SourceStream(stream, filename=None, auto_skip_exec=True, line_no=1, col_no=0)

Bases: BaseSourceStream

Source stream with error reporting helpers

Inherits from the native BaseSourceStream for efficient character-level I/O with position tracking. Adds error reporting and the helper methods used by the reader (skip_exec, suspend_indent).

dedented = False
error(message, position=None)

Create a ReaderSyntaxError for the given or current position

Parameters:
  • message – the error message

  • position – optional (line, col) tuple; uses current if None

Returns:

a ReaderSyntaxError instance

peek_line()

Peek the rest of the current physical line without consuming it

Returns the text from the current position up to (not including) the next newline or carriage return, or to end of stream. Unbounded: the result is the whole line regardless of length.

Returns:

the remaining text on the current physical line

reader_eof(position=None, *, context=None, opened_at=None)

Build a ReaderEOF for stream end inside an unfinished form

Does not read the source file for a source line snippet; callers raise this immediately and REPL parse paths map it to incomplete input without displaying the exception.

When the unfinished form has an opener, a quote, a bracket, a field brace, the error is located at that opener and the message names it, so a reader of the error is pointed at what was never closed rather than at the end of the input.

Parameters:
  • position – optional (line, col) tuple; uses current if None

  • context – optional phrase naming the unfinished form

  • opened_at – (line, col) of the form’s opener; when given it becomes the error location and is named in the message

Returns:

a ReaderEOF instance (caller should raise it)

skip_exec()

Skip a #! line at the start of the stream

speculate()

Mark the stream on entry and rewind to that mark on exit

For pure lookahead: read freely inside the block to inspect upcoming input; the stream (position and line/col) is restored to its pre-block state on exit, whether the block returns or raises. Unlike a fixed-size peek, a speculative scan sees the whole remaining input.

suspend_indent()

No-op indent suspension for streams without indent tracking

Streams that do not bound whitespace skipping by indentation have nothing to suspend, so this simply yields.

koskari.reader.source_open(filename, auto_skip_exec=True)

Context manager to open a file as a SourceStream

The file is read whole and closed before the stream is handed out, so the module body runs with no file open: text appended to the file while it runs is not executed, a fork does not split the read position, and the native stream’s tell and seek are index operations on the text rather than a text-wrapper’s decoder replay. Newlines are normalized as for a string source.

Parameters:
  • filename – path to the file to open

  • auto_skip_exec – if True, skip any #! line at the start

Yields:

SourceStream for the file

koskari.reader.source_str(src, filename, auto_skip_exec=True, line_no=1, col_no=0)

Create a SourceStream from a string

Newlines are normalized to \n so string sources behave like files opened in text mode; the reader then never sees a carriage return.

Parameters:
  • src – the source string

  • filename – the filename to report in errors

  • auto_skip_exec – if True, skip any #! line at the start

  • line_no – starting line number

  • col_no – starting column number

Returns:

a SourceStream

koskari.reader.source_stream(underlying, filename, auto_skip_exec=True)

Create a SourceStream wrapping an existing stream

Parameters:
  • underlying – the stream to wrap

  • filename – the filename to report in errors

  • auto_skip_exec – if True, skip any #! line at the start

Returns:

a SourceStream