2020-06-08 11:19:34 +00:00
|
|
|
import inspect
|
2020-04-27 10:51:42 +00:00
|
|
|
from argparse import Namespace
|
2020-07-20 23:00:20 +00:00
|
|
|
from typing import Dict
|
2020-04-27 10:51:42 +00:00
|
|
|
|
|
|
|
|
2020-06-08 11:19:34 +00:00
|
|
|
def str_to_bool(val):
|
2020-04-26 13:20:06 +00:00
|
|
|
"""Convert a string representation of truth to true (1) or false (0).
|
|
|
|
Copied from the python implementation distutils.utils.strtobool
|
|
|
|
|
|
|
|
True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
|
|
|
|
are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if
|
|
|
|
'val' is anything else.
|
|
|
|
|
2020-06-08 11:19:34 +00:00
|
|
|
>>> str_to_bool('YES')
|
2020-04-26 13:20:06 +00:00
|
|
|
1
|
2020-06-08 11:19:34 +00:00
|
|
|
>>> str_to_bool('FALSE')
|
2020-04-26 13:20:06 +00:00
|
|
|
0
|
|
|
|
"""
|
|
|
|
val = val.lower()
|
|
|
|
if val in ('y', 'yes', 't', 'true', 'on', '1'):
|
|
|
|
return 1
|
|
|
|
elif val in ('n', 'no', 'f', 'false', 'off', '0'):
|
|
|
|
return 0
|
|
|
|
else:
|
|
|
|
raise ValueError(f'invalid truth value {val}')
|
2020-04-27 10:51:42 +00:00
|
|
|
|
|
|
|
|
|
|
|
def clean_namespace(hparams):
|
2020-06-08 11:19:34 +00:00
|
|
|
"""Removes all functions from hparams so we can pickle."""
|
2020-04-27 10:51:42 +00:00
|
|
|
|
|
|
|
if isinstance(hparams, Namespace):
|
|
|
|
del_attrs = []
|
|
|
|
for k in hparams.__dict__:
|
|
|
|
if callable(getattr(hparams, k)):
|
|
|
|
del_attrs.append(k)
|
|
|
|
|
|
|
|
for k in del_attrs:
|
|
|
|
delattr(hparams, k)
|
|
|
|
|
|
|
|
elif isinstance(hparams, dict):
|
|
|
|
del_attrs = []
|
|
|
|
for k, v in hparams.items():
|
|
|
|
if callable(v):
|
|
|
|
del_attrs.append(k)
|
|
|
|
|
|
|
|
for k in del_attrs:
|
|
|
|
del hparams[k]
|
2020-06-08 11:19:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
def get_init_args(frame) -> dict:
|
|
|
|
_, _, _, local_vars = inspect.getargvalues(frame)
|
|
|
|
if '__class__' not in local_vars:
|
|
|
|
return
|
|
|
|
cls = local_vars['__class__']
|
|
|
|
spec = inspect.getfullargspec(cls.__init__)
|
|
|
|
init_parameters = inspect.signature(cls.__init__).parameters
|
|
|
|
self_identifier = spec.args[0] # "self" unless user renames it (always first arg)
|
|
|
|
varargs_identifier = spec.varargs # by convention this is named "*args"
|
|
|
|
kwargs_identifier = spec.varkw # by convention this is named "**kwargs"
|
|
|
|
exclude_argnames = (
|
|
|
|
varargs_identifier, kwargs_identifier, self_identifier, '__class__', 'frame', 'frame_args'
|
|
|
|
)
|
|
|
|
|
|
|
|
# only collect variables that appear in the signature
|
|
|
|
local_args = {k: local_vars[k] for k in init_parameters.keys()}
|
|
|
|
local_args.update(local_args.get(kwargs_identifier, {}))
|
|
|
|
local_args = {k: v for k, v in local_args.items() if k not in exclude_argnames}
|
|
|
|
return local_args
|
|
|
|
|
|
|
|
|
|
|
|
def collect_init_args(frame, path_args: list, inside: bool = False) -> list:
|
|
|
|
"""
|
|
|
|
Recursively collects the arguments passed to the child constructors in the inheritance tree.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
frame: the current stack frame
|
|
|
|
path_args: a list of dictionaries containing the constructor args in all parent classes
|
|
|
|
inside: track if we are inside inheritance path, avoid terminating too soon
|
|
|
|
|
|
|
|
Return:
|
|
|
|
A list of dictionaries where each dictionary contains the arguments passed to the
|
|
|
|
constructor at that level. The last entry corresponds to the constructor call of the
|
|
|
|
most specific class in the hierarchy.
|
|
|
|
"""
|
|
|
|
_, _, _, local_vars = inspect.getargvalues(frame)
|
|
|
|
if '__class__' in local_vars:
|
|
|
|
local_args = get_init_args(frame)
|
|
|
|
# recursive update
|
|
|
|
path_args.append(local_args)
|
|
|
|
return collect_init_args(frame.f_back, path_args, inside=True)
|
|
|
|
elif not inside:
|
|
|
|
return collect_init_args(frame.f_back, path_args, inside)
|
|
|
|
else:
|
|
|
|
return path_args
|
|
|
|
|
|
|
|
|
2020-07-22 17:53:10 +00:00
|
|
|
def flatten_dict(source, result=None):
|
|
|
|
if result is None:
|
|
|
|
result = {}
|
|
|
|
|
|
|
|
for k, v in source.items():
|
|
|
|
if isinstance(v, dict):
|
|
|
|
_ = flatten_dict(v, result)
|
|
|
|
else:
|
|
|
|
result[k] = v
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
2020-07-20 23:00:20 +00:00
|
|
|
class AttributeDict(Dict):
|
2020-06-08 11:19:34 +00:00
|
|
|
"""Extended dictionary accesisable with dot notation.
|
|
|
|
|
|
|
|
>>> ad = AttributeDict({'key1': 1, 'key2': 'abc'})
|
|
|
|
>>> ad.key1
|
|
|
|
1
|
|
|
|
>>> ad.update({'my-key': 3.14})
|
|
|
|
>>> ad.update(mew_key=42)
|
|
|
|
>>> ad.key1 = 2
|
|
|
|
>>> ad
|
|
|
|
"key1": 2
|
|
|
|
"key2": abc
|
|
|
|
"mew_key": 42
|
|
|
|
"my-key": 3.14
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __getattr__(self, key):
|
|
|
|
try:
|
|
|
|
return self[key]
|
|
|
|
except KeyError:
|
|
|
|
raise AttributeError(f'Missing attribute "{key}"')
|
|
|
|
|
|
|
|
def __setattr__(self, key, val):
|
|
|
|
self[key] = val
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
if not len(self):
|
|
|
|
return ""
|
|
|
|
max_key_length = max([len(str(k)) for k in self])
|
|
|
|
tmp_name = '{:' + str(max_key_length + 3) + 's} {}'
|
|
|
|
rows = [tmp_name.format(f'"{n}":', self[n]) for n in sorted(self.keys())]
|
|
|
|
out = '\n'.join(rows)
|
|
|
|
return out
|
2020-08-06 22:34:48 +00:00
|
|
|
|
|
|
|
|
|
|
|
def lightning_hasattr(model, attribute):
|
|
|
|
""" Special hasattr for lightning. Checks for attribute in model namespace
|
|
|
|
and the old hparams namespace/dict """
|
|
|
|
# Check if attribute in model
|
|
|
|
if hasattr(model, attribute):
|
|
|
|
attr = True
|
|
|
|
# Check if attribute in model.hparams, either namespace or dict
|
|
|
|
elif hasattr(model, 'hparams'):
|
|
|
|
if isinstance(model.hparams, dict):
|
|
|
|
attr = attribute in model.hparams
|
|
|
|
else:
|
|
|
|
attr = hasattr(model.hparams, attribute)
|
|
|
|
else:
|
|
|
|
attr = False
|
|
|
|
|
|
|
|
return attr
|
|
|
|
|
|
|
|
|
|
|
|
def lightning_getattr(model, attribute):
|
|
|
|
""" Special getattr for lightning. Checks for attribute in model namespace
|
|
|
|
and the old hparams namespace/dict """
|
|
|
|
# Check if attribute in model
|
|
|
|
if hasattr(model, attribute):
|
|
|
|
attr = getattr(model, attribute)
|
|
|
|
# Check if attribute in model.hparams, either namespace or dict
|
|
|
|
elif hasattr(model, 'hparams'):
|
|
|
|
if isinstance(model.hparams, dict):
|
|
|
|
attr = model.hparams[attribute]
|
|
|
|
else:
|
|
|
|
attr = getattr(model.hparams, attribute)
|
|
|
|
else:
|
|
|
|
raise ValueError(f'{attribute} is not stored in the model namespace'
|
|
|
|
' or the `hparams` namespace/dict.')
|
|
|
|
return attr
|
|
|
|
|
|
|
|
|
|
|
|
def lightning_setattr(model, attribute, value):
|
|
|
|
""" Special setattr for lightning. Checks for attribute in model namespace
|
|
|
|
and the old hparams namespace/dict """
|
|
|
|
# Check if attribute in model
|
|
|
|
if hasattr(model, attribute):
|
|
|
|
setattr(model, attribute, value)
|
|
|
|
# Check if attribute in model.hparams, either namespace or dict
|
|
|
|
elif hasattr(model, 'hparams'):
|
|
|
|
if isinstance(model.hparams, dict):
|
|
|
|
model.hparams[attribute] = value
|
|
|
|
else:
|
|
|
|
setattr(model.hparams, attribute, value)
|
|
|
|
else:
|
|
|
|
raise ValueError(f'{attribute} is not stored in the model namespace'
|
|
|
|
' or the `hparams` namespace/dict.')
|