lightning/pytorch_lightning/utilities/parsing.py

196 lines
6.2 KiB
Python

import inspect
from argparse import Namespace
from typing import Dict
def str_to_bool(val):
"""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.
>>> str_to_bool('YES')
1
>>> str_to_bool('FALSE')
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}')
def clean_namespace(hparams):
"""Removes all functions from hparams so we can pickle."""
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]
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
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
class AttributeDict(Dict):
"""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
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.')