Skip to content

Commit

Permalink
Added pandas-style loc semantics for directly accessing subtree.
Browse files Browse the repository at this point in the history
We could already access subtrees directly from Trees/Treants, but
besides using something like `View.glob` there wasn't a direct way of
doing the same thing from a `View` or `Bundle`. Now there is.
  • Loading branch information
dotsdl committed Jul 11, 2016
1 parent 5dbd8a9 commit dfc1d45
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 14 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ Enhancements

* ``Tree.rsync`` method added that allows syncing of Trees
and Treants to and from remote locations and filesystems
* ``View.loc``, ``Bundle.loc``, ``Tree.loc`` pandas-like semantics
added for accessing subtrees.


Fixes
Expand Down
79 changes: 65 additions & 14 deletions src/datreant/core/collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,56 @@ def __lt__(self, other):
except AttributeError:
return NotImplemented

def glob(self, pattern):
"""Return a View of all child Leaves and Trees of members matching
given globbing pattern.
Parameters
----------
pattern : string
globbing pattern to match files and directories with
"""
return View([member.glob(pattern) for member in self
if isinstance(member, Tree)], limbs=self.limbs)

def draw(self, depth=None, hidden=False):
"""Print an ASCII-fied visual of all member Trees.
Parameters
----------
depth : int
Maximum directory depth to display. ``None`` indicates no limit.
hidden : bool
If False, do not show hidden files; hidden directories are still
shown if they contain non-hidden files or directories.
"""
for member in self:
if isinstance(member, Tree):
member.draw(depth=depth, hidden=hidden)

@property
def loc(self):
"""Get a View giving Tree/Leaf at `path` relative to each Tree in
collection.
Use with getitem syntax, e.g. ``.loc['some name']``
Allowed inputs are:
- A single name
- A list or array of names
If directory/file does not exist at the given path, then whether a Tree
or Leaf is given is determined by the path semantics, i.e. a trailing
separator ("/").
"""
if not hasattr(self, "_loc"):
self._loc = _Loc(self)

return self._loc

@property
def limbs(self):
"""A set giving the names of this collection's attached limbs.
Expand Down Expand Up @@ -391,7 +441,7 @@ def relpaths(self):

@property
def bundle(self):
"""A Bundle of all existing Treants among the Trees and Leaves
"""Obtain a Bundle of all existing Treants among the Trees and Leaves
in this View.
"""
Expand Down Expand Up @@ -452,19 +502,6 @@ def map(self, function, processes=1, **kwargs):

return results

def glob(self, pattern):
"""Return a View of all child Leaves and Trees of members matching
given globbing pattern.
Parameters
----------
pattern : string
globbing pattern to match files and directories with
"""
return View([member.glob(pattern) for member in self
if isinstance(member, Tree)], limbs=self.limbs)

def globfilter(self, pattern):
"""Return a View of members that match by name the given globbing
pattern.
Expand Down Expand Up @@ -1221,3 +1258,17 @@ def _get_members_treanttype(self):
list giving treanttype of each member, in order
"""
return [member['treanttype'] for member in self._state]


class _Loc(object):
"""Subtree accessor for collections."""

def __init__(self, collection):
self._collection = collection

def __getitem__(self, path):
"""Get Tree/Leaf at `path` relative to each Tree in collection.
"""
return View([t[path] for t in self._collection
if isinstance(t, Tree)], limbs=self._collection.limbs)
37 changes: 37 additions & 0 deletions src/datreant/core/trees.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,30 @@ def attach(self, *limbname):
else:
self._attach_limb(limb)

@property
def loc(self):
"""Get Tree/Leaf at `path` relative to Tree.
Use with getitem syntax, e.g. ``.loc['some name']``
Allowed inputs are:
- A single name
- A list or array of names
If directory/file does not exist at the given path, then whether a Tree
or Leaf is given is determined by the path semantics, i.e. a trailing
separator ("/").
Using e.g. ``Tree.loc['some name']`` is equivalent to doing
``Tree['some name']``. ``.loc`` is included for parity with ``View``
and ``Bundle`` API semantics.
"""
if not hasattr(self, "_loc"):
self._loc = _Loc(self)

return self._loc

@property
def abspath(self):
"""Absolute path of ``self.path``.
Expand Down Expand Up @@ -503,3 +527,16 @@ def sync(self, other, mode='upload', compress=True, checksum=True,
dry=dry, include=include, checksum=checksum,
overwrite=overwrite, exclude=exclude,
rsync_path=rsync_path)


class _Loc(object):
"""Subtree accessor for Trees."""

def __init__(self, tree):
self._tree = tree

def __getitem__(self, path):
"""Get Tree/Leaf at `path` relative to attached Tree.
"""
return self._tree[path]

0 comments on commit dfc1d45

Please sign in to comment.