一键导入
rust-ffi
Instructions for using whenever's internal Rust FFI abstractions
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Instructions for using whenever's internal Rust FFI abstractions
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | rust-ffi |
| description | Instructions for using whenever's internal Rust FFI abstractions |
pyo3_ffi, not pyo3The low-level pyo3_ffi module is used, not pyo3 directly.
This avoids overhead, complex abstractions, and gives full control over generated code.
The src/py/ module provides safe wrappers. Key types:
| Type | Purpose |
|---|---|
PyObj | Core wrapper around *mut PyObject. Has .extract() (Copy types), .extract_ref() (ref types), .type_(), .is_none() |
Owned<T> | RAII refcount wrapper. Use Owned::new() to take ownership, .borrow() for non-owning access |
HeapType<T> | A Python heap type that carries module state via .state() → &State |
PyType | A Python type object. .same_module() checks if two types belong to the same module |
PyReturn | Alias for PyResult<Owned<PyObj>> — the return type of Python-visible functions |
PyErrMarker | Sentinel indicating the Python error indicator is set |
ContextVarBool | Copy wrapper for a context variable. Has .get() -> PyResult<bool> |
Key helpers in src/py/:
raise_value_err(), raise_type_err(), raise_key_err() — raise Python exceptionswarn_with_class(cls, msg, stacklevel) — emit a Python warning. Takes PyObj, not a raw pointerhandle_kwargs(fname, kwargs, handler) — iterate kwargs with interned string matchinghandle_one_arg(fname, args) — extract exactly one positional arg, or raise TypeErrorhandle_opt_arg(fname, args) — extract zero or one positional arghandle_one_kwarg(fname, key, kwargs) — extract a single optional kwarg by keyfind_interned(value, handler) — match a PyObj against interned strings, returns Optionmatch_interned_str(name, value, handler) — like find_interned but raises on no matchgeneric_alloc(type_, data) — allocate a Python object with given dataPyAsciiStrBuilder::format() — build a Python string without intermediate Rust StringPyTuple::with_len() / .init_item() — safe tuple construction.to_py() via the ToPy trait — convert Rust values to Python objects.to_tuple() — convert a Python sequence to a tuple (prefer over seq_len+seq_getitem)import(module_name) — import a Python module (don't call PyImport_ImportModule directly)State (in src/pymodule/def.rs) is a large struct stored on the Python module. It holds:
HeapType<T> for each class (date_type, time_delta_type, etc.)exc_repeated, exc_skipped, etc.)warn_deprecation, warn_days_not_always_24h, etc.)ContextVarBools for suppressing warningsstr_years, str_hour, str_units, etc.)Access it via cls.state() from any HeapType<T>. Unpack needed fields at the top
of the function to avoid repeated state. access:
let &State {
date_type,
str_disambiguate,
exc_skipped,
time_delta_type,
..
} = cls.state();
When adding new fields to State, update three places: the struct definition,
the initialization in init_module, and Py_CLEAR in the traverse/clear functions.
Methods are registered in a static mut METHODS: &[PyMethodDef] array using macros:
method0! — no argsmethod1! — one positional argmethod_vararg! — variable positional argsmethod_kwargs! — positional args + keyword argsclassmethod1!, classmethod_kwargs! — class methodsThe function signatures must match the macro used. For method_kwargs!:
fn my_method(cls: HeapType<MyType>, slf: MyType, args: &[PyObj], kwargs: &mut IterKwargs) -> PyReturn
PyAsciiStrBuilder instead of format!() → to_py())i32/i64 over i128 when possibleu8::eq_ignore_ascii_case() instead of manual case checkspy_eq for comparisonsPositional argument handling:
// Exactly one required arg:
let arg = handle_one_arg("method_name", args)?;
// Zero or one optional arg:
let maybe_arg = handle_opt_arg("method_name", args)?;
Kwarg handling:
handle_kwargs("method_name", kwargs, |key, value, eq| {
if eq(key, str_some_kwarg) {
// parse value
} else {
return Ok(false); // unrecognized kwarg
}
Ok(true)
})
Single optional kwarg shortcut:
let relative_to = handle_one_kwarg("total", state.str_relative_to, kwargs)?;
Building deltas from kwargs (shift/add/subtract methods):
Use handle_delta_unit_kwargs() for full datetime units, or
handle_date_delta_unit_kwargs() for calendar-only units. These build typed
DeltaMonths/DeltaDays/TimeDelta directly from kwargs.
Structured since/until kwarg parsing:
let SinceUntilKwargs { units, round_mode, round_increment } =
SinceUntilKwargs::parse(fname, state, kwargs)?;
Unit sets: Use the appropriate bitfield type:
CalUnitSet — calendar units only (years, months, weeks, days)ExactUnitSet — exact units only (weeks through nanoseconds)DeltaUnitSet — all delta units (calendar + exact)Each has from_py() for parsing from Python, .iter(), .smallest(), .contains().
Interned string matching with custom errors:
Use find_interned + manual error message when you need a specific error format.
Use match_interned_str when the default error format is acceptable.
Error handling:
raise_value_err("msg")? for ValueError.ok_or_value_err("msg")? on Options — for domain errors with specific messages.ok_or_range_err()? on Options — for generic out-of-range errors (preferred)PyErrMarker() (with parens) as the sentinel in PyResult<T>Ord in Rust. Compare via .instant() for ordering.
Non-Copy (contains Arc<TimeZone>). Uses Arc::ptr_eq + content equality for timezone comparison.
DST-aware operations need ambiguity_for_local() resolution.Instant has Ord). Offset is an Offset scalar.DateTime in Rust) compares by local date+time. Has Ord.secs: DeltaSeconds + subsec: SubSecNanos. Use .total_nanos() -> i128.
Has .in_single_unit() and .in_exact_units() for unit decomposition.DeltaField<T> with i32::MAX as the UNSET sentinel.
DeltaField has custom Debug showing <unset> for sentinel values..ok_or_range_err() for out-of-range errors instead of custom messages.// SAFETY: comments for unsafe blocks per the Rust convention (exact casing matters).pub(crate) not pub for internal visibility.