Newer
Older
David Hendriks
committed
Module containing most of the utility functions for the binarycpython package
Functions here are mostly functions used in other classes/functions, or
useful functions for the user
Tasks:
- TODO: change all prints to verbose_prints
David Hendriks
committed
"""
import json
David Hendriks
committed
import os
David Hendriks
committed
import tempfile
David Hendriks
committed
import copy
import inspect
from typing import Union, Any
from collections import defaultdict
David Hendriks
committed
import h5py
import numpy as np
from binarycpython import _binary_c_bindings
import binarycpython.utils.moe_distefano_data as moe_distefano_data
David Hendriks
committed
David Hendriks
committed
import py_rinterpolate
########################################################
# Unsorted
########################################################
def convert_bytes(size):
David Hendriks
committed
"""
Function to return the size + a magnitude string
"""
for x in ['bytes', 'KB', 'MB', 'GB', 'TB']:
if size < 1024.0:
return "%3.1f %s" % (size, x)
size /= 1024.0
return size
def get_size(obj, seen=None):
David Hendriks
committed
"""
Recursively finds size of objects
From https://github.com/bosswissam/pysize
"""
size = sys.getsizeof(obj)
if seen is None:
seen = set()
obj_id = id(obj)
if obj_id in seen:
return 0
# Important mark as seen *before* entering recursion to gracefully handle
# self-referential objects
seen.add(obj_id)
if isinstance(obj, dict):
size += sum([get_size(v, seen) for v in obj.values()])
size += sum([get_size(k, seen) for k in obj.keys()])
elif hasattr(obj, '__dict__'):
size += get_size(obj.__dict__, seen)
elif hasattr(obj, '__iter__') and not isinstance(obj, (str, bytes, bytearray)):
size += sum([get_size(i, seen) for i in obj])
return size
def subtract_dicts(dict_1: dict, dict_2: dict) -> dict:
"""
Function to subtract two dictionaries.
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
Only allows values to be either a dict or a numerical type
For the overlapping keys (key name present in both dicts):
When the keys are of the same type:
- If the types are of numerical type: subtract the value at dict 2 from dict 1.
- If the types are both dictionaries: call this function with the subdicts
WHen the keys are not of the same type:
- if the keys are all of numerical types
For the unique keys:
- if the key is from dict 1: adds the value to the new dict (be it numerical value or dict)
- If the key is from dict 2: Adds the negative of its value in case of numerical type.
if the type is a dict, the result of subtract_dicts({}, dict_2[key]) will be set
If the result is 0, the key will be removed from the resulting dict.
If that results in an empty dict, the dict will be removed too.
Args:
dict_1: first dictionary
dict_2: second dictionary
Returns:
Subtracted dictionary
"""
# Set up new dict
new_dict = {}
# Define allowed numerical types
ALLOWED_NUMERICAL_TYPES = (float, int, np.float64)
#
keys_1 = dict_1.keys()
keys_2 = dict_2.keys()
# Find overlapping keys of both dicts
overlapping_keys = set(keys_1).intersection(set(keys_2))
# Find the keys that are unique
unique_to_dict_1 = set(keys_1).difference(set(keys_2))
unique_to_dict_2 = set(keys_2).difference(set(keys_1))
# Add the unique keys to the new dict
for key in unique_to_dict_1:
# If these items are numerical types
if isinstance(dict_1[key], ALLOWED_NUMERICAL_TYPES):
new_dict[key] = dict_1[key]
if new_dict[key] == 0:
del new_dict[key]
# Else, to be safe we should deepcopy them
elif isinstance(dict_1[key], dict):
copy_dict = copy.deepcopy(dict_1[key])
new_dict[key] = copy_dict
else:
msg = "Error: using unsupported type for key {}: {}".format(
key, type(dict_1[key])
)
print(msg)
raise ValueError(msg)
# Add the unique keys to the new dict
for key in unique_to_dict_2:
# If these items are numerical type, we should add the negative of the value
if isinstance(dict_2[key], ALLOWED_NUMERICAL_TYPES):
new_dict[key] = -dict_2[key]
if new_dict[key] == 0:
del new_dict[key]
# Else we should place the negative of that dictionary in the new place
elif isinstance(dict_2[key], dict):
new_dict[key] = subtract_dicts({}, dict_2[key])
else:
msg = "Error: using unsupported type for key {}: {}".format(
key, type(dict_2[key])
)
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
print(msg)
raise ValueError(msg)
# Go over the common keys:
for key in overlapping_keys:
# See whether the types are actually the same
if not type(dict_1[key]) is type(dict_2[key]):
# Exceptions:
if (type(dict_1[key]) in ALLOWED_NUMERICAL_TYPES) and (
type(dict_2[key]) in ALLOWED_NUMERICAL_TYPES
):
# We can safely subtract the values since they are all numeric
new_dict[key] = dict_1[key] - dict_2[key]
if new_dict[key] == 0:
del new_dict[key]
else:
print(
"Error key: {} value: {} type: {} and key: {} value: {} type: {} are not of the same type and cannot be merged".format(
key,
dict_1[key],
type(dict_1[key]),
key,
dict_2[key],
type(dict_2[key]),
)
)
raise ValueError
# This is where the keys are the same
else:
# If these items are numeric types
if isinstance(dict_1[key], ALLOWED_NUMERICAL_TYPES):
new_dict[key] = dict_1[key] - dict_2[key]
# Remove entry if the value is 0
if new_dict[key] == 0:
del new_dict[key]
# Else, to be safe we should deepcopy them
elif isinstance(dict_1[key], dict):
new_dict[key] = subtract_dicts(dict_1[key], dict_2[key])
# Remove entry if it results in an empty dict
# TODO: write test to prevent empty dicts from showing up
if not new_dict[key]:
del new_dict[key]
else:
msg = "Error: using unsupported type for key {}: {}".format(
key, type(dict_2[key])
)
print(msg)
raise ValueError(msg)
#
return new_dict
def get_moe_distefano_dataset(options):
"""
Function to get the default moe and Distefano dataset or accept a userinput.
"""
if not options.get("file", None):
print("Using the default Moe and de Stefano 2017 datafile")
David Hendriks
committed
json_data = copy.deepcopy(moe_distefano_data.moe_distefano_2017_data)
else:
if not os.path.isfile(options["file"]):
print(
"The provided 'file' Moe and de Stefano JSON file does not seem to exist at {}".format(
options["file"]
)
)
raise ValueError
if not options["file"].endswith(".json"):
print("Provided filename does not end with .json")
else:
# Read input data and Clean up the data if there are whitespaces around the keys
with open(options["file"], "r") as data_filehandle:
datafile_data = data_filehandle.read()
datafile_data = datafile_data.replace('" ', '"')
datafile_data = datafile_data.replace(' "', '"')
datafile_data = datafile_data.replace(' "', '"')
json_data = json.loads(datafile_data)
return json_data
def imports():
for name, val in globals().items():
if isinstance(val, types.ModuleType):
yield val.__name__
"""
Context manager to calculate time spent
"""
self.t = time.clock()
return self
def __exit__(self, type, value, traceback):
"""On exit we stop the clock and measure the time spent"""
self.t = time.clock() - self.t
print("Took {}s".format(self.t))
David Hendriks
committed
"""
Function to tell whether object is a capsule
"""
return t.__module__ == "builtins" and t.__name__ == "PyCapsule"
class Capturing(list):
"""
Context manager to capture output and store it
"""
self._stdout = sys.stdout
sys.stdout = self._stringio = StringIO()
return self
def __exit__(self, *args):
self.extend(self._stringio.getvalue().splitlines())
sys.stdout = self._stdout
David Hendriks
committed
########################################################
# utility functions
########################################################
def verbose_print(message: str, verbosity: int, minimal_verbosity: int) -> None:
David Hendriks
committed
"""
Function that decides whether to print a message based on the current verbosity
and its minimum verbosity
if verbosity is equal or higher than the minimum, then we print
Args:
message: message to print
verbosity: current verbosity level
minimal_verbosity: threshold verbosity above which to print
David Hendriks
committed
"""
if verbosity >= minimal_verbosity:
print(message)
def remove_file(file: str, verbosity: int = 0) -> None:
David Hendriks
committed
"""
Function to remove files but with verbosity
David Hendriks
committed
Args:
file: full filepath to the file that will be removed.
verbosity: current verbosity level (Optional)
David Hendriks
committed
Returns:
the path of a subdirectory called binary_c_python in the TMP of the filesystem
David Hendriks
committed
David Hendriks
committed
"""
if os.path.exists(file):
David Hendriks
committed
if not os.path.isfile(file):
verbose_print(
"This path ({}) is a directory, not a file".format(file), verbosity, 0
)
David Hendriks
committed
David Hendriks
committed
try:
David Hendriks
committed
verbose_print("Removed {}".format(file), verbosity, 1)
David Hendriks
committed
os.remove(file)
David Hendriks
committed
except FileNotFoundError as inst:
print("Error while deleting file {}: {}".format(file, inst))
verbose_print(
"File/directory {} doesn't exist. Can't remove it.".format(file),
verbosity,
1,
)
def temp_dir(*args: str) -> str:
David Hendriks
committed
"""
Function to create directory within the TMP directory of the filesystem
David Hendriks
committed
Makes use of os.makedirs exist_ok which requires python 3.2+
David Hendriks
committed
Args:
function arguments: str input where each next input will be a child of the previous full_path. e.g. temp_dir('tests', 'grid') will become '/tmp/binary_c_python/tests/grid'
David Hendriks
committed
Returns:
the path of a subdirectory called binary_c_python in the TMP of the filesystem
David Hendriks
committed
"""
tmp_dir = tempfile.gettempdir()
path = os.path.join(tmp_dir, "binary_c_python")
# loop over the other paths if there are any:
if args:
for extra_dir in args:
path = os.path.join(path, extra_dir)
David Hendriks
committed
#
os.makedirs(path, exist_ok=True)
return path
def create_hdf5(data_dir: str, name: str) -> None:
David Hendriks
committed
"""
David Hendriks
committed
Function to create an hdf5 file from the contents of a directory:
- settings file is selected by checking on files ending on settings
- data files are selected by checking on files ending with .dat
TODO: fix missing settingsfiles
Args:
data_dir: directory containing the data files and settings file
name: name of hdf5file.
David Hendriks
committed
"""
David Hendriks
committed
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
# Make HDF5:
# Create the file
hdf5_filename = os.path.join(data_dir, "{}".format(name))
print("Creating {}".format(hdf5_filename))
hdf5_file = h5py.File(hdf5_filename, "w")
# Get content of data_dir
content_data_dir = os.listdir(data_dir)
# Settings
if any([file.endswith("_settings.json") for file in content_data_dir]):
print("Adding settings to HDF5 file")
settings_file = os.path.join(
data_dir,
[file for file in content_data_dir if file.endswith("_settings.json")][0],
)
with open(settings_file, "r") as settings_file:
settings_json = json.load(settings_file)
# Create settings group
settings_grp = hdf5_file.create_group("settings")
# Write version_string to settings_group
settings_grp.create_dataset("used_settings", data=json.dumps(settings_json))
# Get data files
data_files = [el for el in content_data_dir if el.endswith(".dat")]
if data_files:
print("Adding data to HDF5 file")
# Create the data group
data_grp = hdf5_file.create_group("data")
# Write the data to the file:
# Make sure:
for data_file in data_files:
# filename stuff
filename = data_file
full_path = os.path.join(data_dir, filename)
base_name = os.path.splitext(os.path.basename(filename))[0]
# Get header info
header_name = "{base_name}_header".format(base_name=base_name)
data_headers = np.genfromtxt(full_path, dtype="str", max_rows=1)
data_headers = np.char.encode(data_headers)
data_grp.create_dataset(header_name, data=data_headers)
# Add data
data = np.loadtxt(full_path, skiprows=1)
data_grp.create_dataset(base_name, data=data)
hdf5_file.close()
David Hendriks
committed
########################################################
# version_info functions
########################################################
def return_binary_c_version_info(parsed: bool = False) -> Union[str, dict]:
David Hendriks
committed
"""
David Hendriks
committed
Function that returns the version information of binary_c. This function calls the function
_binary_c_bindings.return_version_info()
Args:
parsed: Boolean flag whether to parse the version_info output of binary_c. default = False
Returns:
David Hendriks
committed
Either the raw string of binary_c or a parsed version of this in the form of a nested
dictionary
David Hendriks
committed
"""
found_prev = False
if "BINARY_C_MACRO_HEADER" in os.environ:
# the envvar is already present. lets save that and put that back later
found_prev = True
prev_value = os.environ["BINARY_C_MACRO_HEADER"]
os.environ["BINARY_C_MACRO_HEADER"] = "macroxyz"
# Get version_info
version_info = _binary_c_bindings.return_version_info().strip()
David Hendriks
committed
David Hendriks
committed
if parsed:
version_info = parse_binary_c_version_info(version_info)
# delete value
del os.environ["BINARY_C_MACRO_HEADER"]
David Hendriks
committed
# put stuff back if we found a previous one
if found_prev:
os.environ["BINARY_C_MACRO_HEADER"] = prev_value
return version_info
def parse_binary_c_version_info(version_info_string: str) -> dict:
David Hendriks
committed
"""
Function that parses the binary_c version info. Long function with a lot of branches
TODO: fix this function. stuff is missing: isotopes, macros, nucleosynthesis_sources
Args:
version_info_string: raw output of version_info call to binary_c
Returns:
Parsed version of the version info, which is a dictionary containing the keys: 'isotopes' for isotope info, 'argpairs' for argument pair info (TODO: explain), 'ensembles' for ensemble settings/info, 'macros' for macros, 'elements' for atomic element info, 'DTlimit' for (TODO: explain), 'nucleosynthesis_sources' for nucleosynthesis sources, and 'miscellaneous' for all those that were not caught by the previous groups. 'git_branch', 'git_build', 'revision' and 'email' are also keys, but its clear what those contain.
David Hendriks
committed
"""
David Hendriks
committed
version_info_dict = {}
# Clean data and put in correct shape
splitted = version_info_string.strip().splitlines()
David Hendriks
committed
cleaned = {el.strip() for el in splitted if not el == ""}
##########################
# Network:
David Hendriks
committed
networks = {el for el in cleaned if el.startswith("Network ")}
cleaned = cleaned - networks
networks_dict = {}
for el in networks:
network_dict = {}
split_info = el.split("Network ")[-1].strip().split("==")
network_number = int(split_info[0])
network_info_split = split_info[1].split(" is ")
shortname = network_info_split[0].strip()
if not network_info_split[1].strip().startswith(":"):
network_split_info_extra = network_info_split[1].strip().split(":")
longname = network_split_info_extra[0].strip()
implementation = (
network_split_info_extra[1].strip().replace("implemented in", "")
)
if implementation:
network_dict["implemented_in"] = implementation.strip().split()
networks_dict[network_number] = copy.deepcopy(network_dict)
version_info_dict["networks"] = networks_dict if networks_dict else None
##########################
# Isotopes:
# Split off
David Hendriks
committed
isotopes = {el for el in cleaned if el.startswith("Isotope ")}
cleaned = cleaned - isotopes
isotope_dict = {}
for el in isotopes:
split_info = el.split("Isotope ")[-1].strip().split(" is ")
isotope_info = split_info[-1]
name = isotope_info.split(" ")[0].strip()
mass_g = float(
isotope_info.split(",")[0].split("(")[1].split("=")[-1][:-2].strip()
)
mass_amu = float(
isotope_info.split(",")[0].split("(")[-1].split("=")[-1].strip()
)
mass_mev = float(
isotope_info.split(",")[-3].split("=")[-1].replace(")", "").strip()
)
A = int(isotope_info.split(",")[-1].strip().split("=")[-1].replace(")", ""))
Z = int(isotope_info.split(",")[-2].strip().split("=")[-1])
#
isotope_dict[int(split_info[0])] = {
"name": name,
"Z": Z,
"A": A,
"mass_mev": mass_mev,
"mass_g": mass_g,
"mass_amu": mass_amu,
}
version_info_dict["isotopes"] = isotope_dict if isotope_dict else None
##########################
# Argpairs:
# Split off
argpairs = set([el for el in cleaned if el.startswith("ArgPair")])
cleaned = cleaned - argpairs
David Hendriks
committed
argpair_dict = {}
for el in sorted(argpairs):
split_info = el.split("ArgPair ")[-1].split(" ")
if not argpair_dict.get(split_info[0], None):
argpair_dict[split_info[0]] = {split_info[1]: split_info[2]}
else:
argpair_dict[split_info[0]][split_info[1]] = split_info[2]
version_info_dict["argpairs"] = argpair_dict if argpair_dict else None
##########################
# ensembles:
# Split off
David Hendriks
committed
ensembles = {el for el in cleaned if el.startswith("Ensemble")}
cleaned = cleaned - ensembles
ensemble_dict = {}
for el in ensembles:
split_info = el.split("Ensemble ")[-1].split(" is ")
ensemble_dict[int(split_info[0])] = split_info[-1]
version_info_dict["ensembles"] = ensemble_dict if ensemble_dict else None
##########################
# macros:
# Split off
David Hendriks
committed
macros = {el for el in cleaned if el.startswith("macroxyz")}
cleaned = cleaned - macros
param_type_dict = {
"STRING": str,
"FLOAT": float,
"MACRO": str,
"INT": int,
"LONG_INT": int,
}
macros_dict = {}
for el in macros:
split_info = el.split("macroxyz ")[-1].split(" : ")
param_type = split_info[0]
new_split = "".join(split_info[1:]).split(" is ")
param_name = new_split[0]
param_value = " is ".join(new_split[1:])
# Sometimes the macros have extra information behind it. Needs an update in outputting by binary_c
try:
macros_dict[param_name] = param_type_dict[param_type](param_value)
except ValueError:
macros_dict[param_name] = str(param_value)
version_info_dict["macros"] = macros_dict if macros_dict else None
##########################
# Elements:
# Split off:
David Hendriks
committed
elements = {el for el in cleaned if el.startswith("Element")}
cleaned = cleaned - elements
# Fill dict:
elements_dict = {}
for el in elements:
split_info = el.split("Element ")[-1].split(" : ")
name_info = split_info[0].split(" is ")
# get isotope info
isotopes = {}
if not split_info[-1][0] == "0":
isotope_string = split_info[-1].split(" = ")[-1]
isotopes = {
int(split_isotope.split("=")[0]): split_isotope.split("=")[1]
for split_isotope in isotope_string.split(" ")
}
elements_dict[int(name_info[0])] = {
"name": name_info[-1],
"atomic_number": int(name_info[0]),
"amt_isotopes": len(isotopes),
"isotopes": isotopes,
}
version_info_dict["elements"] = elements_dict if elements_dict else None
##########################
# dt_limits:
# split off
David Hendriks
committed
dt_limits = {el for el in cleaned if el.startswith("DTlimit")}
cleaned = cleaned - dt_limits
# Fill dict
dt_limits_dict = {}
for el in dt_limits:
split_info = el.split("DTlimit ")[-1].split(" : ")
dt_limits_dict[split_info[1].strip()] = {
"index": int(split_info[0]),
"value": float(split_info[-1]),
}
version_info_dict["dt_limits"] = dt_limits_dict if dt_limits_dict else None
##########################
# Nucleosynthesis sources:
# Split off
David Hendriks
committed
nucsyn_sources = {el for el in cleaned if el.startswith("Nucleosynthesis")}
cleaned = cleaned - nucsyn_sources
# Fill dict
nucsyn_sources_dict = {}
for el in nucsyn_sources:
split_info = el.split("Nucleosynthesis source")[-1].strip().split(" is ")
nucsyn_sources_dict[int(split_info[0])] = split_info[-1]
version_info_dict["nucleosynthesis_sources"] = (
nucsyn_sources_dict if nucsyn_sources_dict else None
)
##########################
# miscellaneous:
# All those that I didnt catch with the above filters. Could try to get some more out though.
# TODO: filter a bit more.
misc_dict = {}
git_revision = [el for el in cleaned if el.startswith("git revision")]
misc_dict["git_revision"] = (
git_revision[0].split("git revision ")[-1].replace('"', "")
)
cleaned = cleaned - set(git_revision)
# filter out git url
git_url = [el for el in cleaned if el.startswith("git URL")]
misc_dict["git_url"] = git_url[0].split("git URL ")[-1].replace('"', "")
cleaned = cleaned - set(git_url)
# filter out version
version = [el for el in cleaned if el.startswith("Version")]
misc_dict["version"] = str(version[0].split("Version ")[-1])
git_branch = [el for el in cleaned if el.startswith("git branch")]
misc_dict["git_branch"] = git_branch[0].split("git branch ")[-1].replace('"', "")
build = [el for el in cleaned if el.startswith("Build")]
misc_dict["build"] = build[0].split("Build: ")[-1].replace('"', "")
email = [el for el in cleaned if el.startswith("Email")]
misc_dict["email"] = email[0].split("Email ")[-1].split(",")
other_items = set([el for el in cleaned if " is " in el])
version_info_dict["miscellaneous"] = misc_dict if misc_dict else None
David Hendriks
committed
return version_info_dict
David Hendriks
committed
########################################################
# binary_c output functions
########################################################
def output_lines(output: str) -> list:
David Hendriks
committed
"""
Function that outputs the lines that were recieved from the binary_c run, but now as an iterator.
Args:
output: raw binary_c output
Returns:
Iterator over the lines of the binary_c output
David Hendriks
committed
"""
if output:
return output.splitlines()
David Hendriks
committed
return []
def example_parse_output(output: str, selected_header: str) -> dict:
David Hendriks
committed
"""
David Hendriks
committed
Function that parses output of binary_c. This version serves as an example and is quite
detailed. Custom functions can be easier:
David Hendriks
committed
David Hendriks
committed
This function works in two cases:
if the caught line contains output like 'example_header time=12.32 mass=0.94 ..'
or if the line contains output like 'example_header 12.32 0.94'
David Hendriks
committed
David Hendriks
committed
You can give a 'selected_header' to catch any line that starts with that.
Then the values will be put into a dictionary.
David Hendriks
committed
Tasks:
- TODO: Think about exporting to numpy array or pandas instead of a defaultdict
- TODO: rethink whether this function is necessary at all
- TODO: check this function again
Args:
output: binary_c output string
David Hendriks
committed
selected_header: string header of the output (the start of the line that you want to
process)
dictionary containing parameters as keys and lists for the values
David Hendriks
committed
value_dicts = []
David Hendriks
committed
# split output on newlines
for line in output.split("\n"):
# Skip any blank lines
if not line == "":
split_line = line.split()
David Hendriks
committed
# Select parts
header = split_line[0]
values_list = split_line[1:]
David Hendriks
committed
# print(values_list)
# Catch line starting with selected header
if header == selected_header:
# Check if the line contains '=' symbols:
value_dict = {}
if all("=" in value for value in values_list):
for value in values_list:
key, val = value.split("=")
value_dict[key.strip()] = val.strip()
value_dicts.append(value_dict)
else:
if any("=" in value for value in values_list):
raise ValueError(
"Caught line contains some = symbols but not \
all of them do. aborting run"
)
David Hendriks
committed
for j, val in enumerate(values_list):
value_dict[j] = val
value_dicts.append(value_dict)
David Hendriks
committed
if len(value_dicts) == 0:
print(
"Sorry, didnt find any line matching your header {}".format(selected_header)
David Hendriks
committed
return None
David Hendriks
committed
keys = value_dicts[0].keys()
David Hendriks
committed
# Construct final dict.
final_values_dict = defaultdict(list)
for value_dict in value_dicts:
for key in keys:
final_values_dict[key].append(value_dict[key])
David Hendriks
committed
return final_values_dict
David Hendriks
committed
########################################################
# Argument and default value functions
########################################################
def get_defaults(filter_values: bool = False) -> dict:
David Hendriks
committed
"""
Function that calls the binaryc get args function and cast it into a dictionary.
David Hendriks
committed
All the values are strings
Args:
filter_values: whether to filter out NULL and Function defaults.
Returns:
dictionary containing the parameter name as key and the parameter default as value
David Hendriks
committed
"""
default_output = _binary_c_bindings.return_arglines()
David Hendriks
committed
default_dict = {}
David Hendriks
committed
for default in default_output.split("\n"):
if not default in ["__ARG_BEGIN", "__ARG_END", ""]:
key, value = default.split(" = ")
default_dict[key] = value
David Hendriks
committed
if filter_values:
default_dict = filter_arg_dict(default_dict)
David Hendriks
committed
return default_dict
David Hendriks
committed
"""
David Hendriks
committed
Function that return the list of possible keys to give in the arg string.
This function calls get_defaults()
David Hendriks
committed
list of all the parameters that binary_c accepts (and has default values for, since
we call get_defaults())
David Hendriks
committed
"""
def filter_arg_dict(arg_dict: dict) -> dict:
David Hendriks
committed
"""
Function to filter out keys that contain values included in ['NULL', 'Function', '']
This function is called by get_defaults()
Args:
arg_dict: dictionary containing the argument + default keypairs of binary_c
Returns:
filtered dictionary (pairs with NULL and Function values are removed)
David Hendriks
committed
"""
old_dict = arg_dict.copy()
new_dict = {}
David Hendriks
committed
for key in old_dict.keys():
if not old_dict[key] in ["NULL", "Function"]:
if not old_dict[key] == "":
new_dict[key] = old_dict[key]
return new_dict
def create_arg_string(
arg_dict: dict, sort: bool = False, filter_values: bool = False
) -> str:
David Hendriks
committed
Function that creates the arg string for binary_c. Takes a dictionary containing the arguments
and writes them to a string
This string is missing the 'binary_c ' at the start.
arg_dict: dictionary
sort: (optional, default = False) Boolean whether to sort the order of the keys.
filter_values: (optional, default = False) filters the input dict on keys that have NULL or `function` as value.
The string built up by combining all the key + value's.
David Hendriks
committed
David Hendriks
committed
if filter_values:
David Hendriks
committed
keys = sorted(arg_dict.keys()) if sort else arg_dict.keys()
David Hendriks
committed
arg_string += "{key} {value} ".format(key=key, value=arg_dict[key])
arg_string = arg_string.strip()
return arg_string
David Hendriks
committed
########################################################
# Help functions
########################################################
def get_help(
param_name: str = "", print_help: bool = True, fail_silently: bool = False
) -> Union[dict, None]:
Function that returns the help info for a given parameter, by interfacing with binary_c
Will check whether it is a valid parameter.
Binary_c will output things in the following order;
- Did you mean?
- binary_c help for variable
David Hendriks
committed
- default
- available macros
This function reads out that structure and catches the different components of this output
David Hendriks
committed
Tasks:
- TODO: consider not returning None, but return empty dict
David Hendriks
committed
param_name: name of the parameter that you want info from. Will get checked whether its a
valid parameter name
print_help: (optional, default = True) whether to print out the help information
David Hendriks
committed
fail_silently: (optional, default = False) Whether to print the errors raised if the
parameter isn't valid
David Hendriks
committed
Dictionary containing the help info. This dictionary contains 'parameter_name',
'parameter_value_input_type', 'description', optionally 'macros'
available_arg_keys = get_arg_keys()
if not param_name:
print(
"Please set the param_name to any of the following:\n {}".format(
sorted(available_arg_keys)
)
)
return None