Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ Chronological list of authors

2018
- Shujie Fan
- Richard J Gowers
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you also have to add yourself to docs/conf.py – we haven't introduced fancy authorship processing here.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And add entry to CHANGELOG.


3 changes: 2 additions & 1 deletion CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ The rules for this file:
* release numbers follow "Semantic Versioning" http://semver.org

------------------------------------------------------------------------------
xx/xx/18 VOD555
xx/xx/18 VOD555, richardjgowers

* 0.2.0

Enhancements
* add add timing for _conclude and _prepare (Issue #49)
* add parallel particle-particle RDF calculation module pmda.rdf (Issue #41)
* add readonly_attributes context manager to ParallelAnalysisBase


06/07/18 orbeckst
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@

# General information about the project.
project = u'PMDA'
author = u'Max Linke, Shujie Fan, Oliver Beckstein'
author = u'Max Linke, Shujie Fan, Richard J. Gowers, Oliver Beckstein'
copyright = u'2018, ' + author


Expand Down
52 changes: 40 additions & 12 deletions pmda/parallel.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

"""
from __future__ import absolute_import, division
from contextlib import contextmanager
from six.moves import range

import MDAnalysis as mda
Expand Down Expand Up @@ -154,6 +155,32 @@ def __init__(self, universe, atomgroups):
self._traj = universe.trajectory.filename
self._indices = [ag.indices for ag in atomgroups]

@contextmanager
def readonly_attributes(self):
"""Set the attributes of this class to be read only

Useful to avoid the class being modified when passing it around.

To be used as a context manager::

with analysis.readonly_attributes():
some_function(analysis)

"""
self._attr_lock = True
yield
self._attr_lock = False

def __setattr__(self, key, val):
# guards to stop people assigning to self when they shouldn't
# if locked, the only attribute you can modify is _attr_lock
# if self._attr_lock isn't set, default to unlocked
if key == '_attr_lock' or not getattr(self, '_attr_lock', False):
super(ParallelAnalysisBase, self).__setattr__(key, val)
else:
# raise HalError("I'm sorry Dave, I'm afraid I can't do that")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kain88-de I'm hoping that this survives pickling as it's just a class attribute. We are just pickling dict directly right?

Honestly, no idea.

raise AttributeError("Can't set attribute at this time")

def _conclude(self):
"""Finalise the results you've gathered.

Expand Down Expand Up @@ -259,18 +286,19 @@ def run(self,
self._prepare()
time_prepare = prepare.elapsed
blocks = []
for b in range(n_blocks):
task = delayed(
self._dask_helper, pure=False)(
b * bsize * step + start,
min(stop, (b + 1) * bsize * step + start),
step,
self._indices,
self._top,
self._traj, )
blocks.append(task)
blocks = delayed(blocks)
res = blocks.compute(**scheduler_kwargs)
with self.readonly_attributes():
for b in range(n_blocks):
task = delayed(
self._dask_helper, pure=False)(
b * bsize * step + start,
min(stop, (b + 1) * bsize * step + start),
step,
self._indices,
self._top,
self._traj, )
blocks.append(task)
blocks = delayed(blocks)
res = blocks.compute(**scheduler_kwargs)
self._results = np.asarray([el[0] for el in res])
with timeit() as conclude:
self._conclude()
Expand Down
19 changes: 19 additions & 0 deletions pmda/test/test_parallel.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,22 @@ def test_nblocks(analysis, n_blocks):
def test_guess_nblocks(analysis):
analysis.run(n_jobs=-1)
assert len(analysis._results) == joblib.cpu_count()


def test_attrlock():
u = mda.Universe(PSF, DCD)
pab = parallel.ParallelAnalysisBase(u, (u.atoms,))

# Should initially be allowed to set attributes
pab.thing1 = 24
assert pab.thing1 == 24
# Apply lock
with pab.readonly_attributes():
# Reading should still work
assert pab.thing1 == 24
# Setting should fail
with pytest.raises(AttributeError):
pab.thing2 = 100
# Outside of lock context setting should again work
pab.thing2 = 100
assert pab.thing2 == 100