Skip to content

Commit

Permalink
Bundles and Views obtained from other Bundles, Views, Trees inherit l…
Browse files Browse the repository at this point in the history
…imbs

Set operations between Bundles/Views will give Bundles/Views with unions
of the operands' attached limbs
  • Loading branch information
dotsdl committed Mar 27, 2016
1 parent ee1a548 commit 39d457f
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 71 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ Enhancements


Fixes

* Bundles and Views obtained from other Bundles, Views, Trees, or Treants
automatically get all limbs of the object they were obtained from;
set operations between Views/Bundles will give unions of their attached
limbs


Changes
Expand Down
126 changes: 75 additions & 51 deletions src/datreant/core/collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ class View(CollectionMixin):
Trees and/or Leaves to be added, which may be nested lists of Trees
and Leaves. Trees and Leaves can be given as either objects or
paths.
limbs : list or set
Names of limbs to immediately attach.
"""
_classagglimbs = set()
Expand All @@ -75,6 +77,13 @@ def __init__(self, *vegs, **kwargs):
self._state = list()
self.add(*vegs)

# attach any limbs given
for agglimb in kwargs.pop('limbs', []):
try:
self.attach(agglimb)
except KeyError:
pass

def __repr__(self):
return "<View({})>".format(self._list())

Expand All @@ -97,22 +106,25 @@ def __getitem__(self, index):
all([isinstance(item, bool) for item in index])):
# boolean indexing
memberlist = self._list()
out = View([memberlist[i] for i, val in enumerate(index) if val])
out = View([memberlist[i] for i, val in enumerate(index) if val],
limbs=self.limbs)
elif isinstance(index, list):
memberlist = self._list()
out = View([memberlist[item] for item in index])
out = View([memberlist[item] for item in index],
limbs=self.limbs)
elif isinstance(index, int):
# an index gets the member at that position
out = self._list()[index]
elif isinstance(index, string_types):
# a name can be used for indexing
# always returns a View
out = View([self._list()[i] for i, name
in enumerate(self.names) if name == index])
in enumerate(self.names) if name == index],
limbs=self.limbs)

elif isinstance(index, slice):
# we also take slices, obviously
out = View(*self._list()[index])
out = View(*self._list()[index], limbs=self.limbs)
else:
raise IndexError("Cannot index View with given values")

Expand All @@ -132,7 +144,8 @@ def __add__(self, other):
"""
if isinstance(other, (Tree, Leaf, View, list)):
return View(self, other)
limbs = self.limbs | other.limbs
return View(self, other, limbs=limbs)
else:
raise TypeError("Right operand must be a Tree, Leaf, or View.")

Expand All @@ -143,9 +156,11 @@ def __sub__(self, other):
"""
if isinstance(other, View):
return View(list(set(self) - set(other)))
limbs = self.limbs | other.limbs
return View(list(set(self) - set(other)), limbs=limbs)
elif isinstance(other, (Tree, Leaf)):
return View(list(set(self) - set([other])))
limbs = self.limbs | other.limbs
return View(list(set(self) - set([other])), limbs=limbs)
else:
raise TypeError("Right operand must be a Tree, Leaf, or View.")

Expand All @@ -154,7 +169,8 @@ def __or__(self, other):
"""
if isinstance(other, View):
return View(self, other)
limbs = self.limbs | other.limbs
return View(self, other, limbs=limbs)
else:
raise TypeError("Operands must be Views.")

Expand All @@ -163,7 +179,8 @@ def __and__(self, other):
"""
if isinstance(other, View):
return View(list(set(self) & set(other)))
limbs = self.limbs | other.limbs
return View(list(set(self) & set(other)), limbs=limbs)
else:
raise TypeError("Operands must be Views.")

Expand All @@ -173,7 +190,8 @@ def __xor__(self, other):
"""
if isinstance(other, View):
return View(list(set(self) ^ set(other)))
limbs = self.limbs | other.limbs
return View(list(set(self) ^ set(other)), limbs=limbs)
else:
raise TypeError("Operands must be Views.")

Expand Down Expand Up @@ -309,21 +327,24 @@ def membertrees(self):
"""A View giving only members that are Trees (or subclasses).
"""
return View([member for member in self if isinstance(member, Tree)])
return View([member for member in self if isinstance(member, Tree)],
limbs=self.limbs)

@property
def memberleaves(self):
"""A View giving only members that are Leaves (or subclasses).
"""
return View([member for member in self if isinstance(member, Leaf)])
return View([member for member in self if isinstance(member, Leaf)],
limbs=self.limbs)

@property
def children(self):
"""A View of all children within the member Trees.
"""
return View([member.children for member in self.membertrees])
return View([member.children for member in self.membertrees],
limbs=self.limbs)

@property
def trees(self):
Expand All @@ -332,7 +353,8 @@ def trees(self):
Hidden directories are not included.
"""
return View([member.trees for member in self.membertrees])
return View([member.trees for member in self.membertrees],
limbs=self.limbs)

@property
def leaves(self):
Expand All @@ -341,14 +363,16 @@ def leaves(self):
Hidden files are not included.
"""
return View([member.leaves for member in self.membertrees])
return View([member.leaves for member in self.membertrees],
limbs=self.limbs)

@property
def hidden(self):
"""A View of the hidden files and directories within the member Trees.
"""
return View([member.hidden for member in self.membertrees])
return View([member.hidden for member in self.membertrees],
limbs=self.limbs)

@property
def abspaths(self):
Expand All @@ -371,17 +395,7 @@ def bundle(self):
in this View.
"""
b = Bundle(self)

# try and attach all the limbs this Bundle has
for agglimb in self.limbs:
try:
b.attach(agglimb)
except KeyError:
pass

return b

return Bundle(self, limbs=self.limbs)

@property
def exists(self):
Expand Down Expand Up @@ -449,7 +463,7 @@ def glob(self, pattern):
"""
return View([member.glob(pattern) for member in self
if isinstance(member, Tree)])
if isinstance(member, Tree)], limbs=self.limbs)

def globfilter(self, pattern):
"""Return a View of members that match by name the given globbing
Expand All @@ -462,7 +476,7 @@ def globfilter(self, pattern):
"""
return View([self[name] for name in
fnmatch.filter(self.names, pattern)])
fnmatch.filter(self.names, pattern)], limbs=self.limbs)

def make(self):
"""Make the Trees and Leaves in this View if they don't already exist.
Expand All @@ -489,6 +503,9 @@ class Bundle(CollectionMixin):
can be given as either objects or paths to directories that contain
Treant statefiles. Glob patterns are also allowed, and all found
Treants will be added to the collection.
limbs : list or set
Names of limbs to immediately attach.
"""
_memberpaths = ['abspath']
_fields = ['uuid', 'treanttype']
Expand All @@ -504,6 +521,13 @@ def __init__(self, *treants, **kwargs):

self.add(*treants)

# attach any limbs given
for agglimb in kwargs.pop('limbs', []):
try:
self.attach(agglimb)
except KeyError:
pass

def __repr__(self):
return "<Bundle({})>".format(self._list())

Expand Down Expand Up @@ -535,18 +559,21 @@ def __getitem__(self, index):
all([isinstance(item, bool) for item in index])):
# boolean indexing
memberlist = self._list()
out = Bundle([memberlist[i] for i, val in enumerate(index) if val])
out = Bundle([memberlist[i] for i, val in enumerate(index) if val],
limbs=self.limbs)
elif isinstance(index, list):
memberlist = self._list()
out = Bundle([memberlist[item] for item in index])
out = Bundle([memberlist[item] for item in index],
limbs=self.limbs)
elif isinstance(index, int):
# an index gets the member at that position
out = self._list()[index]
elif isinstance(index, string_types):
# a name or uuid can be used for indexing
# a name always returns a Bundle
out = Bundle([self.filepaths[i] for i, name
in enumerate(self.names) if name == index])
in enumerate(self.names) if name == index],
limbs=self.limbs)

# if no names match, we try uuids
if not len(out):
Expand All @@ -558,7 +585,7 @@ def __getitem__(self, index):
out = out[0]
elif isinstance(index, slice):
# we also take slices, obviously
out = Bundle(*self.filepaths[index])
out = Bundle(*self.filepaths[index], limbs=self.limbs)
out._cache.update(self._cache)
else:
raise IndexError("Cannot index Bundle with given values")
Expand All @@ -572,7 +599,8 @@ def __add__(self, other):
from .treants import Treant

if isinstance(other, (Treant, Bundle, list)):
return Bundle(self, other)
limbs = self.limbs | other.limbs
return Bundle(self, other, limbs=limbs)
else:
raise TypeError("Operands must be Treant-derived or Bundles.")

Expand All @@ -585,9 +613,11 @@ def __sub__(self, other):
from .treants import Treant

if isinstance(other, Bundle):
return Bundle(list(set(self) - set(other)))
limbs = self.limbs | other.limbs
return Bundle(list(set(self) - set(other)), limbs=limbs)
elif isinstance(other, Treant):
return Bundle(list(set(self) - set([other])))
limbs = self.limbs | other.limbs
return Bundle(list(set(self) - set([other])), limbs=limbs)
else:
raise TypeError("Operands must be Treant-derived or Bundles.")

Expand All @@ -596,7 +626,8 @@ def __or__(self, other):
"""
if isinstance(other, Bundle):
return Bundle(self, other)
limbs = self.limbs | other.limbs
return Bundle(self, other, limbs=limbs)
else:
raise TypeError("Operands must be Bundles.")

Expand All @@ -605,7 +636,8 @@ def __and__(self, other):
"""
if isinstance(other, Bundle):
return Bundle(list(set(self) & set(other)))
limbs = self.limbs | other.limbs
return Bundle(list(set(self) & set(other)), limbs=limbs)
else:
raise TypeError("Operands must be Bundles.")

Expand All @@ -615,7 +647,8 @@ def __xor__(self, other):
"""
if isinstance(other, Bundle):
return Bundle(list(set(self) ^ set(other)))
limbs = self.limbs | other.limbs
return Bundle(list(set(self) ^ set(other)), limbs=limbs)
else:
raise TypeError("Operands must be Bundles.")

Expand Down Expand Up @@ -1013,7 +1046,7 @@ def flatten(self, exclude=None):

guuids = list(exclude)
memberlist = self._list()
flattened = Bundle()
flattened = Bundle(limbs=self.limbs)

for member in memberlist:
if hasattr(member, 'members') and member.uuid not in exclude:
Expand All @@ -1029,16 +1062,7 @@ def view(self):
"""Obtain a View giving the Tree for each Treant in this Bundle.
"""
v = View([member.tree for member in self])

# try and attach all the limbs this Bundle has
for agglimb in self.limbs:
try:
v.attach(agglimb)
except KeyError:
pass

return v
return View([member.tree for member in self], limbs=self.limbs)

def globfilter(self, pattern):
"""Return a Bundle of members that match by name the given globbing
Expand All @@ -1051,7 +1075,7 @@ def globfilter(self, pattern):
"""
return Bundle([self[name] for name in
fnmatch.filter(self.names, pattern)])
fnmatch.filter(self.names, pattern)], limbs=self.limbs)

def _add_members(self, uuids, treanttypes, abspaths):
"""Add many members at once.
Expand Down
7 changes: 4 additions & 3 deletions src/datreant/core/treants.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,13 @@ def __lt__(self, other):
except AttributeError:
return NotImplemented

def __add__(a, b):
def __add__(self, other):
"""Addition of treants with collections or treants yields Bundle.
"""
if isinstance(b, (Treant, Bundle)):
return Bundle(a, b)
if isinstance(other, (Treant, Bundle)):
limbs = self.limbs | other.limbs
return Bundle(self, other, limbs=limbs)
else:
raise TypeError("Operands must be Treants or Bundles.")

Expand Down

0 comments on commit 39d457f

Please sign in to comment.