Skip to content

API Reference

Everything exported by DynamicObjects. For usage and worked examples see the manual.

The struct macro

Missing docstring.

Missing docstring for @dynamicstruct. Check Documenter's build log for details.

In-struct property markers

These are not real macros — they are pattern-matched by @dynamicstruct inside a struct body. Outside a struct body they're either no-ops, real macros (e.g. @memo), or undefined. Don't rely on them in arbitrary positions.

MarkerEffect
@cached prop = exprPersist to disk under cache_path. Per-key for indexed properties.
@cached v"N" prop = exprVersioned disk cache; bumping N invalidates files without changing inputs.
@persist prop = exprWrite the in-memory value back to disk on demand (see @persist).
@lru N prop(idx) = exprBound the per-property in-memory dict to N entries (LRU eviction).
@memo prop = exprInside a struct: rewrite call → bracket access. Outside: process-wide function memoize.

Cache inspection

Real macros — usable inside and outside @dynamicstruct bodies. Inside a body, drop the object prefix and use the bare property name.

DynamicObjects.@cache_status Macro
julia
@cache_status o.prop
@cache_status o.prop[indices...]

Return the disk-cache status of a @cached property as a Symbol:

  • :unstarted — no cache file exists yet.

  • :started — an empty placeholder file exists (previous run may have crashed).

  • :ready — a complete cache file exists and can be deserialized.

Can be used both outside and inside a @dynamicstruct body. Inside a struct definition, omit the object prefix — just use the property name:

julia
# Outside the struct:
@cache_status e.result          # :unstarted (before first access)
e.result
@cache_status e.result          # :ready
@cache_status e.ci[2]           # for indexable properties

# Inside the struct body:
@dynamicstruct struct App
    @cached result(key) = expensive(key)
    status(key) = @cache_status result[key]   # :unstarted, :started, or :ready
end
source
DynamicObjects.@is_cached Macro
julia
@is_cached o.prop
@is_cached o.prop[indices...]

Return true if the disk cache for o.prop (or o.prop[indices...]) is :ready, i.e. the cached value can be loaded from disk without recomputation.

Can be used both outside and inside a @dynamicstruct body. Inside a struct definition, omit the object prefix — just use the property name:

julia
# Outside the struct:
@is_cached e.result   # false before first access, true afterwards

# Inside the struct body:
@dynamicstruct struct App
    @cached result(key) = expensive(key)
    summary(key) = if @is_cached result[key]
        "cached: $(result[key])"
    else
        "not yet computed"
    end
end
source
DynamicObjects.@cache_path Macro
julia
@cache_path o.prop
@cache_path o.prop[indices...]

Return the file path where the disk-cached value of o.prop (or o.prop[indices...]) is (or would be) stored.

julia
@cache_path e.result          # e.g. "cache/<hash>/result.sjl"
@cache_path e.ci[2]           # "cache/<hash>/2.sjl"
source
DynamicObjects.@clear_cache! Macro
julia
@clear_cache! o.prop
@clear_cache! o.prop[indices...]

Clear the disk cache (and in-memory cache) for a @cached property.

Without indices, clears all cached entries for the property (both the in-memory value and all .sjl files for that property on disk). With indices, clears only the specific entry.

julia
@clear_cache! e.result        # clear all cached entries for `result`
@clear_cache! e.ci[3]         # clear only the ci[3] entry
source
DynamicObjects.@persist Macro
julia
@persist o.prop
@persist o.prop[indices...]

Write the in-memory value of o.prop (or the indexed entry o.prop[indices...]) back to its disk cache. Use after mutating a value in place when the property was declared with @cached and the on-disk copy is now stale relative to the in-memory copy.

source

Functions

DynamicObjects.remake Function
julia
remake(obj; kwargs...)

Create a new instance of the same @dynamicstruct type as obj, copying all fixed fields from obj and overriding any specified via keyword arguments.

Keyword arguments that correspond to fixed fields replace those field values in the new instance. Any remaining keyword arguments are forwarded to the constructor as cache pre-population overrides.

Example

julia
@dynamicstruct struct Config
    n::Int
    scale::Float64
    result = scale * sum(rand(n))
end

c  = Config(100, 2.0)
c2 = remake(c; n=200)       # n=200, scale=2.0, result recomputed fresh
c3 = remake(c; scale=3.0)   # n=100, scale=3.0, result recomputed fresh
c4 = remake(c; result=0.0)  # n=100, scale=2.0, result pre-set to 0.0
source
DynamicObjects.fetchindex Function
julia
fetchindex(fetch, ip, indices...; kwargs...)

Call getindex(ip, indices...; kwargs...) with a custom fetch function.

For IndexableProperty backed by a ThreadsafeDict, getindex spawns a Task for the computation. The fetch callback receives (rv, status) where rv is the Task (still running) or the computed result (done), and status is the substatus object (from __substatus__) or nothing.

Pass force=true to unconditionally recompute: clears both the in-memory cache entry and the on-disk cache file so getindex always spawns a fresh Task.

Example

julia
fetchindex(app.results, key) do rv, status
    if rv isa Task
        # still computing — status is the progress node
        render_progress(status)
    else
        # done — render result
        render(rv)
    end
end
source
DynamicObjects.fetchindex! Function
julia
fetchindex!(callback, ip, indices...; fetch=Base.fetch, kwargs...)

In-place variant of fetchindex. When callback is nothing, falls through to a plain getindex(ip, indices...; fetch, kwargs...) — useful for sites that opt out of the two-phase fetch dance without changing call shape.

source
DynamicObjects.getstatus Function
julia
getstatus(ip::IndexableProperty, indices...; kwargs...)

Return the status object associated with an in-flight computation for the given key, or nothing if no status exists (computation not started, already finished, or no __substatus__ defined).

Only meaningful for IndexableProperty backed by a ThreadsafeDict.

source

Cache maintenance

DynamicObjects.entries Function
julia
entries(ip::IndexableProperty)

Return a vector of (; key, state, status, value) for all entries in a ThreadsafeDict-backed IndexableProperty. state is one of :running, :failed, :finishing, or :done. value is the cached result (for :done) or the Task (for running/failed/finishing). status is the substatus object or nothing.

source
DynamicObjects.cached_entries Function
julia
cached_entries(ip::IndexableProperty)

Return a vector of (key, value) pairs for completed (non-Task) entries only.

source
DynamicObjects.clear_all_caches! Function
julia
clear_all_caches!(obj)

Clear all @cached properties on a @dynamicstruct instance — both in-memory and on disk. Equivalent to clear_mem_caches! + clear_disk_caches!.

source
DynamicObjects.clear_mem_caches! Function
julia
clear_mem_caches!(obj)

Clear all in-memory memoized property values on a @dynamicstruct instance, leaving disk caches (@cached files) untouched. Every derived property — including child DOs stored as values — will be recomputed on next access.

This is useful after hot-reloading code via Revise: property values computed by old method definitions stay memoized until the process restarts or this function is called.

source
DynamicObjects.clear_disk_caches! Function
julia
clear_disk_caches!(obj)

Delete all on-disk cache files for @cached properties on a @dynamicstruct instance. In-memory values are left intact (they'll be stale until clear_mem_caches! is also called, or until the process restarts).

source

Cancellation

DynamicObjects.cancel! Function
julia
cancel!(ip::IndexableProperty, indices...; kwargs...)

Cancel a running task for the given key on a ThreadsafeDict-backed IndexableProperty. Returns true if a running task was found and interrupted, false otherwise.

source
DynamicObjects.cancel_all! Function
julia
cancel_all!(ip::IndexableProperty)

Cancel all running tasks on a ThreadsafeDict-backed IndexableProperty.

source

Error handling

DynamicObjects.PropertyComputationError Type
julia
PropertyComputationError <: Exception

Wraps an error that occurred during lazy property computation, adding context about which property failed (property name, type, indices/kwargs). The original exception and backtrace are stored in the cause field.

source
DynamicObjects.unwrap_error Function
julia
unwrap_error(e)

Recursively unwrap TaskFailedException, CompositeException, and PropertyComputationError wrappers to find the root cause exception.

source

Persistent / bounded collections

DynamicObjects.PersistentSet Type
julia
PersistentSet(path)

A thread-safe Set that persists to disk via Serialization. Loads existing data from path on construction, or starts empty if the file doesn't exist.

source
DynamicObjects.LazyPersistentDict Type
julia
LazyPersistentDict{D<:AbstractDict}(path[, empty_data]; seed!)

Threadsafe dict backed by Serialization.serialize/deserialize. The backing file path is resolved lazily via a callable path so the constructor itself is precompile-safe (no mkpath, no file I/O). The on-disk file is loaded on the first operation (double-checked under the lock), and the optional seed!(data) callback runs once after load if the dict is empty. Mutations persist to disk synchronously under the lock.

path may be an AbstractString (fixed path) or a 0-arg function returning a String. Pass an ordered backing dict (e.g. OrderedDict{K,V}()) to preserve insertion order.

source
DynamicObjects.LRUDict Type
julia
LRUDict{K,V}(maxsize)

Plain (non-thread-safe) Dict bounded to maxsize entries via least-recently-used eviction. Used as the per-property in-memory cache for @lru-marked properties on :serial @dynamicstruct instances.

source
DynamicObjects.ThreadsafeLRUDict Type
julia
ThreadsafeLRUDict{K,V}(maxsize)

A ThreadsafeDict variant that bounds its cache to maxsize entries, evicting the least-recently-used keys on insert. Eviction skips keys that have an in-flight Task (i.e. a running computation), so callers awaiting a result never observe its cache slot vanish underneath them. If every slot is pinned by a running task, the dict is allowed to temporarily exceed maxsize.

The (lock, cache, tasks, status) shape matches ThreadsafeDict, so all the generic dispatch on <:AbstractThreadsafeDict (getstatus, cancel!, fetchindex, entries, …) works unchanged.

source

Pluggable key tracking

For bounding on-disk caches when the full key set isn't known up front.

DynamicObjects.KeyTracker Type
julia
KeyTracker

Abstract type for pluggable accessed-keys persistence strategies. Implement record!(tracker, key) and load_keys(tracker) for custom strategies.

Override key_tracker(o, ::Val{name}) on your object type to select a strategy.

source
DynamicObjects.SharedFileTracker Type
julia
SharedFileTracker(path)

Default strategy: all pods/processes share a single _keys.sjl file. Simple, but not safe for concurrent multi-process writes to NFS.

source
DynamicObjects.PerPodFileTracker Type
julia
PerPodFileTracker(base_path, pod_id)

Per-pod strategy: each pod writes only to its own _keys_{pod_id}.sjl file. load_keys unions all matching files. Safe for NFS multi-pod setups — writes are never concurrent since each pod touches only its own file.

source
DynamicObjects.NoKeyTracker Type
julia
NoKeyTracker()

No-op strategy: never records or loads keys. Use when tracking is unwanted.

source
DynamicObjects.key_tracker Function
julia
key_tracker(o, ::Val{name}) -> KeyTracker

Return the KeyTracker to use for property name on object o. Override this method on your type to change the tracking strategy.

julia
# Example: use per-pod files for all indexed properties on MyType
DynamicObjects.key_tracker(o::MyType, ::Val{name}) where {name} =
    DynamicObjects.PerPodFileTracker(joinpath(o.cache_path, string(name) * "_keys"), pod_id)
source
DynamicObjects.record! Function
julia
record!(tracker::KeyTracker, key)

Record that key was accessed, persisting according to the tracker's strategy.

source
DynamicObjects.load_keys Function
julia
load_keys(tracker::KeyTracker) -> Set

Load the full set of recorded keys according to the tracker's strategy.

source
You are viewing the dev branch. This branch may include code written with Claude Code with less human supervision. Only human-approved code is merged into main.