Skip to content

Commit

Permalink
Add option to add metadata to artifacts
Browse files Browse the repository at this point in the history
A dict with custom metadata can now be added to artifacts. This only has
an effect for the MongoObserver. The metadata will appear in the
`.metadata` attribute of the file like objects retrieved with pymongo
from gridfs.
  • Loading branch information
JarnoRFB committed Jul 13, 2018
1 parent 8b73c80 commit 46b35a6
Show file tree
Hide file tree
Showing 4 changed files with 18 additions and 11 deletions.
8 changes: 5 additions & 3 deletions sacred/experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ def add_resource(self, filename):
assert self.current_run is not None, "Can only be called during a run."
self.current_run.add_resource(filename)

def add_artifact(self, filename, name=None):
def add_artifact(self, filename, name=None, metadata=None):
"""Add a file as an artifact.
In Sacred terminology an artifact is a file produced by the experiment
Expand All @@ -338,10 +338,12 @@ def add_artifact(self, filename, name=None):
name : str, optional
optionally set the name of the artifact.
Defaults to the relative file-path.
metadata: dict, optional
optionally attach metadata to the artifact.
This only has an effect when using the MongoObserver.
"""
assert self.current_run is not None, "Can only be called during a run."
self.current_run.add_artifact(filename, name)
self.current_run.add_artifact(filename, name, metadata)

@property
def info(self):
Expand Down
4 changes: 2 additions & 2 deletions sacred/observers/mongo.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,12 +184,12 @@ def resource_event(self, filename):
self.run_entry['resources'].append((filename, md5hash))
self.save()

def artifact_event(self, name, filename):
def artifact_event(self, name, filename, metadata=None):
with open(filename, 'rb') as f:
run_id = self.run_entry['_id']
db_filename = 'artifact://{}/{}/{}'.format(self.runs.name, run_id,
name)
file_id = self.fs.put(f, filename=db_filename)
file_id = self.fs.put(f, filename=db_filename, metadata=metadata)
self.run_entry['artifacts'].append({'name': name,
'file_id': file_id})
self.save()
Expand Down
12 changes: 8 additions & 4 deletions sacred/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ def add_resource(self, filename):
filename = os.path.abspath(filename)
self._emit_resource_added(filename)

def add_artifact(self, filename, name=None):
def add_artifact(self, filename, name=None, metadata=None):
"""Add a file as an artifact.
In Sacred terminology an artifact is a file produced by the experiment
Expand All @@ -173,10 +173,13 @@ def add_artifact(self, filename, name=None):
name : str, optional
optionally set the name of the artifact.
Defaults to the filename.
metadata: dict
optionally attach metadata to the artifact.
This only has an effect when using the MongoObserver.
"""
filename = os.path.abspath(filename)
name = os.path.basename(filename) if name is None else name
self._emit_artifact_added(name, filename)
self._emit_artifact_added(name, filename, metadata)

def __call__(self, *args):
r"""Start this run.
Expand Down Expand Up @@ -374,11 +377,12 @@ def _emit_resource_added(self, filename):
for observer in self.observers:
self._safe_call(observer, 'resource_event', filename=filename)

def _emit_artifact_added(self, name, filename):
def _emit_artifact_added(self, name, filename, metadata):
for observer in self.observers:
self._safe_call(observer, 'artifact_event',
name=name,
filename=filename)
filename=filename,
metadata=metadata)

def _safe_call(self, obs, method, **kwargs):
if obs not in self._failed_observers and hasattr(obs, method):
Expand Down
5 changes: 3 additions & 2 deletions tests/test_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,9 @@ def test_run_heartbeat_event(run):
def test_run_artifact_event(run):
observer = run.observers[0]
handle, f_name = tempfile.mkstemp()
run.add_artifact(f_name, name='foobar')
observer.artifact_event.assert_called_with(filename=f_name, name='foobar')
metadata = {'testkey': 42}
run.add_artifact(f_name, name='foobar', metadata=metadata)
observer.artifact_event.assert_called_with(filename=f_name, name='foobar', metadata=metadata)
os.close(handle)
os.remove(f_name)

Expand Down

0 comments on commit 46b35a6

Please sign in to comment.