pipelogic.types

The pipelang type system, exposed to Python.

Use this module when you need to construct typed values explicitly. Most of the time the higher-level pipelogic.cv wrappers do this for you.

Module-level constructors

Atomic constructors

from pipelogic.types import (
    create_int32, create_uint32, create_int64, create_uint64,
    create_bool, create_char, create_float, create_double,
    create_string, create_bytes,
)

Each returns a pipelang Type handle for the named atomic. You'll mostly use the high-level Python wrappers (Int32(42), String("hi")) instead of these constructors directly.

Built-in Maybe<t>

Maybe<t> is a built-in option type, semantically Nothing | Just<t>. The Cond transformation produces it; coercion lets a Maybe<t> flow into a t-receiving consumer through unpack_named / unpack_union. In C++ the same type is exposed as ppl::Maybe<t>.

You don't register Maybe — it's always available. Use it directly in component.yml:

worker:
  input_type: "Maybe<Image>"
  output_type: "Maybe<BoundingBox>"

Composite constructors

Three functions cover almost everything you'll need:

create_type(expr) → Type

Parses a pipelang type expression and returns a reusable Type handle.

from pipelogic.types import create_type

LIST_OF_INTS  = create_type("[Int32]")
POINT         = create_type("{x: Float, y: Float}")
IMAGE_OR_TEXT = create_type("Image | String")

Build the handle once at module scope and reuse it — re-parsing the expression on every tick is wasted work.

register_named(pairs) → None

Registers one or more named types globally. Idempotent — safe to call multiple times. Call it once at import time, near your wrapper-class definitions.

from pipelogic.types import register_named

register_named([
    ("Vector3D",   "{x: Float, y: Float, z: Float}"),
    ("Trajectory", "[Vector3D]"),
])

After registration, the names are available everywhere — in component.yml, in other components, and via get_type.

get_type(name) → Type

Looks up a previously registered named type.

from pipelogic.types import get_type

IMAGE   = get_type("Image")
VECTOR  = get_type("Vector3D")

Lower-level shape-specific constructors — create_record, create_list, create_tuple, create_union, create_named — are also exported. They produce the same Type handles as create_type but skip the expression parser. Reach for them when you're building types programmatically (e.g. assembling a record whose fields aren't known statically).

Wrapper classes

Every typed value has a Python wrapper class. They all inherit from PipeObject. The worker runtime serializes them automatically when you return them from your function — you don't call any serialization method yourself.

Atomic wrappers

Int32, Int64, UInt32, UInt64, Bool, Char, Float, Double all derive from ConstSizeAtomic. They support .value() and equality with native Python numerics.

from pipelogic.types import Int32, Bool

Int32(42).value()        # 42
Int32(42) == 42          # True
Bool(True) == True       # True

The default Python int maps to Int64 and the default Python float maps to Double. To use Int32 or Float, construct explicitly or pass a type string to a composite constructor.

String

from pipelogic.types import String

s = String("hello")
str(s)           # "hello"
len(s)           # 5
s == "hello"     # True

Bytes

A binary-safe blob with a numpy buffer protocol.

from pipelogic.types import Bytes
import numpy as np

b = Bytes(b"\x01\x02\x03")
b[0]                       # 1   (int, not b"\x01")
np.asarray(b)              # array([1, 2, 3], dtype=uint8)
np.array(b, copy=True)     # explicit copy
b.unsafe_numpy(dtype=np.uint8, shape=(3,))   # zero-copy view
b.safe_numpy()             # always returns a fresh copy

Bytes also supports indexing, len, __contains__, and Bytes.from_numpy(ndarray) to construct from raw numpy data.

List

A typed sequence. The element type is inferred from the first element or supplied as a type string.

from pipelogic.types import List

L = List([1, 2, 3])                     # element type inferred as Int64
L = List([1, 2, 3], '[Int32]')          # explicit Int32
L.append(4)
L[0] = 99
L.numpy_view = L.unsafe_numpy(shape=(4,))    # zero-copy view
L_arr = np.asarray(L)                   # zero-copy via __array__
L.safe_numpy()                          # always copies

List supports append, extend, clear, pop, index, count, __contains__, __add__, __mul__, slicing, and the numpy buffer protocol.

List.from_numpy(ndarray, type_string=None) is the canonical fast path for ndarray-backed lists. The element type is inferred from the dtype.

Tuple

A fixed-length positional product type.

from pipelogic.types import Tuple

t = Tuple(("Life of Brian", 1979), '(String, Int32)')
t[0]              # String("Life of Brian")
t[1] = 1980       # mutable
len(t)            # 2

Record

A named-field product type. Fields are accessed by attribute or key.

from pipelogic.types import Record

r = Record({"name": "Ada", "age": 36}, '{name: String, age: UInt32}')
r.name            # String("Ada")
r["age"]          # UInt32(36)
r.keys()          # ["name", "age"]
r.items()         # iterable of (key, value)
"name" in r       # True

You can also let Record infer the type from values when unambiguous:

r = Record({"name": "Ada", "age": 36})

Named

A type alias wrapper. Used for domain types like Image, BoundingBox, AudioFrame. Once a name is registered with register_named, you can construct values by name:

from pipelogic.types import Named, Tuple, get_type, register_named

register_named([
    ("EmptyToken", "()"),
])

token = Named(Tuple(()), get_type("EmptyToken"))
token.type_name()   # "EmptyToken"
token.get()         # Tuple(())

Most components don't construct Named directly — they use the pipelogic.cv wrappers which call Named for you.

Union

A discriminated sum-type wrapper. The first argument is the value, the second is the union type.

from pipelogic.types import Union, create_type

UNION_TYPE = create_type("Int32 | String")
v = Union("oops", UNION_TYPE)
v.get()              # String("oops")
v.get_type()         # the resolved variant type

Numpy buffer protocol

Bytes and List participate in the numpy buffer protocol via __array__. The semantics are copy-on-write by default:

  • np.asarray(x) — zero-copy view, valid for the current tick.
  • np.array(x) — explicit copy.
  • x.unsafe_numpy(...) — zero-copy view (you're responsible for lifetime).
  • x.safe_numpy() — always returns a copy.

Writing through a np.asarray view writes back into the underlying buffer. That's intentional — preprocessing backends operate on the view directly. But it means don't keep the view past the next runtime tick.

Type strings

create_type and the second argument to List, Tuple, Record, etc. accept a pipelang type expression. The full grammar:

FormExample
AtomicInt32, Float, String, Bytes, Bool, Char
List[Int32], [[Float]]
Tuple(Float, Float), (String, Int32, Bool)
Record{x: Float, y: Float}
UnionInt32 | Float, Image.BGR | Image.RGB
Named lookupImage, BoundingBox, any registered name
Type variablet, t0, t1
GenericMaybe<t>, [Maybe<t>]
Variadic pack$ts... (used in worker input declarations)

Whitespace around :, ,, and | is ignored.

Constants

from pipelogic.types import (
    PIPELOGIC_TYPES,                       # tuple of all wrapper classes
    PIPELOGIC_CONST_SIZE_ATOMIC_TYPES,     # tuple of atomic wrappers
    Type,                                  # pipelang Type handle
    TypeManager,                           # global type registry
)

isinstance(value, PIPELOGIC_TYPES)         # True for any wrapper instance

What's next

  • Concepts — how types interact with streams and workers.
  • API: cv — the prebuilt wrappers for Image, Tensor, AudioFrame, etc.
  • Footgunsnumpy() removed, COW semantics, single-output tuple wrapping.

Was this page helpful?