print(plate.properties.dimensions.rows.value) # a resource property
print(step.parameters.acquisition.exposure.value) # a step parameter16
0.25
Resource properties and step parameters are stored the same way and are read the same way. ParameterSchema deliberately mirrors PropertySchema, so once you know one you know the other.
Writing them differs, and that difference is the main thing this page exists to record.
Both follow the same four-level chain:
resource.properties . <group> . <attribute> . value
step.parameters . <group> . <attribute> . value
The third level is not a bare number or string. It is a small wrapper that carries the value together with its unit and its template definition. The .value at the end is what unwraps it.
print(plate.properties.dimensions.rows.value) # a resource property
print(step.parameters.acquisition.exposure.value) # a step parameter16
0.25
.values is optionalThe wrapper models live under a values attribute, but both properties and parameters forward attribute access through it, so you can leave it out. These are equivalent:
16
16
Prefer the short form. The long form is only useful when you want the values model itself, for example to call .keys() on it.
.value is not optionalOmitting .value gives you the wrapper, not the value. The wrapper renders as its value when printed, so this mistake survives a print() and fails at the first comparison:
rows = plate.properties.dimensions.rows
print("printed: ", rows)
print("rows == 16: ", rows == 16)
print("rows.value: ", rows.value)
print("rows.value==16:", rows.value == 16)printed: 16
rows == 16: False
rows.value: 16
rows.value==16: True
If a comparison, arithmetic operation, or function call behaves oddly, check whether a .value is missing.
This is why the wrapper exists:
| Form | Accepts | Missing attribute |
|---|---|---|
properties.dimensions.rows |
slug only | AttributeError |
properties["dimensions"]["rows"] |
slug or original name | KeyError |
properties["dimensions"].get("rows") |
slug or original name | returns None |
Dotted access needs the slug, so a group named "Sample Temperature" is properties.sample_temperature. Bracket access accepts either, which makes it the right choice when names are not valid Python identifiers or are built at runtime.
Assign the raw value to the attribute, inside a resource builder block. There is no .value on the left-hand side:
Reading it back confirms the change was saved. Note that a mutable resource is not returned by queries, so it is read through a builder rather than query_maker():
with namespace.build_resource(resource_id=draft_id) as check:
print(check.get_model(update=True).properties.dimensions.rows.value)24
Bracket forms work identically and accept original names:
builder.resource.properties["dimensions"]["rows"] = 24
builder.resource.properties["dimensions"].values["rows"] = 24
The builder validates against the template and saves once, on clean exit. If the block raises, nothing is written.
A resource is frozen once it becomes ACTIVE, and an attempted edit does not raise. It simply has no effect:
print("before:", plate.status)
with namespace.build_resource(resource_id=plate.id) as builder:
builder.resource.properties.dimensions.rows = 9999
check = namespace.query_maker().resources().filter(name="Plate 001").first()
print("after: ", check.properties.dimensions.rows.value, "(9999 discarded)")before: LifecycleStatus.ACTIVE
after: 16 (9999 discarded)
Since queries only return active resources, any resource you found by querying is already frozen. To change one, copy it first, as described in Manage Resource Lifecycle.
Parameters are not written by assigning through a loaded process run. Use get_params() and set_params() inside the build_process_run block:
with namespace.build_process_run(...) as run:
params = run.get_params("Scan")
params.acquisition.exposure = 0.25
run.set_params(params)The model returned by get_params() holds the same wrappers, so you assign raw values and read them back with .value:
Assigning through a queried process run mutates only the in-memory copy. The statement below succeeds and changes nothing in the database:
scan.steps["Scan"].parameters.acquisition.exposure = 5.0 # no effect
set_params() is the only supported way to record parameter values.
| Properties | Parameters | |
|---|---|---|
| Read | resource.properties.<g>.<a>.value |
step.parameters.<g>.<a>.value |
| Unit | ....<a>.unit |
....<a>.unit |
.values level |
optional | optional |
.value suffix |
required | required |
| Write | assign on builder.resource.properties |
get_params() / set_params() |
| Write window | while MUTABLE only | inside build_process_run |
Related: Edit Resource Properties, Build Process Runs, Manage Resource Lifecycle.