Skip to content

Commit b4d5ff1

Browse files
committed
Add docs on CaptureHolder.set_value
1 parent c15c824 commit b4d5ff1

1 file changed

Lines changed: 30 additions & 1 deletion

File tree

‎book/plugins/how-to-guides/track-the-value-of-auto-params-in-provenance.md‎

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,36 @@ If the user passed `None`, the callable is invoked and its return value is used.
6161
If the user passed an explicit value, that value is returned directly.
6262
In both cases the resolved value is written back into the {term}`Action`'s {term}`Provenance` as though it had been passed in by the user originally.
6363

64+
## Modifying the Value on a CaptureHolder
65+
66+
{term}`CaptureHolder` objects also support overwriting the value that was set on them. This is a feature that will not be necessary in most cases, and should be used with care.
67+
68+
For a real example of how this might be useful, [`scikit_learn.PCA`](https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.PCA.html) allows the user to pass in n_components as an integer, or a float 0 < n_components < 1. The float will cause PCA to select the number of components such that the amount of variance that needs to be explained is less than the percentage specified by n_components. Fortunately, it is possible to retrieve the integer number of components that ended up being used from the PCA object. In a situation like this, if a user passes in a float, it may be desirable to set the integer number of components actually used in provenance, and not the float.
69+
70+
This can be accomplished by calling `CaptureHolder.set_value` as seen below.
71+
72+
```python
73+
from qiime2.plugin import CaptureHolder
74+
75+
76+
def random_seed_method(random_seed: CaptureHolder[int] = None) -> int:
77+
# Resolve the seed: if the user passed None, generate a random value and
78+
# record it in provenance; otherwise use the user-supplied value as-is.
79+
random_int = CaptureHolder.get_or_set(
80+
random_seed, lambda: random.randrange(sys.maxsize)
81+
)
82+
83+
# Use the resolved integer value (guaranteed to never be None here)
84+
my_value = my_function(random_int)
85+
# Set the new value on the CaptureHolder overwriting the old one
86+
CaptureHolder.set_value(random_seed, my_value, overwrite=True)
87+
88+
return my_value
89+
```
90+
91+
`CaptureHolder.set_value` takes three arguments: the {term}`CaptureHolder` {term}`Parameter` instance, the value you are setting on it, and a boolean called `overwrite` that defaults to `False`. You must pass `overwrite=True` and explicitly assert that you are changing the value on the {term}`CaptureHolder`, and as a result the value in {term}`Provenance` just to make sure you know what you're doing and want to do this. The value set in {term}`Provenance` needs to be a value that could be passed into the given {term}`Action` for the given {term}`Parameter` leading to the same result as the run the {term}`Provenance` is recording.
92+
6493
```{note}
65-
When calling the underlying function directly during testing (rather than through QIIME 2), `CaptureHolder.get_or_set` behaves correctly whether the parameter is a `CaptureHolder` instance, a plain value, or `None`.
94+
When calling the underlying function directly during testing (rather than through QIIME 2), `CaptureHolder.get_or_set` and `CaptureHolder.set` behave correctly whether the parameter is a `CaptureHolder` instance, a plain value, or `None`.
6695
This means you can write unit tests that call the function directly without any special handling.
6796
```

0 commit comments

Comments
 (0)