Skip to content

Commit

Permalink
Merge pull request #119 from ZLLentz/md-edit
Browse files Browse the repository at this point in the history
API: Simplify metadata_namespace into tree_namespace
  • Loading branch information
ZLLentz authored May 7, 2018
2 parents d749122 + 7fbb657 commit 373aab1
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 86 deletions.
7 changes: 3 additions & 4 deletions hutch_python/load_conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from .daq import get_daq_objs
from .exp_load import get_exp_objs
from .happi import get_happi_objs, get_lightpath
from .namespace import class_namespace, metadata_namespace
from .namespace import class_namespace, tree_namespace
from .qs_load import get_qs_objs
from .user_load import get_user_objs
from .utils import (get_current_experiment, safe_load, hutch_banner,
Expand Down Expand Up @@ -277,10 +277,9 @@ def load_conf(conf, hutch_dir=None):
default_class_namespace('EpicsMotor', 'motors', cache)
default_class_namespace('Slits', 'slits', cache)
if hutch is not None:
meta = metadata_namespace(['beamline', 'stand'],
scope='hutch_python.db')
tree = tree_namespace(scope='hutch_python.db')
# Prune meta, remove branches with only one object
for name, space in meta.__dict__.items():
for name, space in tree.__dict__.items():
if count_ns_leaves(space) > 1:
cache(**{name: space})

Expand Down
66 changes: 25 additions & 41 deletions hutch_python/namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,59 +111,43 @@ def inspect_device_cls(device_cls, desired_cls, cache):
return class_space


def metadata_namespace(md, scope=None):
def tree_namespace(scope=None):
"""
Create a ``namespace`` that accumulates objects and creates a tree based on
their metadata.
Create a ``namespace`` that accumulates objects and creates a tree.
This tree is a nested set of `IterableNamespace` objects based on the
object names as defined in scope. We will split on underscores and use the
splits to create the tree.
Parameters
----------
md: ``list`` of ``str``
Each of the metadata categories to group objects by, in order from the
root of the tree to the leaves.
scope: ``module``, ``namespace``, or ``list`` of these
Every object attached to the given modules will be considered for the
`metadata_namespace`. If ``scope`` is omitted, we'll check all objects
`tree_namespace`. If ``scope`` is omitted, we'll check all objects
loaded by ``hutch-python`` and everything in the caller's global frame.
Returns
-------
namespace: `IterableNamespace`
"""
logger.debug('Create metadata_namespace md=%s, scope=%s', md, scope)
metadata_space = IterableNamespace()
logger.debug('Create tree_namespace scope=%s', scope)
tree_space = IterableNamespace()
scope_objs = extract_objs(scope=scope, stack_offset=1)

for name, obj in scope_objs.items():
# Collect obj metadata
if hasattr(obj, 'md'):
raw_keys = [getattr(obj.md, filt, None) for filt in md]
# Fallback: use_the_name
else:
if '_' not in name:
continue
name_keys = name.split('_')
raw_keys = name_keys[:len(md)]
# Abandon if no matches
if raw_keys[0] is None:
continue
# Force lowercase
keys = []
for key in raw_keys:
if isinstance(key, str):
keys.append(key.lower())
else:
keys.append(key)
# Add key to existing namespace branch, create new if needed
logger.debug('Add %s to metadata namespace', name)
upper_space = metadata_space
for key in keys:
if key is None:
break
name = strip_prefix(name, key)
if not hasattr(upper_space, key):
setattr(upper_space, key, IterableNamespace())
upper_space = getattr(upper_space, key)
setattr(upper_space, name, obj)
return metadata_space
logger.debug('Add %s to tree namespace', name)
upper_space = tree_space
keys = name.split('_')[:-1]

if keys:
# Add key to existing namespace branch, create new if needed
for key in keys:
name = strip_prefix(name, key)
# Force lowercase
key = key.lower()
if not hasattr(upper_space, key):
setattr(upper_space, key, IterableNamespace())
upper_space = getattr(upper_space, key)
setattr(upper_space, name, obj)
logger.debug('Created tree namespace %s', tree_space)
return tree_space
52 changes: 11 additions & 41 deletions hutch_python/tests/test_namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from ophyd.device import Device, Component
from ophyd.signal import Signal

from hutch_python.namespace import class_namespace, metadata_namespace
from hutch_python.namespace import class_namespace, tree_namespace


logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -51,46 +51,16 @@ def test_class_namespace_subdevices():
assert not hasattr(device_space, 'tree_oranges')


def test_metadata_namespace():
def test_tree_namespace():
logger.debug('test_metadata_namespace')
obj1 = SimpleNamespace()
obj2 = SimpleNamespace()
obj3 = SimpleNamespace()
obj4 = SimpleNamespace()
obj5 = SimpleNamespace()
obj6 = SimpleNamespace()
obj1.md = SimpleNamespace(beamline='MFX', stand='DIA')
obj2.md = SimpleNamespace(beamline='MFX', stand='DIA')
obj3.md = SimpleNamespace(beamline='MFX', stand='DG2')
obj4.md = SimpleNamespace(beamline='MFX')
obj5.md = SimpleNamespace(beamline='XPP', stand='SB2')
obj6.md = SimpleNamespace(beamline='MFX', stand='DIA')
scope = SimpleNamespace(mfx_dia_obj1=obj1, mfx_dia_obj2=obj2,
mfx_dg2_obj3=obj3, mfx_obj4=obj4,
xpp_sb2_obj5=obj5, hello=obj6)
md = ['beamline', 'stand']
namespaces = metadata_namespace(md, scope=scope)
scope = SimpleNamespace(mfx_dia_obj1=1, mfx_dia_obj2=2,
mfx_dg2_obj3=3, mfx_obj4=4,
xpp_sb2_obj5=5)
namespaces = tree_namespace(scope=scope)
mfx = namespaces.mfx
xpp = namespaces.xpp
assert mfx.dia.obj1 == obj1
assert mfx.dia.obj2 == obj2
assert mfx.dg2.obj3 == obj3
assert mfx.obj4 == obj4
assert xpp.sb2.obj5 == obj5
assert mfx.dia.hello == obj6


def test_metadata_namespace_fallback():
logger.debug('test_metadata_namespace_fallback')
# objects without md, so use name
obj1 = SimpleNamespace()
obj2 = SimpleNamespace()
obj3 = SimpleNamespace()
scope = SimpleNamespace(mfx_dia_obj1=obj1, mfx_dia_obj2=obj2,
mfx_dg2_obj3=obj3)
md = ['beamline', 'stand']
namespaces = metadata_namespace(md, scope=scope)
mfx = namespaces.mfx
assert mfx.dia.obj1 == obj1
assert mfx.dia.obj2 == obj2
assert mfx.dg2.obj3 == obj3
assert mfx.dia.obj1 == 1
assert mfx.dia.obj2 == 2
assert mfx.dg2.obj3 == 3
assert mfx.obj4 == 4
assert xpp.sb2.obj5 == 5

0 comments on commit 373aab1

Please sign in to comment.