Skip to main content

zopy

Zod for Python — the Zod 4 API, typed for Pylance / pyright, validated by a Rust engine.

from zopy import z


class User(z.Object):
    name = z.string().min(1)
    email = z.email()
    age = z.int().nonnegative().optional()


user = User.parse({"name": "Ada", "email": "ada@example.com"})  # => User
user.name  # => "Ada"  (str)
user.age  # => None   (int | None)

Schemas, checks and helpers keep Zod's names and behaviour (z.string().min(1), safeParse, refine, transform, pipe, codec, discriminatedUnion, prettifyError, toJSONSchema, …), so the Zod documentation is the API reference. This README covers what is specific to zopy: how Zod maps onto Python.

Contents

Installation

zopy requires Python 3.11+:

uv add zopy
# or
pip install zopy

Everything is reached through z:

from zopy import z  # or: import zopy as z

Quick start

class Player(z.Object):
    username = z.string()
    xp = z.number()


player = Player.parse({"username": "billie", "xp": 100})  # => Player
player.username  # => "billie"

result = Player.safeParse({"username": 42, "xp": "100"})
if not result.success:
    print(z.prettifyError(result.error))
    # ✖ Invalid input: expected string, received number
    #   → at username
    # ✖ Invalid input: expected number, received string
    #   → at xp

.parse() returns the parsed value or raises z.ZodError; .safeParse() returns SafeParseSuccess[Player] | SafeParseError, and checking result.success narrows it.

Differences from Zod

Zod (TypeScript) zopy (Python)
z.object({ name: z.string() }) class X(z.Object): name = z.string()
z.strictObject({...}) / z.looseObject({...}) class X(z.StrictObject) / class X(z.LooseObject)
A.extend({...}) / A.merge(B) a subclass: class C(A): ...
A.pick / omit / partial / required declare the shape you need as its own class
z.intersection(A, B) / A.and(B) multiple inheritance: class C(A, B): ...
z.infer<typeof X> the class X, or the return type of parse()
z.enum(["a", "b"]) z.enum(("a", "b")) — a tuple
z.tuple([a, b]) z.tuple((a, b)) — a tuple
undefined None (and a missing key)
.or(b) .or_(b) or a | b
.brand<"UserId">() .brand(UserId) with UserId = NewType("UserId", str)

Only APIs whose output type pyright can infer are provided. That is why object schemas are classes, and why the shape-deriving helpers (pick, omit, partial, extend, merge, intersections) and list-form z.enum([...]) / z.tuple([...]) are not: their results could only be typed as Any. Most camelCase names also have a snake_case alias (safe_parse, to_json_schema, …).

zopy implements Zod 4 only. Zod 3 syntax — the message, required_error, invalid_type_error and errorMap params, .strict() / .passthrough(), z.string().email() and the other format methods, z.nativeEnum, error.format() / .flatten() … — is not provided.

Schemas

The primitive schemas and their checks. Objects are covered in Objects are classes.

Primitives

z.string()  # str
z.number()  # float (ints are accepted; bool is not)
z.int()  # int
z.bigint()  # int (no safe-integer bound)
z.boolean()  # bool
z.date()  # datetime / date
z.symbol()

# None-like
z.null()  # None
z.undefined()  # None
z.void()  # None

# catch-all
z.any()
z.unknown()

# accepts nothing
z.never()
z.nan()  # float("nan")

Coercion

z.coerce converts the input before validating it:

z.coerce.string().parse(42)  # => "42"
z.coerce.number().parse("4.5e2")  # => 450.0
z.coerce.boolean().parse("false")  # => True   (truthiness, like JavaScript's Boolean())
z.coerce.bigint().parse("99")  # => 99
z.coerce.date().parse("2024-01-01T00:00:00Z")  # => datetime(2024, 1, 1, tzinfo=utc)
z.coerce.date().parse(1_704_067_200_000)  # => datetime(2024, 1, 1, tzinfo=utc)  (epoch ms)

Literals

tuna = z.literal("tuna")  # Literal["tuna"]
twelve = z.literal(12)
yes = z.literal(True)

colors = z.literal(["red", "green", "blue"])  # any of several literals
colors.parse("green")  # => "green"

Strings

z.string().max(5)
z.string().min(5)
z.string().length(5)
z.string().nonempty()
z.string().regex(r"^[a-z]+$")
z.string().startsWith("aaa")
z.string().endsWith("zzz")
z.string().includes("---")
z.string().uppercase()
z.string().lowercase()

String transforms run as part of parsing:

z.string().trim().parse("  hello  ")  # => "hello"
z.string().toLowerCase().parse("HeLLo")  # => "hello"
z.string().toUpperCase().parse("hello")  # => "HELLO"
z.string().normalize().parse("hello")  # NFC by default; .normalize("NFKC") etc.

String formats

Formats are top-level functions, as in Zod 4.

z.email()
z.uuid()
z.uuidv4()
z.uuidv7()
z.url()
z.httpUrl()  # http / https only
z.hostname()
z.emoji()
z.base64()
z.base64url()
z.hex()
z.jwt()
z.nanoid()
z.cuid()
z.cuid2()
z.ulid()
z.ipv4()
z.ipv6()
z.mac()
z.cidrv4()
z.cidrv6()
z.hash("sha256")  # or "sha1", "sha384", "sha512", "md5"
z.e164()  # phone numbers
z.creditCard()  # Luhn check
z.iban()
z.currencyCode()  # ISO 4217
z.iso.date()
z.iso.time()
z.iso.datetime()
z.iso.duration()

Formats take options:

z.email({"pattern": z.regexes.html5Email})  # the regex browsers use
z.url({"protocol": r"^https$", "hostname": r"^example\.com$"})
z.iso.datetime({"offset": True})  # allow +09:00 offsets (default: Z only)
z.iso.datetime({"precision": 3})  # exactly millisecond precision
z.mac({"delimiter": "-"})
z.jwt({"alg": "HS256"})
z.hash("sha256", {"enc": "base64url"})

Define your own format with a regex or a predicate:

ticket = z.stringFormat("ticket", r"^[A-Z]+-\d+$")
cool = z.stringFormat("cool-id", lambda value: value.startswith("cool-"))

ticket.parse("ABC-123")  # => "ABC-123"
cool.safeParse("nope").error.issues[0]["message"]  # => "Invalid cool-id"

Template literals

css = z.templateLiteral([z.number(), z.enum(("px", "em", "rem"))])
css.parse("1.5rem")  # => "1.5rem"
css.safeParse("1.5pt").success  # => False

Numbers

z.number() accepts any finite int or float (not bool, not NaN / infinity).

z.number().gt(5)
z.number().gte(5)  # alias: .min(5)
z.number().lt(5)
z.number().lte(5)  # alias: .max(5)
z.number().positive()
z.number().nonnegative()
z.number().negative()
z.number().nonpositive()
z.number().multipleOf(5)  # alias: .step(5)

Integers

z.int()  # a safe integer; 5.0 is accepted and returned as 5
z.int32()
z.uint32()

z.int().parse(5.0)  # => 5
z.int().safeParse(5.5).success  # => False

BigInts

Python has one int type; z.bigint() is an int without the safe-integer bounds of z.int():

z.bigint().parse(2**80)  # => 1208925819614629174706176
z.int64()
z.uint64()

Booleans

z.boolean().parse(True)  # => True
z.boolean().safeParse(1).success  # => False

Stringbool

z.stringbool() parses environment-variable-style strings into bool:

flag = z.stringbool()
flag.parse("yes")  # => True
flag.parse("OFF")  # => False   (case-insensitive by default)

z.stringbool({"truthy": ["はい"], "falsy": ["いいえ"]}).parse("はい")  # => True

Dates

z.date() accepts datetime and date objects:

from datetime import datetime, timezone

z.date().min(datetime(1900, 1, 1, tzinfo=timezone.utc))
z.date().max(datetime(2100, 1, 1, tzinfo=timezone.utc))

For ISO 8601 strings, use z.iso.date(), z.iso.datetime() … (see String formats).

Enums

Pass a tuple of strings — a tuple (unlike a list) keeps the literal types:

Fish = z.enum(("Salmon", "Tuna", "Trout"))
Fish.parse("Salmon")  # => "Salmon"  (Literal["Salmon", "Tuna", "Trout"])
Fish.safeParse("Swordfish").success  # => False

Fish.enum.Salmon  # => "Salmon"
Fish.options  # => ["Salmon", "Tuna", "Trout"]

Fish.extract(("Salmon", "Trout")).options  # => ["Salmon", "Trout"]
Fish.exclude(("Salmon",)).options  # => ["Tuna", "Trout"]

Python Enum classes work too; members and their values are accepted and a member is returned:

from enum import Enum


class Color(Enum):
    RED = "red"
    GREEN = "green"


z.enum(Color).parse("red")  # => Color.RED
z.enum(Color).parse(Color.GREEN)  # => Color.GREEN

Optionals and nullables

z.string().optional()  # str | None — the key may be missing (or None)
z.string().nullable()  # str | None — None is allowed, but the key must be present
z.string().nullish()  # both
z.string().exactOptional()  # the key may be missing, but an explicit None is rejected

z.string().optional().unwrap()  # => the inner z.string()
z.string().optional().nonoptional()  # rejects None again

Objects are classes

An object schema is a class deriving from z.Object. The class is the schema, and its instances are the parsed values, so the class is also the type:

class Dog(z.Object):
    name = z.string()
    age = z.int().nonnegative()
    tags = z.array(z.string()).default(list)
    nickname = z.string().optional()


dog = Dog.parse({"name": "Rex", "age": 3})  # => Dog(name='Rex', age=3, tags=[])

Fields are assigned, not annotated: name: z.string() raises TypeError when the class is defined. Attributes starting with _ and ClassVars are ordinary class attributes.

Instances

An instance is an attribute object that also reads like a mapping:

dog.name  # => "Rex"
dog["name"]  # => "Rex"
dog.get("nickname", "-")  # => "-"
dog.to_dict()  # => {"name": "Rex", "age": 3, "tags": []}
dog == {"name": "Rex", "age": 3, "tags": []}  # => True

The constructor validates too:

Dog(name="Fido", age=1)  # => Dog(name='Fido', age=1, tags=[])
try:
    Dog(name="Fido", age=-1)
except z.ZodError:
    pass  # age: Too small

Nesting

A class is used as a field like any other schema, and z.array(Dog), z.union([Cat, Dog]), Dog.optional() … keep the class as the type:

class Owner(z.Object):
    name = z.string()
    dog = Dog
    pets = z.array(Dog).default(list)


owner = Owner.parse({"name": "Ada", "dog": {"name": "Rex", "age": 3}})
owner.dog.name  # => "Rex"   (owner.dog is a Dog)

Refer to a class before it exists (recursion) with z.lazy:

class Category(z.Object):
    name = z.string()
    subcategories = z.lazy(lambda: z.array(Category)).default(list)


Category.parse({"name": "root", "subcategories": [{"name": "leaf"}]}).subcategories[0].name
# => "leaf"

Unknown keys

Unknown keys are stripped by default. Choose another policy with the base class or a class keyword:

class Strict(z.StrictObject):  # unknown keys are an error
    a = z.string()


class Loose(z.LooseObject):  # unknown keys are kept
    a = z.string()


class Extras(z.Object, catchall=z.number()):  # unknown keys must be numbers
    a = z.string()


Strict.safeParse({"a": "x", "b": 1}).success  # => False
Loose.parse({"a": "x", "b": 1}).to_dict()  # => {"a": "x", "b": 1}

class X(z.Object, strict=True) / loose=True are the same. For a strict or loose variant of an existing class, inherit from it: class StrictDog(Dog, z.StrictObject): ....

Deriving shapes

Where Zod derives a shape (extend, pick, omit, partial), declare it as a class and share fields through inheritance, so every shape stays fully typed:

class Animal(z.Object):  # the smaller shape is the base
    name = z.string()


class Cat(Animal):  # Animal.extend({ lives: ... })
    lives = z.int().min(0).max(9)


class CatPatch(z.Object):  # Cat.partial()
    name = z.string().optional()
    lives = z.int().min(0).max(9).optional()


isinstance(Cat.parse({"name": "Tom", "lives": 9}), Animal)  # => True

Combining objects

The intersection of object schemas is multiple inheritance:

class Person(z.Object):
    name = z.string()


class Employee(z.Object):
    role = z.string()


class EmployedPerson(Person, Employee):  # z.intersection(Person, Employee)
    pass

If two bases declare the same field differently, defining the subclass raises TypeError until the subclass redeclares the field — no base's checks are silently dropped.

Class-level helpers

Methods on the class keep the class as the type, and callbacks receive instances:

class SignupForm(z.Object):
    password = z.string().min(8)
    confirm = z.string()


Signup = SignupForm.refine(
    lambda form: form.password == form.confirm,  # form: SignupForm
    {"error": "Passwords don't match", "path": ["confirm"]},
)

Dog.default({"name": "Rex", "age": 3})  # a dict default is parsed into a Dog
Dog.shape  # => {"name": ZodString(), "age": ZodInt(), "tags": ZodDefault(), "nickname": ZodOptional()}
Dog.keyof().options  # => ["name", "age", "tags", "nickname"]

Type inference

There is no z.infer (Python has no typeof); the type lives on parse(), and for objects on the class. Hover a value in VS Code to see it:

z.string().trim().parse("  Ada  ")  # => "Ada"           str
z.int().optional().parse(None)  # => None            int | None
z.enum(("admin", "user")).parse("admin")  # => "admin"         Literal["admin", "user"]
z.tuple((z.string(), Dog)).parse(["x", {"name": "Rex", "age": 3}])  # tuple[str, Dog]
z.union([z.string(), z.int()]).parse(1)  # => 1               int | str
z.record(z.string(), Dog).parse({})  # => {}              dict[str, Dog]
z.string().transform(len).parse("hello")  # => 5               int

Lambdas get their parameter types from the schema (transform(lambda s: ...) knows s: str), and the code that uses parsed values is type-checked as usual.

Pass tuples to z.enum and z.tuple: a tuple keeps each literal / position type, a list would not (and is rejected).

Python types

Zod zopy schema Python value
string z.string() str
number z.number() float (an int is accepted; bool is not)
int z.int() int (a safe integer; 5.0 is returned as 5)
bigint z.bigint() int (no safe-integer bound)
boolean z.boolean() bool
Date z.date() datetime / date
null / undefined z.null() / z.undefined() None
T[] z.array(t) list (a tuple input is accepted)
[A, B] z.tuple((a, b)) tuple
Record<K, V> z.record(k, v) dict
Map<K, V> z.map(k, v) dict (keys of any type)
Set<T> z.set(t) set
File z.file() bytes
TS enum z.enum(MyEnum) the Enum member (values are accepted as input)
branded type .brand(MyNewType) the typing.NewType
from enum import Enum
from typing import NewType


class Color(Enum):
    RED = "red"


UserId = NewType("UserId", str)

z.enum(Color).parse("red")  # => Color.RED
z.uuid().brand(UserId).parse("123e4567-e89b-12d3-a456-426614174000")  # UserId

Missing keys and None

None plays the part of undefined. An optional field that was not given stays absent from the instance, but reading it as an attribute gives None:

dog = Dog.parse({"name": "Rex", "age": 3})
"nickname" in dog  # => False
dog.nickname  # => None
dog.to_dict()  # => {"name": "Rex", "age": 3, "tags": []}

dog = Dog.parse({"name": "Rex", "age": 3, "nickname": None})
"nickname" in dog  # => True

.exactOptional() accepts a missing key but rejects an explicit None.

Readonly

.readonly() freezes the parsed value shallowly (like Zod's Object.freeze), and the output type is the frozen type:

schema value type
z.array(t).readonly() tuple tuple[T, ...]
z.record(k, v).readonly() / z.map(k, v).readonly() mappingproxy Mapping[K, V]
z.set(t).readonly() frozenset frozenset[T]
z.tuple(...).readonly() tuple the same tuple[...]
MyObject.readonly() a frozen instance MyObject
tags = z.array(z.string()).readonly().parse(["a"])  # => ("a",)   tuple[str, ...]
# tags.append("b") is a type error, as it would be in TypeScript

frozen = Dog.readonly().parse({"name": "Rex", "age": 3})
try:
    frozen.name = "Max"
except AttributeError:
    pass  # Dog is readonly: cannot change 'name'

A frozen instance rejects attribute assignment and deletion at runtime. Python's type system has no Readonly<T> for classes, so pyright does not flag frozen.name = ... statically.

Brands

Zod's .brand<"UserId">() makes a nominal type: structurally a string, but not assignable from a plain string or from another brand. Python's equivalent is typing.NewType, so .brand() takes one:

from typing import NewType

UserId = NewType("UserId", str)
OrderId = NewType("OrderId", str)

UserIdSchema: z.ZodType[UserId] = z.uuid().brand(UserId)

uid = UserIdSchema.parse("123e4567-e89b-12d3-a456-426614174000")  # UserId
z.array(UserIdSchema).parse([])  # list[UserId]


def load_user(user_id: UserId) -> None: ...


load_user(uid)  # OK
load_user("123e4567-e89b-12d3-a456-426614174000")  # type error: a plain str is not a UserId
load_user(OrderId("x"))  # type error: nor is another brand

Annotate the schema variable (z.ZodType[UserId]): type checkers cannot infer the branded type from a NewType argument, so the annotation is what gives .parse() its output type. Without it the schema is a ZodType[Any].

As in Zod, a brand is static only. The parsed value is unchanged (type(uid) is str), and validation, errors and toJSONSchema are those of the schema being branded. Brand after the checks, which keep running as before:

Age = NewType("Age", int)
AgeSchema: z.ZodType[Age] = z.int().nonnegative().brand(Age)

The NewType must fit what the schema outputs. An obvious mismatch is caught when the schema is built:

try:
    z.int().brand(UserId)
except TypeError:
    pass  # brand(UserId): UserId is a NewType of str, but this number schema outputs int | float

.brand("UserId") with a string label is also accepted, for code ported from Zod. It is a no-op for typing: the schema keeps its own type (z.string().brand("UserId") parses to str). Anything other than a label or a NewType (a class such as str, for instance) raises TypeError.

Refinements

.refine(check, options) adds a custom check: return a truthy value to accept the input. The options are Zod 4's:

option
"error" the message, or a function of the issue (see Customizing errors)
"path" where the issue points, e.g. ["confirm"]
"abort" True: stop at this check when it fails (by default later checks still run)
"when" when(payload) decides whether the check runs (see below)
Password = (
    z.string()
    .min(8)
    .refine(
        lambda s: any(c.isdigit() for c in s),
        {"error": "Must contain a digit"},
    )
)
Password.safeParse("password").error.issues[0]["message"]  # => "Must contain a digit"

Even = z.int().refine(lambda n: n % 2 == 0, {"error": lambda issue: f"{issue['input']} is odd"})
Even.safeParse(3).error.issues[0]["message"]  # => "3 is odd"

On a class, the check receives the instance:

class PasswordForm(z.Object):
    password = z.string().min(8)
    confirm = z.string()


Passwords = PasswordForm.refine(
    lambda form: form.password == form.confirm,  # form: PasswordForm
    {"error": "Passwords don't match", "path": ["confirm"]},
)
Passwords.safeParse({"password": "secret123", "confirm": "nope"}).error.issues[0]["path"]
# => ["confirm"]

All failing refinements are reported; "abort": True stops at the first:

Both = z.string().refine(lambda s: False, {"error": "a"}).refine(lambda s: False, {"error": "b"})
[issue["message"] for issue in Both.safeParse("x").error.issues]  # => ["a", "b"]

First = (
    z.string().refine(lambda s: False, {"error": "a", "abort": True}).refine(lambda s: False, {"error": "b"})
)
[issue["message"] for issue in First.safeParse("x").error.issues]  # => ["a"]

when

A refinement does not run once the value already has an aborting issue — for an object, for example, a missing field. "when" replaces that rule: it receives the payload (payload.value, payload.issues) and returns whether to run. The object has not been built into an instance at that point, so read the fields with ["..."], which works on both:

class SignupForm(z.Object):
    password = z.string().min(8)
    confirm = z.string()
    nickname = z.string()


def passwords_given(payload):
    value = payload.value
    return isinstance(value.get("password"), str) and isinstance(value.get("confirm"), str)


Signup = SignupForm.refine(
    lambda form: form["password"] == form["confirm"],
    {"error": "Passwords don't match", "path": ["confirm"], "when": passwords_given},
)
result = Signup.safeParse({"password": "secret123", "confirm": "nope"})  # nickname is missing
[issue["path"] for issue in result.error.issues]  # => [["nickname"], ["confirm"]]

superRefine

.superRefine(fn, options) adds any number of issues through ctx.addIssue. An issue is a dict with a code (usually "custom"), its message, and optionally a path relative to the value:

def unique(items, ctx):
    if len(items) != len(set(items)):
        ctx.addIssue({"code": "custom", "message": "No duplicates allowed"})
    if len(items) > 3:
        ctx.addIssue(
            {
                "code": "too_big",
                "maximum": 3,
                "origin": "array",
                "inclusive": True,
                "message": "Too many items",
            }
        )


Tags = z.array(z.string()).superRefine(unique)
[issue["message"] for issue in Tags.safeParse(["a", "a", "b", "c"]).error.issues]
# => ["No duplicates allowed", "Too many items"]

An issue with "continue": False aborts the remaining checks. superRefine takes the same "error", "abort" and "when" options as refine.

check

.check(fn) is the low-level form: fn(ctx) reads ctx.value and pushes issues onto ctx.issues. Built-in checks such as z.gt(0) can be passed too:

def positive(ctx):
    if ctx.value <= 0:
        ctx.issues.push({"code": "custom", "message": "Must be positive", "input": ctx.value})


z.int().check(positive).safeParse(-1).error.issues[0]["message"]  # => "Must be positive"
z.number().check(z.gt(0), z.lt(10)).safeParse(42).success  # => False

Async checks (coroutine functions) need parseAsync / safeParseAsync — see Async.

Errors

A failed parse() raises z.ZodError; its issues are dicts that also allow attribute access. Messages are built only when the issues are read, so a failure you don't inspect is cheap:

try:
    Player.parse({"username": 1, "xp": 0})
except z.ZodError as error:
    issue = error.issues[0]
    issue["path"]  # => ["username"]
    issue.message  # => "Invalid input: expected string, received number"

Issues leave out the input unless you pass {"reportInput": True} to the parse.

Customizing errors

Every schema and every check takes {"error": ...}:

z.string({"error": "Not a string!"}).safeParse(1).error.issues[0]["message"]  # => "Not a string!"
z.string().min(5, {"error": "Too short!"}).safeParse("ab").error.issues[0]["message"]  # => "Too short!"

Set them per field to build form-friendly messages:

class SignupFields(z.Object):
    email = z.email({"error": "Enter a valid email address"})
    age = z.int({"error": "Age must be a whole number"}).min(18, {"error": "You must be 18 or older"})


result = SignupFields.safeParse({"email": "nope", "age": 16})
[(issue["path"], issue["message"]) for issue in result.error.issues]
# => [(["email"], "Enter a valid email address"), (["age"], "You must be 18 or older")]

Error functions

error can be a function of the issue. It receives the issue dict — code, input, path and the code-specific fields such as minimum or expected — and returns the message:

MinFive = z.string().min(
    5,
    {"error": lambda issue: f"Need {issue['minimum']} characters, got {len(issue['input'])}"},
)
MinFive.safeParse("ab").error.issues[0]["message"]  # => "Need 5 characters, got 2"

Tell a missing value from a wrong type with issue["input"]:

RequiredString = z.string({"error": lambda issue: "Required" if issue["input"] is None else "Not a string"})
RequiredString.safeParse(None).error.issues[0]["message"]  # => "Required"
RequiredString.safeParse(1).error.issues[0]["message"]  # => "Not a string"

Return None to fall back to the next error in the precedence order (for an error function on a schema, that is the default message):

OnlyTooSmall = z.int({"error": lambda issue: "Too small!" if issue["code"] == "too_small" else None})
OnlyTooSmall.min(3).safeParse(1).error.issues[0]["message"]  # => "Too small!"

Per parse

Pass {"error": ...} to a single parse / safeParse call:

class Counter(z.Object):
    n = z.int()


Counter.safeParse({"n": "x"}, {"error": lambda issue: f"bad {issue['code']}"}).error.issues[0]["message"]
# => "bad invalid_type"

Globally

z.config({"customError": ...}) sets an error function for every schema — for example to translate messages. Return None for the issues you leave to the default messages:

def japanese(issue):
    if issue["code"] == "invalid_type":
        return f"{issue['expected']} を入力してください"
    return None  # other codes: the default message


z.config({"customError": japanese})
z.int().safeParse("x").error.issues[0]["message"]  # => "number を入力してください"
z.int().min(3).safeParse(1).error.issues[0]["message"]  # => "Too small: expected number to be >=3"
z.config({"customError": None})  # back to the default messages

Precedence

The first error that gives a message wins:

  1. the error of the check (.min(5, {"error": ...}))
  2. the error of the schema (z.string({"error": ...}))
  3. the per-parse error (.safeParse(data, {"error": ...}))
  4. z.config({"customError": ...})
  5. z.config({"localeError": ...}) — the default English messages
SchemaError = z.int({"error": "schema wins"})
SchemaError.safeParse("x", {"error": lambda issue: "per-parse"}).error.issues[0]["message"]
# => "schema wins"

Async

Async refinements and transforms take coroutine functions and need the async methods (parseAsync, safeParseAsync), which work with asyncio:

import asyncio


async def available(name: str) -> bool:
    await asyncio.sleep(0)  # e.g. a database lookup
    return name != "root"


Username = z.string().min(3).refine(available, {"error": "Username is taken"})
asyncio.run(Username.parseAsync("ada"))  # => "ada"

.parse() on a schema with async parts raises z.ZodAsyncError.

Engines

A schema is compiled on first use and validated by the Rust engine (zopy._core, built with PyO3). Parts it does not run itself — Python callbacks such as refine / transform, async schemas — are handed to the pure-Python engine, with identical results.

To run everything on the pure-Python engine (for debugging, or without the extension), set ZOD_ENGINE=python, or call zopy._compile.set_engine("python") at runtime.

Development

The repository is a uv + Cargo workspace:

.
├── packages/zopy/     # the library (maturin: Rust in src/, Python in python/zopy/, tests/)
└── apps/examples/      # runnable demos (`just examples`)

Requires uv and a Rust toolchain.

uv sync         # install everything (builds the Rust extension; also installs `just`)
just            # list the tasks
just check      # lint + typecheck + test (what CI runs)
just fmt        # format Python and Rust
just build      # rebuild the extension after changing Rust code
just examples   # run the demos

The code blocks in this README are executed by the test suite, so the examples stay accurate.

License

MIT

zopy

Metadata

Release files for zopy 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for zopy 0.1.1
File Size Uploaded Provenance Actions
zopy-0.1.1.tar.gz 145.8 kB With provenance Details for zopy-0.1.1.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for zopy 0.1.1
File Actions
zopy-0.1.1-cp314-cp314-win_amd64.whl CPython 3.14 CPython 3.14 Windows x86-64 Details for zopy-0.1.1-cp314-cp314-win_amd64.whl
zopy-0.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl CPython 3.14 CPython 3.14 Linux glibc 2.17+ x86-64 Details for zopy-0.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
zopy-0.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl CPython 3.14 CPython 3.14 Linux glibc 2.17+ ARM64 Details for zopy-0.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
zopy-0.1.1-cp314-cp314-macosx_11_0_arm64.whl CPython 3.14 CPython 3.14 macOS 11.0+ ARM64 Details for zopy-0.1.1-cp314-cp314-macosx_11_0_arm64.whl
zopy-0.1.1-cp314-cp314-macosx_10_12_x86_64.whl CPython 3.14 CPython 3.14 macOS 10.12+ x86-64 Details for zopy-0.1.1-cp314-cp314-macosx_10_12_x86_64.whl
zopy-0.1.1-cp313-cp313-win_amd64.whl CPython 3.13 CPython 3.13 Windows x86-64 Details for zopy-0.1.1-cp313-cp313-win_amd64.whl
zopy-0.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl CPython 3.13 CPython 3.13 Linux glibc 2.17+ x86-64 Details for zopy-0.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
zopy-0.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl CPython 3.13 CPython 3.13 Linux glibc 2.17+ ARM64 Details for zopy-0.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
zopy-0.1.1-cp313-cp313-macosx_11_0_arm64.whl CPython 3.13 CPython 3.13 macOS 11.0+ ARM64 Details for zopy-0.1.1-cp313-cp313-macosx_11_0_arm64.whl
zopy-0.1.1-cp313-cp313-macosx_10_12_x86_64.whl CPython 3.13 CPython 3.13 macOS 10.12+ x86-64 Details for zopy-0.1.1-cp313-cp313-macosx_10_12_x86_64.whl
zopy-0.1.1-cp312-cp312-win_amd64.whl CPython 3.12 CPython 3.12 Windows x86-64 Details for zopy-0.1.1-cp312-cp312-win_amd64.whl
zopy-0.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl CPython 3.12 CPython 3.12 Linux glibc 2.17+ x86-64 Details for zopy-0.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
zopy-0.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl CPython 3.12 CPython 3.12 Linux glibc 2.17+ ARM64 Details for zopy-0.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
zopy-0.1.1-cp312-cp312-macosx_11_0_arm64.whl CPython 3.12 CPython 3.12 macOS 11.0+ ARM64 Details for zopy-0.1.1-cp312-cp312-macosx_11_0_arm64.whl
zopy-0.1.1-cp312-cp312-macosx_10_12_x86_64.whl CPython 3.12 CPython 3.12 macOS 10.12+ x86-64 Details for zopy-0.1.1-cp312-cp312-macosx_10_12_x86_64.whl
zopy-0.1.1-cp311-cp311-win_amd64.whl CPython 3.11 CPython 3.11 Windows x86-64 Details for zopy-0.1.1-cp311-cp311-win_amd64.whl
zopy-0.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl CPython 3.11 CPython 3.11 Linux glibc 2.17+ x86-64 Details for zopy-0.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
zopy-0.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl CPython 3.11 CPython 3.11 Linux glibc 2.17+ ARM64 Details for zopy-0.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
zopy-0.1.1-cp311-cp311-macosx_11_0_arm64.whl CPython 3.11 CPython 3.11 macOS 11.0+ ARM64 Details for zopy-0.1.1-cp311-cp311-macosx_11_0_arm64.whl
zopy-0.1.1-cp311-cp311-macosx_10_12_x86_64.whl CPython 3.11 CPython 3.11 macOS 10.12+ x86-64 Details for zopy-0.1.1-cp311-cp311-macosx_10_12_x86_64.whl

Total release size: 26.0 MB

Release files / zopy-0.1.1.tar.gz

Download URL zopy-0.1.1.tar.gz
Size 145.8 kB
Tags Source
SHA-256 checksum
How to use checksums
8af40758946b923f7b6ff9fdfb1148920439e40f6d958f650457b62e0012e212
BLAKE2b-256 checksum
How to use checksums
f2a29beca972c57640a0bb41f4c32575ac3e7cbb936612f2cf6d57ced402c311
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

Release files / zopy-0.1.1-cp314-cp314-win_amd64.whl

Download URL zopy-0.1.1-cp314-cp314-win_amd64.whl
Size 1.2 MB
Tags CPython 3.14 Windows x86-64
SHA-256 checksum
How to use checksums
72e4bdde82e29fec6b875ba4d1d095039b992b81106e25869338c17e5d5dfcd8
BLAKE2b-256 checksum
How to use checksums
d7c706255cd4738d96e65f399d7218c029ad10662e83e59509768c7f1a623753
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

Release files / zopy-0.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl

Download URL zopy-0.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
Size 1.4 MB
Tags CPython 3.14 Linux glibc 2.17+ x86-64
SHA-256 checksum
How to use checksums
cc453456f2da3b7b6482013f2e7d7c873d980d8794261f443e746b759395d24c
BLAKE2b-256 checksum
How to use checksums
cfdcc5ed55532bfb4ffccc6ea7e58a838b69f7464ca28db9f9840f899f1484dd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

Release files / zopy-0.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl

Download URL zopy-0.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
Size 1.3 MB
Tags CPython 3.14 Linux glibc 2.17+ ARM64
SHA-256 checksum
How to use checksums
ea61ae2bbc2c0a0d161417c0e7f4a9ca68620bdb21cd5b4a6fe8c7f83b4e6d1a
BLAKE2b-256 checksum
How to use checksums
c74a09ac2e11d398cd6433e665d57bd30aeefd5bc118c3e9d149a832c9892800
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

Release files / zopy-0.1.1-cp314-cp314-macosx_11_0_arm64.whl

Download URL zopy-0.1.1-cp314-cp314-macosx_11_0_arm64.whl
Size 1.2 MB
Tags CPython 3.14 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
f06d58e4b9037bb8ecfe7de3613390bc5fb22dcd56a00d07d1d72d267b3f0bb2
BLAKE2b-256 checksum
How to use checksums
7369af9ab3b0c4c2cf4857b9f785508c89e18ca0981d2437d85cbbb92d37cc43
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

Release files / zopy-0.1.1-cp314-cp314-macosx_10_12_x86_64.whl

Download URL zopy-0.1.1-cp314-cp314-macosx_10_12_x86_64.whl
Size 1.3 MB
Tags CPython 3.14 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
a3d55e9e5a2ac6e07b73cffa6fa8f02035af8d47cceb61884e76b973c303d36d
BLAKE2b-256 checksum
How to use checksums
4a2b9c6124a9f67709800290b55f537841a999ee1223cfc10df0fd282c6cdc65
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

Release files / zopy-0.1.1-cp313-cp313-win_amd64.whl

Download URL zopy-0.1.1-cp313-cp313-win_amd64.whl
Size 1.2 MB
Tags CPython 3.13 Windows x86-64
SHA-256 checksum
How to use checksums
8d5ee8774cfef61b0f6dbf79f42e7555d5cf068383af8a15516d6ffaddc15b1f
BLAKE2b-256 checksum
How to use checksums
c5f0c8e1150df8ca68d18ad16b8105fdffd2ab226a90f74e8bb7eb04a911b215
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

Release files / zopy-0.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl

Download URL zopy-0.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
Size 1.4 MB
Tags CPython 3.13 Linux glibc 2.17+ x86-64
SHA-256 checksum
How to use checksums
5fc7c54d4d7a6a6dd248a750e7f8a55116f2cbebfb6fd1d9ede97c995e6c294c
BLAKE2b-256 checksum
How to use checksums
32fb7eddffbf4cbdb0106efbffe7606b5fbdf74be848db921716a4ee935d7617
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

Release files / zopy-0.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl

Download URL zopy-0.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
Size 1.3 MB
Tags CPython 3.13 Linux glibc 2.17+ ARM64
SHA-256 checksum
How to use checksums
91fcd139cdc260b709d824e17e389d4a7ec53b1b9280490dd0d59e7cd4ff1c1d
BLAKE2b-256 checksum
How to use checksums
17580f2b5463159b185ccdd81530881fac3f16ae30bbd0dcf1364b8270e3e4e6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

Release files / zopy-0.1.1-cp313-cp313-macosx_11_0_arm64.whl

Download URL zopy-0.1.1-cp313-cp313-macosx_11_0_arm64.whl
Size 1.2 MB
Tags CPython 3.13 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
4ab487644ba3212a24fca5d4577be2a5b90a9a0b290f687f863b46c53fa06139
BLAKE2b-256 checksum
How to use checksums
3eee3524628abd6c67640d87ec25d9d4ccaf9f7611a8617c42c2d3df6f0cd4e8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

Release files / zopy-0.1.1-cp313-cp313-macosx_10_12_x86_64.whl

Download URL zopy-0.1.1-cp313-cp313-macosx_10_12_x86_64.whl
Size 1.3 MB
Tags CPython 3.13 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
2791a526d05d3e020eb7ad73c83523a6ad885b099e6c68c0ac4c3fc826045811
BLAKE2b-256 checksum
How to use checksums
80d615b41676d523c7d756b89ccf9eeca584d26016d1900f04c2d9e2eb8df1de
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

Release files / zopy-0.1.1-cp312-cp312-win_amd64.whl

Download URL zopy-0.1.1-cp312-cp312-win_amd64.whl
Size 1.2 MB
Tags CPython 3.12 Windows x86-64
SHA-256 checksum
How to use checksums
12e83850aa2ceb7631339bd0c5e4232576b07eb0da01578460fe5e68e4acda75
BLAKE2b-256 checksum
How to use checksums
e749278f5808f1cf6b145b0efe3a3cf0f6e959c687b364052eb072ada3d2b1b2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

Release files / zopy-0.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl

Download URL zopy-0.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
Size 1.4 MB
Tags CPython 3.12 Linux glibc 2.17+ x86-64
SHA-256 checksum
How to use checksums
fc47b934585257ae96692d32f2247f68a98bae92c41f89e266fe8f5958be355f
BLAKE2b-256 checksum
How to use checksums
923ea9c892e6b50276a95f39646f1ec79d2e683d29dc4899aefe5c677e36d0a8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

Release files / zopy-0.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl

Download URL zopy-0.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
Size 1.3 MB
Tags CPython 3.12 Linux glibc 2.17+ ARM64
SHA-256 checksum
How to use checksums
220e5757131a70ef28f74888801d073eb8f9a0f26e58de09f9468530fd32249e
BLAKE2b-256 checksum
How to use checksums
5262d3fe4135eb3671d9b356be02e040b3fe747c2a6b83ef0e255b8ba77e4dce
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

Release files / zopy-0.1.1-cp312-cp312-macosx_11_0_arm64.whl

Download URL zopy-0.1.1-cp312-cp312-macosx_11_0_arm64.whl
Size 1.2 MB
Tags CPython 3.12 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
0f2f8dea676d01588329d0061d80407e1802093a4ca531e9876647e635e252ec
BLAKE2b-256 checksum
How to use checksums
ab77c62a2ffec9ed16731e0541e02ad2cf7c6d6f9790b0de293f00833a68b400
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

Release files / zopy-0.1.1-cp312-cp312-macosx_10_12_x86_64.whl

Download URL zopy-0.1.1-cp312-cp312-macosx_10_12_x86_64.whl
Size 1.3 MB
Tags CPython 3.12 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
9672fbc0400abdfb9b62c95086e528fb3f8b7745b2e102404e09fbdba38917a8
BLAKE2b-256 checksum
How to use checksums
0af038fce8077afb61e209daed4ca7f8e2188b11ab37dcf8683cb3e92814c99a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

Release files / zopy-0.1.1-cp311-cp311-win_amd64.whl

Download URL zopy-0.1.1-cp311-cp311-win_amd64.whl
Size 1.2 MB
Tags CPython 3.11 Windows x86-64
SHA-256 checksum
How to use checksums
eccd9e1d391aecc58b7daee2cf8984abb171a0c19eb9bcfce633dafb0db9a501
BLAKE2b-256 checksum
How to use checksums
c7f1dc185e20632bc94fc26850483ec5d1e5f9edc52cc9668c054c35a566af25
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

Release files / zopy-0.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl

Download URL zopy-0.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
Size 1.4 MB
Tags CPython 3.11 Linux glibc 2.17+ x86-64
SHA-256 checksum
How to use checksums
80dd892086c975956fd996149ab6b39b5571614aa192fa2e940c1c13f0308794
BLAKE2b-256 checksum
How to use checksums
74f4e1aec63badd24dcdf96ad957ff7dc8acb53dda8054ede49165a6127b3c43
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

Release files / zopy-0.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl

Download URL zopy-0.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
Size 1.3 MB
Tags CPython 3.11 Linux glibc 2.17+ ARM64
SHA-256 checksum
How to use checksums
dd70d0f6e4ac94cc6d32413070f6b35e7ade89b371e4655d7f7ab8404746222f
BLAKE2b-256 checksum
How to use checksums
4fd26268895c78d8ae422f2339b54b2991e31a5eb43c85ee38b46ea6234bfa04
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

Release files / zopy-0.1.1-cp311-cp311-macosx_11_0_arm64.whl

Download URL zopy-0.1.1-cp311-cp311-macosx_11_0_arm64.whl
Size 1.2 MB
Tags CPython 3.11 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
379d5e08aef96b90ef5577d5a0c936a9a017c2763633adb4a7aa2e519487b7b4
BLAKE2b-256 checksum
How to use checksums
1053b9d33a52f758cec8984524f3653e58c2b3b8535603e8b030c182137da2c7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

Release files / zopy-0.1.1-cp311-cp311-macosx_10_12_x86_64.whl

Download URL zopy-0.1.1-cp311-cp311-macosx_10_12_x86_64.whl
Size 1.3 MB
Tags CPython 3.11 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
4418a666f77dd58265520448bc82fd0531e0461cf9bebee246513e59459fcc34
BLAKE2b-256 checksum
How to use checksums
17791cc7b4516e374b57f30b798efa9fa37fb5c44b4ab558e2f22939f9b7f158
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

Security for zopy 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 21 files in this release have been uploaded with provenance metadata.

Select from 21 files

Attestation for zopy-0.1.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page