Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

draft: sketched out SO interface CI workflow. #798

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/interface_ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

name: interface-ci
on: [pull_request]

jobs:
check-so-interface:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./tests/ci
steps:
- name: Check out the repository to the runner
uses: actions/checkout@v4
- name: Check SO interface spec
run: python ./check_so_interface.py
Binary file added tests/.DS_Store
Binary file not shown.
219 changes: 219 additions & 0 deletions tests/ci/check_so_interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
import ast
# functions and their arguments to check
# specified here: https://www.overleaf.com/project/5e837cac9659910001e5f71e
# could move this to a file
#to complete: work on functions and arguments to check file and spec_args dictionary
interface = {
"python/pysmurf/client/tune/smurf_tune.py": { # file
"SmurfTuneMixin": { # class
"find_freq": [], # function and args
}
},
}

spec_args = {
"setup": [

],
"set_amplifier_bias": [

],
"set_cryo_card_ps_en": [

],
"which_on": [

],
"band_off": [

],
"channel_off": [

],
"full_band_resp": [

],
"find_freq": [

],
"setup_notches": [

],
"run_serial_gradient_descent": [

],
"run_serial_eta_scan": [

],
"plot_tune_summary": [

],
"tracking_setup": [

],
"set_amplitude_scale_array": [

],
"set_tes_bias_bipolar_array": [

],
"set_tes_bias_high_current": [

],
"set_tes_bias_low_current": [

],
"set_mode_dc" "set_mode_ac": [

],
"flux_ramp_setup": [

],
"set_stream_enable": [

],
"take_stream_data": [

],
"take_noise_psd": [

],
"stream_data_on": [

],
"stream_data_off": [

],
"read_stream_data": [

],
"set_downsample_filter": [

],
"run_iv": [

],
"analyze_iv": [

]
}
freeze = {}

with open('tests/ci/frozen_functions.py', 'r') as fh: #opens file "fname", in read mode 'r', to be used in code as "fh"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is going to be stored in this file? Is it a pseudo-python representation of the objects/methods structure that define the interface?

frozen = ast.parse(fh.read())
for node in ast.walk(frozen):
if isinstance(node, ast.FunctionDef) and node.name in spec_args.keys():
freeze.update({node.name: node})
print(freeze.items())
"""
*** ==> I found this function in smurf_tune.py
frozen functions list:

SETUP FUNCTIONS
setup
set_amplifier_bias
set_cryo_card_ps_en
which_on
band_off
channel_off

TUNING FUNCTIONS
full_band_resp ***
find_freq ***
setup_notches ***
run_serial_gradient_descent
run_serial_eta_scan
plot_tune_summary ***
tracking_setup ***
set_amplitude_scale_array

TES/FLUX RAMP FUNCTIONS
set_tes_bias_bipolar_array
set_tes_bias_high_current
set_tes_bias_low_current
set_mode_dc
set_mode_ac
flux_ramp_setup ***

DATA ACQUISITION FUNCTIONS
set_stream_enable
take_stream_data
take_noise_psd
stream_data_on
stream_data_off
read_stream_data
set_downsample_filter

IV FUNCTIONS
run_iv
analyze_iv

DATA OUTPUTS TO DISK
tune files generated when new resonators are found
channel mapping file format
.dat noise files - generated by take_stream_data
iv_files - generated by run_iv"

"""
def compare_args(found, spec_args):
for func_name, func_node in found.items():
if func_name not in spec_args:
print(func_name + " not in specified functions")
continue

spec_func = spec_args[func_name]

# Handle the case where spec_func is a list
if isinstance(spec_func, list):
if len(spec_func) == 0:
print(f"No specification for {func_name}")
continue
spec_func = spec_func[0] # Assume the function is the first item in the list

# Extract arguments from the AST node
found_args = [arg.arg for arg in func_node.args.args]
found_defaults = [ast.unparse(d) if d else None for d in func_node.args.defaults]

# Extract arguments from the spec function
spec_sig = ast.parse(f"def {func_name}{ast.get_source_segment(spec_func, spec_func.args)}:pass").body[0]
spec_args_list = [arg.arg for arg in spec_sig.args.args]
spec_defaults = [ast.unparse(d) if d else None for d in spec_sig.args.defaults]

# Compare arguments
if found_args != spec_args_list:
print(f"Mismatch in arguments for function {func_name}")
print(f"Found: {found_args}")
print(f"Expected: {spec_args_list}")
return False

# Compare default values
if found_defaults != spec_defaults:
print(f"Mismatch in default values for function {func_name}")
print(f"Found: {found_defaults}")
print(f"Expected: {spec_defaults}")
return False

return True

if __name__ == "__main__": #if this is the main thing being run
for fname, spec in interface.items(): #loop over file names "fname" as keys and "spec" as values in dictionary called "interface"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currently interface is not defined

print(interface.items())
with open(fname, 'r') as fh: #opens file "fname", in read mode 'r', to be used in code as "fh"
tree = ast.parse(fh.read()) #parsing contents of file into abstract syntax tree
notfound = []
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these two lines don't need to be in the context manager

found = {}
dump = ast.dump(tree)
for node in ast.walk(tree):
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the first layer in the tree is usually going to be made up of ClassDef objects (though not always), which in turn contain FunctionDef's. The original sketch had a check for this.

if isinstance(node, ast.FunctionDef) and node.name in spec_args.keys():
found.update({node.name: node})
for key in spec_args:
if key not in found:
notfound.append(key)
print("FOUND:")
print(found.keys())
print("NOT FOUND:")
print(notfound)
#compare arguments
assert compare_args(found, spec_args)
#print(ast.dump(found.get('find_freq')))
pass
Loading