My code in working through the CS Primer material.
// https://csprimer.com/watch/varint-extension/
// inspired by https://github.com/adamserafini/zaml/blob/27b2d54ffb39aace5d5d58f0aa75396c3e6fe84d/zamlmodule.zig

const std = @import("std");

const varint = @import("protobuf-varint");

const py = @cImport({
    @cDefine("PY_SSIZE_T_CLEAN", {});
    @cInclude("Python.h");
});

fn cvarint_encode(
    self: [*c]py.PyObject,
    args: [*c]py.PyObject,
) callconv(.C) ?[*]py.PyObject {
    _ = self;

    var cvalue_K: CUInt = undefined;
    if (py.PyArg_ParseTuple(args, "K", &cvalue_K) == 0) return null;
    if (!canIntCast(u64, cvalue_K)) return null;
    const value = @intCast(u64, cvalue_K);

    var buffer: [10]varint.VarintByte = undefined;
    const result_vbytes = varint.encode(value, &buffer);

    // see https://docs.python.org/3/c-api/arg.html#building-values
    return py.Py_BuildValue(
        "y#",
        @ptrCast([*c]u8, result_vbytes.ptr),
        @intCast(py.Py_ssize_t, result_vbytes.len),
    );
}

fn cvarint_decode(
    self: [*c]py.PyObject,
    args: [*c]py.PyObject,
) callconv(.C) ?[*]py.PyObject {
    _ = self;

    var cvalue_ptr: [*c]u8 = undefined;
    var cvalue_len: py.Py_ssize_t = undefined;
    if (py.PyArg_ParseTuple(args, "y#", &cvalue_ptr, &cvalue_len) == 0) return null;
    if (cvalue_ptr == null) return null;
    const value = @ptrCast([]const varint.VarintByte, cvalue_ptr[0..@intCast(usize, cvalue_len)]);

    const result = varint.decode(value) catch return null;
    return py.Py_BuildValue("K", @as(CUInt, result));
}

/// A c-native uint at least as big as u64.
const CUInt: type = c_ulonglong;
comptime {
    std.debug.assert(@bitSizeOf(CUInt) >= @bitSizeOf(u64));
}

/// Runtime check that an int value fits in another int type.
fn canIntCast(comptime T: type, value: anytype) bool {
    return @truncate(@TypeOf(value), @truncate(T, value)) == value;
}

var CVarintMethods = [_]py.PyMethodDef{
    py.PyMethodDef{
        .ml_name = "encode",
        .ml_meth = cvarint_encode,
        .ml_flags = py.METH_VARARGS,
        .ml_doc = "Encode an integer as varint.",
    },
    py.PyMethodDef{
        .ml_name = "decode",
        .ml_meth = cvarint_decode,
        .ml_flags = py.METH_VARARGS,
        .ml_doc = "Decode varint bytes to an integer.",
    },
    py.PyMethodDef{
        .ml_name = null,
        .ml_meth = null,
        .ml_flags = 0,
        .ml_doc = null,
    },
};

var cvarintmodule = py.PyModuleDef{
    .m_base = py.PyModuleDef_Base{
        .ob_base = py.PyObject{
            .ob_refcnt = 1,
            .ob_type = null,
        },
        .m_init = null,
        .m_index = 0,
        .m_copy = null,
    },
    .m_name = "cvarint",
    .m_doc = "A C implementation of protobuf varint encoding",
    .m_size = -1,
    .m_methods = &CVarintMethods,
    .m_slots = null,
    .m_traverse = null,
    .m_clear = null,
    .m_free = null,
};

pub export fn PyInit_cvarint() [*]py.PyObject {
    return py.PyModule_Create(&cvarintmodule);
}