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

WIP: Error messages #348

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 23 additions & 4 deletions sacred/config/captured_function.py
Expand Up @@ -9,9 +9,10 @@
from sacred.config.custom_containers import FallbackDict
from sacred.config.signature import Signature
from sacred.randomness import create_rnd, get_seed
from sacred.utils import ConfigError, MissingConfigError


def create_captured_function(function, prefix=None):
def create_captured_function(function, prefix=None, ingredient=None):
sig = Signature(function)
function.signature = sig
function.uses_randomness = ("_seed" in sig.arguments or
Expand All @@ -21,6 +22,7 @@ def create_captured_function(function, prefix=None):
function.rnd = None
function.run = None
function.prefix = prefix
function.ingredient = ingredient
return captured_function(function)


Expand All @@ -37,13 +39,30 @@ def captured_function(wrapped, instance, args, kwargs):
options['_rnd'] = create_rnd(options['_seed'])

bound = (instance is not None)
args, kwargs = wrapped.signature.construct_arguments(args, kwargs, options,
bound)
try:
args, kwargs = wrapped.signature.construct_arguments(args, kwargs,
options,
bound)
except MissingConfigError as e:
if e.func is None:
e.func = wrapped
raise e

if wrapped.logger is not None:
wrapped.logger.debug("Started")
start_time = time.time()
# =================== run actual function =================================
result = wrapped(*args, **kwargs)
try:
result = wrapped(*args, **kwargs)
except ConfigError as e:
if not e.__prefix_handled__:
if wrapped.prefix:
e.conflicting_configs = ('.'.join((wrapped.prefix, str(c))) for
c in e.conflicting_configs)
e.__config_sources__ = wrapped.sources
e.__config__ = wrapped.config
e.__prefix_handled__ = True
raise e
# =========================================================================
if wrapped.logger is not None:
stop_time = time.time()
Expand Down
3 changes: 2 additions & 1 deletion sacred/config/config_dict.py
Expand Up @@ -7,9 +7,10 @@


class ConfigDict(object):
def __init__(self, d):
def __init__(self, d, source=None):
super(ConfigDict, self).__init__()
self._conf = normalize_or_die(d)
self.config_source = source

def __call__(self, fixed=None, preset=None, fallback=None):
result = dogmatize(fixed or {})
Expand Down
6 changes: 4 additions & 2 deletions sacred/config/config_scope.py
Expand Up @@ -11,6 +11,7 @@
from copy import copy

from sacred import SETTINGS
from sacred.config.config_sources import ConfigScopeConfigSource
from sacred.config.config_summary import ConfigSummary
from sacred.config.utils import dogmatize, normalize_or_die, recursive_fill_in
from sacred.config.signature import get_argspec
Expand All @@ -28,7 +29,8 @@ def __init__(self, func):
"default values are not allowed for ConfigScope functions"

self._func = func
self._body_code = get_function_body_code(func)
self._body_code, file_name, line_offset = get_function_body_code(func)
self.config_source = ConfigScopeConfigSource.from_filename_and_lineno(file_name, line_offset)
self._var_docs = get_config_comments(func)
self.__doc__ = self._func.__doc__

Expand Down Expand Up @@ -161,7 +163,7 @@ def get_function_body_code(func):
statement.strip(), filename, lineno))
else:
raise
return body_code
return body_code, filename, line_offset


def is_ignored(line):
Expand Down
88 changes: 88 additions & 0 deletions sacred/config/config_sources.py
@@ -0,0 +1,88 @@
class ConfigSource:
@staticmethod
def get_string(config_source, ):
if isinstance(config_source, dict):
def _get_sources(d):
sources = set()
if isinstance(d, dict):
for v in config_source.values():
sources.update(_get_sources(v))
else:
sources.add(d)
return sources
sources = _get_sources(config_source)
# '\n'.join([s.get_source_string_for_config()])
else:
return config_source.get_source_string_for_config

def get_source_string_for_config(self, config=None):
raise NotImplementedError()


class FileConfigSource(ConfigSource):
@classmethod
def from_filename_and_lineno(cls, filename, lineno=None, **kwargs):
return cls(filename, lineno, **kwargs)

@classmethod
def from_stack(cls, offset, **kwargs):
import inspect
stack = inspect.stack()
frame = stack[offset + 1]
return cls(frame.filename, frame.lineno, **kwargs)

def __init__(self, file, line=None) -> None:
super().__init__()
self.file = file
self.line = line

def get_source_string_for_config(self, config=None):
if self.line is None:
return '"{}"'.format(self.file)
else:
return '"{}:{}"'.format(self.file, self.line)


class ConfigScopeConfigSource(FileConfigSource):

def get_source_string_for_config(self, config=None):
return 'ConfigScope at' + super().get_source_string_for_config(config)


class NamedConfigScopeConfigSource(ConfigScopeConfigSource):
@classmethod
def from_file_config_source(cls, file_config_source, config_name):
return cls(file_config_source.file, file_config_source.line,
config_name)

def __init__(self, file, line=None, config_name=None) -> None:
super().__init__(file, line)
self.config_name = config_name

def get_source_string_for_config(self, config=None):
file = super().get_source_string_for_config(config)
return 'Named Config "{}" at {}'.format(self.config_name, file)


class CommandLineConfigSource(ConfigSource):

def __init__(self, config_updates) -> None:
super().__init__()
self.config_updates = config_updates

def get_source_string_for_config(self, config=None):
if config is not None and config in self.config_updates:
return 'command line config "{}={}"'.format(config,
self.config_updates[
config])
else:
return 'command line config with "{}"'.format(' '.join(
'{}={}'.format(k, v) for k, v in self.config_updates.items()))


class ConfigDictConfigSource(FileConfigSource):
def get_source_string_for_config(self, config=None):
return f'{self.__class__.__name__} in ' + \
super().get_source_string_for_config(config)


6 changes: 5 additions & 1 deletion sacred/config/config_summary.py
Expand Up @@ -7,13 +7,14 @@

class ConfigSummary(dict):
def __init__(self, added=(), modified=(), typechanged=(),
ignored_fallbacks=(), docs=()):
ignored_fallbacks=(), docs=(), sources=()):
super(ConfigSummary, self).__init__()
self.added = set(added)
self.modified = set(modified) # TODO: test for this member
self.typechanged = dict(typechanged)
self.ignored_fallbacks = set(ignored_fallbacks) # TODO: test
self.docs = dict(docs)
self.sources = dict(sources)
self.ensure_coherence()

def update_from(self, config_mod, path=''):
Expand All @@ -40,6 +41,9 @@ def update_add(self, config_mod, path=''):
self.docs.update({join_paths(path, k): v
for k, v in config_mod.docs.items()
if path == '' or k != 'seed'})
self.sources.update({join_paths(path, k): v
for k, v in config_mod.sources.items()
if path == '' or k != 'seed'})
self.ensure_coherence()

def ensure_coherence(self):
Expand Down
5 changes: 3 additions & 2 deletions sacred/config/signature.py
Expand Up @@ -6,6 +6,7 @@
from collections import OrderedDict
import sys

from sacred.utils import MissingConfigError

if sys.version_info[0] < 3: # python2
def get_argspec(f):
Expand Down Expand Up @@ -156,5 +157,5 @@ def _assert_no_missing_args(self, args, kwargs, bound):
free_params = self.get_free_parameters(args, kwargs, bound)
missing_args = [m for m in free_params if m not in self.kwargs]
if missing_args:
raise TypeError("{} is missing value(s) for {}".format(
self.name, missing_args))
raise MissingConfigError("{} is missing value(s) for {}".format(
self.name, missing_args), missing_configs=missing_args)
19 changes: 16 additions & 3 deletions sacred/config/utils.py
Expand Up @@ -6,8 +6,11 @@

from sacred import SETTINGS
import sacred.optional as opt
from sacred.config.config_sources import ConfigScopeConfigSource, \
ConfigDictConfigSource
from sacred.config.custom_containers import DogmaticDict, DogmaticList
from sacred.utils import PYTHON_IDENTIFIER, basestring
from sacred.utils import PYTHON_IDENTIFIER, basestring, recursive_update, \
convert_to_nested_dict, iterate_flattened, get_by_dotted_path


def assert_is_valid_key(key):
Expand Down Expand Up @@ -100,17 +103,27 @@ def chain_evaluate_config_scopes(config_scopes, fixed=None, preset=None,
fallback = fallback or {}
final_config = dict(preset or {})
config_summaries = []
config_sources = {}

fixed_keys = [k for k, _ in iterate_flattened(fixed)]
for config in config_scopes:
flattened_preset = dict(iterate_flattened(preset))
cfg = config(fixed=fixed,
preset=final_config,
fallback=fallback)
config_summaries.append(cfg)
final_config.update(cfg)
source = config.config_source

recursive_update(
config_sources,
{k: source for k, _ in iterate_flattened(cfg) if k not in fixed_keys and (k, get_by_dotted_path(cfg, k)) not in flattened_preset}
)
final_config.update(cfg)
config_sources = convert_to_nested_dict(config_sources)
if not config_scopes:
final_config.update(fixed)

return undogmatize(final_config), config_summaries
return undogmatize(final_config), config_summaries, config_sources


def dogmatize(obj):
Expand Down