Source

simplere / simplere / core.py

"""
A simpler way to access and use regular expressions. As a bonus,
also simpler access to globs.
"""

from mementos import MementoMetaclass, with_metaclass    
import sys, re, fnmatch

_PY3 = sys.version_info[0] > 2
if _PY3:
    basestring = str

class ReMatch(object):
    """
    An easier-to-use proxy for regular expression match objects. Ideally this
    would be a subclass of the re module's match object, but their type
    ``_sre.SRE_Match`` appears to be unsubclassable
    <http://stackoverflow.com/questions/4835352/subclassing-matchobject-in-python>`_.
    Thus, ReMatch is a proxy exposes the match object's numeric (positional) and
    named groups through indices and attributes. If a named group has the same
    name as a match object method or property, it takes precedence. Either
    change the name of the match group or access the underlying property thus:
    ``x._match.property``
    """
     
    def __init__(self, match):
        self._match = match
        self._groupdict = match.groupdict()
        
    def __getattr__(self, name):
        if name in self.__dict__:
            return self.__dict__[name]
        if name in self._groupdict:
            return self._groupdict[name]
        try:
            return getattr(self._match, name)
        except AttributeError:
            return AttributeError("no such attribute '{}'".format(name))

    def __getitem__(self, index):
        return self._match.group(index)

class Re(with_metaclass(MementoMetaclass, object)):

    # convenience copy of re flag constants
    
    DEBUG      = re.DEBUG
    I          = re.I
    IGNORECASE = re.IGNORECASE
    L          = re.L
    LOCALE     = re.LOCALE
    M          = re.M
    MULTILINE  = re.MULTILINE
    S          = re.S
    DOTALL     = re.DOTALL
    U          = re.U
    UNICODE    = re.UNICODE
    X          = re.X
    VERBOSE    = re.VERBOSE
    
    _ = None

    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.
        """
        result = ReMatch(m) if m else m
        Re._ = result
        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))
    
    def finditer(self, *args, **kwargs):
        for m in self.re.finditer(*args, **kwargs):
            yield self._regroup(m)
    
    ### methods that don't need ReMatch objects
      
    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(with_metaclass(MementoMetaclass, object)):
    """
    An item matches a Glob via Unix filesystem glob semantics.
    
    E.g. 'alpha' matches 'a*' and 'a????' but not 'b*'
    """

    def __init__(self, pattern):
        self.pattern = pattern
        
    def __contains__(self, item):
        return fnmatch.fnmatch(str(item), self.pattern)