intensional / intensional.py

from mementos import MementoMetaclass   # to memoize IntensionalSet
from stuf import orderedstuf            # for ReMatch
import re                               # for Re
import fnmatch                          # for Glob
import inspect                          # for en passant operation in Re

class IntensionalSet(object):
    """
    An intensional set (actually, an intensionally defined set) is a set that
    is defined by a definition, rather than an explicit listing of members
    (which would be an extensional set).
    
    Like other Python sets, IntensionalSet objects can include explicit
    items--but it also admits the possibility of rule-based membership.
    """

    __metaclass__ = MementoMetaclass
    
    
# Any = Union, Every = Intersection
# might want to complete more of the set functions like symmetric difference, discard, etc

# should this be reorganized into multiple files?

class ReMatch(orderedstuf):
    """
    An attributes-exposed regular expression match object. Ideally this would be
    a subclass of the re module's match object, but they are of type ``_sre.SRE_Match``
    and `appear to be unsubclassable
    <http://stackoverflow.com/questions/4835352/subclassing-matchobject-in-python>`_.
    Thus, ReMatch is a subclass of an attributes-exposed dict type, ``stuf``, into
    which the relevant data has been copied.
    """
    pass

    # in future, might want to do a pass-through for methods of _sre.SRE_Match
    # though they are currently available through x._match.method
        
def show_frames():
    f = inspect.currentframe().f_back # the caller
    while f:
        print f, f.f_lineno
        print "   f_locals:", ' '.join(f.f_locals.keys())
        f = f.f_back
    
class Re(IntensionalSet):
    
    ### need go back meahcnism

    def __init__(self, pattern, flags=0):
        self.pattern = pattern
        self.flags   = flags
        self.re = re.compile(pattern, flags)
        self.groups     = self.re.groups
        self.groupindex = self.re.groupindex
        
    def _regroup(self, m):
        """
        Given an _sre.SRE_Match object, create and return a corresponding
        ReMatch object. Also, set the en passant variable to it.
        """
        if not m:
            return m
        result = ReMatch()
        result['_match'] = m   # access to original match object
        result.update(m.groupdict())  # update named groups
        # update numerical / positional groups
        result[0] = m.group(0)
        for i, g in enumerate(m.groups(), start=1):
            result[i] = g
            
        # this method -> calling method -> MementosMetaclass.__call__ -> Re.__init__ -> caller
        inspect.currentframe().f_back.f_back.f_back.f_back.f_locals['_'] = result

        # do we want to make the variable for '_' settable?
        # do we want to wait for an attribute to be called to get it?
        return result
        
    def __contains__(self, item):
        # if not isinstance(item, basestring):
        #     item = str(item)
        return self._regroup(self.re.search(item))
    
    # methods that return ReMatch objects
    
    def search(self, *args, **kwargs):
        return self._regroup(self.re.search(*args, **kwargs))

    def match(self, *args, **kwargs):
        return self._regroup(self.re.match(*args, **kwargs))
    
    # methods that don't need ReMatch objects
    
    def finditer(self, *args, **kwargs):
        return self.re.finditer(*args, **kwargs)
      
    def findall(self, *args, **kwargs):
        return self.re.findall(*args, **kwargs)
    
    def split(self, *args, **kwargs):
        return self.re.split(*args, **kwargs)
    
    def sub(self, *args, **kwargs):
        return self.re.sub(*args, **kwargs)
    
    def subn(self, *args, **kwargs):
        return self.re.subn(*args, **kwargs)
    
    def escape(self, *args, **kwargs):
        return self.re.escape(*args, **kwargs)

class Glob(IntensionalSet):
        
    def __init__(self, expr):
        self.expr = expr
        
    def __contains__(self, item):
        return fnmatch.fnmatch(item, self.expr)
    
    
class Any(IntensionalSet):
    """
    An item is in an Any if it is or is in any member of the set.
    """
    
    def __init__(self, *args):
        self.items = set(args)
        
    def __contains__(self, item):
        if item in self.items:
            return True
        
        for i in self.items:
            if hasattr(i, '__contains__'):
                if item in i:
                    return True
            else:
                if item == i:
                    return True
        return False
    
class Every(IntensionalSet):
    """
    An item is in an Every if it is or is in every member of the set.
    """
    
    def __init__(self, *args):
        self.items = set(args)

    def __contains__(self, item):
        for i in self.items:
            if hasattr(i, '__contains__'):
                if item not in i:
                    return False
            else:
                if item != i:
                    return False
        return True

class ButNot(IntensionalSet):
    """
    An item is in a ButNot if it's in the primary set and not the exclusion.
    """
    
    def __init__(self, items, exclusion):
        self.items = items
        self.exclusion = exclusion

    def __contains__(self, item):
        if item == self.items or item in self.items:
            if item != self.exclusion and item not in self.exclusion:
                return True
        return False

class Test(IntensionalSet):
    
    def __init__(self, expr, *args, **kwargs):
        IntensionalSet.__init__(self)
        self.args = args
        self.kwargs = kwargs
        self.expr = expr
        if isinstance(expr, basestring):
            if not expr.startswith('lambda'):
                expr = 'lambda x: ' + expr
            self.func = eval(expr)
        elif hasattr(expr, '__call__'):
            self.func = expr
        else:
            raise ValueError('expr needs to be string or callable')
        
    def __contains__(self, item):
        try:
            return self.func(item, *self.args, **self.kwargs)
        except StandardError:
            return False
        
        # NB failure to run test => fails test
        # might silently hide syntax errors and such
        # do we want a mode or mechanism to make such errors into Warnings?
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.