Skip to content

Commit

Permalink
Allow setting default arguments for dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesWrigley committed Jul 7, 2023
1 parent 7739e56 commit 6085018
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 3 deletions.
19 changes: 16 additions & 3 deletions damnit/ctxsupport/ctxrunner.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import argparse
import functools
import inspect
import logging
import os
import time
Expand Down Expand Up @@ -212,16 +213,28 @@ def __init__(self, data, ctx):
def create(cls, ctx_file: ContextFile, inputs, run_number, proposal):
res = {'start_time': np.asarray(get_start_time(inputs['run_data']))}

def get_dep_or_default(var, arg_name, dep_name):
"""
Helper function to get either the value returned from the dependency
`dep_name` of `var`, if any, or the default value of the argument in
the function signature.
"""
value = res.get(dep_name)
if value is None:
value = inspect.signature(var.func).parameters[arg_name].default

return value

for name in ctx_file.ordered_vars():
var = ctx_file.vars[name]

try:
# Add all variable dependencies
kwargs = { arg_name: res.get(dep_name)
kwargs = { arg_name: get_dep_or_default(var, arg_name, dep_name)
for arg_name, dep_name in var.arg_dependencies().items() }

# If any are None, skip this variable since we're missing a dependency
missing_deps = [key for key, value in kwargs.items() if value is None]
# Check for missing dependencies with no default value
missing_deps = [key for key, value in kwargs.items() if value is inspect.Parameter.empty]
if len(missing_deps) > 0:
log.warning(f"Skipping {name} because of missing dependencies: {', '.join(missing_deps)}")
continue
Expand Down
10 changes: 10 additions & 0 deletions docs/backend.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,16 @@ def bar(run, value: "var#foo"):
return value * 2
```

Dependencies with default values are also allowed, the default value will be
passed to the function if the dependency did not complete execution for some
reason:
```python
@Variable(title="baz")
def baz(run, bar: "var#foo"=42):
# This will return the result of foo() if foo() succeeded, otherwise 42
return value
```

## Reprocessing
The context file is loaded each time a run is received, so if you edit the
context file the changes will only take effect for the runs coming later. But,
Expand Down
13 changes: 13 additions & 0 deletions tests/test_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,19 @@ def bar(run, foo: "var#foo"):
# There should be no computed variables since we treat None as a missing dependency
assert tuple(results.data.keys()) == ("start_time",)

default_value_code = """
from damnit_ctx import Variable
@Variable(title="foo")
def foo(run): return None
@Variable(title="bar")
def bar(run, foo: "var#foo"=1): return 41 + foo
"""
default_value_ctx = mkcontext(default_value_code)
results = results_create(default_value_ctx)
assert results.reduced["bar"].item() == 42

# Test that the backend completely updates all datasets belonging to a
# variable during reprocessing. e.g. if it had a trainId dataset but now
# doesn't, the trainId dataset should be deleted from the HDF5 file.
Expand Down

0 comments on commit 6085018

Please sign in to comment.