<commit_before>def or_(a, b):
    if not isinstance(a, Filter) or not isinstance(b, Filter):
        raise TypeError
    return a.__or__(b)

def and_(a, b):
    return a.__and__(b)

class Filter:
    def __init__(self, dct):
        self.dct = dct

    def __or__(self, other):
        if not isinstance(other, Filter):
            raise TypeError("You can only or Filter types!")
        if '+or' in self.dct:
            return Filter({ '+or': self.dct['+or'] + [ other.dct ] })
        else:
            return Filter({ '+or': [self.dct, other.dct] })

    def __and__(self, other):
        if not isinstance(other, Filter):
            raise TypeErrand("You can only and Filter types!")
        if '+and' in self.dct:
            return Filter({ '+and': self.dct['+and'] + [ other.dct ] })
        else:
            return Filter({ '+and': [self.dct, other.dct] })

class FilterableAttribute:
    def __init__(self, name):
        self.name = name

    def __eq__(self, other):
        return Filter({ self.name: other })

    def __ne__(self, other):
        return Filter({ self.name: { "+ne": other } })

    # "in" evaluates the return value - have to use 
    # type.contains instead
    def contains(self, other):
        return Filter({ self.name: { "+contains": other } })

    def __gt__(self, other):
        return Filter({ self.name: { "+gt": other } })

    def __lt__(self, other):
        return Filter({ self.name: { "+lt": other } })

    def __ge__(self, other):
        return Filter({ self.name: { "+gte": other } })

    def __le__(self, other):
        return Filter({ self.name: { "+lte": other } })

class FilterableMetaclass(type):
    def __init__(cls, name, bases, dct):
        if hasattr(cls, 'properties'):
            for key in cls.properties.keys():
                if cls.properties[key].filterable:
                    setattr(cls, key, FilterableAttribute(key))

        super(FilterableMetaclass, cls).__init__(name, bases, dct)<commit_msg>Better errors when trying to filter on unfilterable properties<commit_after>def or_(a, b):
    if not isinstance(a, Filter) or not isinstance(b, Filter):
        raise TypeError
    return a.__or__(b)

def and_(a, b):
    return a.__and__(b)

class Filter:
    def __init__(self, dct):
        self.dct = dct

    def __or__(self, other):
        if not isinstance(other, Filter):
            raise TypeError("You can only or Filter types!")
        if '+or' in self.dct:
            return Filter({ '+or': self.dct['+or'] + [ other.dct ] })
        else:
            return Filter({ '+or': [self.dct, other.dct] })

    def __and__(self, other):
        if not isinstance(other, Filter):
            raise TypeErrand("You can only and Filter types!")
        if '+and' in self.dct:
            return Filter({ '+and': self.dct['+and'] + [ other.dct ] })
        else:
            return Filter({ '+and': [self.dct, other.dct] })

class FilterableAttribute:
    def __init__(self, name):
        self.name = name

    def __eq__(self, other):
        return Filter({ self.name: other })

    def __ne__(self, other):
        return Filter({ self.name: { "+ne": other } })

    # "in" evaluates the return value - have to use 
    # type.contains instead
    def contains(self, other):
        return Filter({ self.name: { "+contains": other } })

    def __gt__(self, other):
        return Filter({ self.name: { "+gt": other } })

    def __lt__(self, other):
        return Filter({ self.name: { "+lt": other } })

    def __ge__(self, other):
        return Filter({ self.name: { "+gte": other } })

    def __le__(self, other):
        return Filter({ self.name: { "+lte": other } })

class NonFilterableAttribute:
    """This class is used to provide better error messages should a user attempt
    to filter an object on an attribute that is defined in properties, but is
    not filterable (otherwise they'd see "{} has no attribute {}" which is less
    obvious
    """
    def __init__(self, clsname, atrname):
        self.clsname = clsname
        self.atrname = atrname

    def __eq__(self, other):
        raise AttributeError("{} cannot be filtered by {}".format(self.clsname, self.atrname))

    def __ne__(self, other):
        raise AttributeError("{} cannot be filtered by {}".format(self.clsname, self.atrname))

    def contains(self, other):
        raise AttributeError("{} cannot be filtered by {}".format(self.clsname, self.atrname))

    def __gt__(self, other):
        raise AttributeError("{} cannot be filtered by {}".format(self.clsname, self.atrname))

    def __lt__(self, other):
        raise AttributeError("{} cannot be filtered by {}".format(self.clsname, self.atrname))

    def __ge__(self, other):
        raise AttributeError("{} cannot be filtered by {}".format(self.clsname, self.atrname))

    def __le__(self, other):
        raise AttributeError("{} cannot be filtered by {}".format(self.clsname, self.atrname))

class FilterableMetaclass(type):
    def __init__(cls, name, bases, dct):
        if hasattr(cls, 'properties'):
            for key in cls.properties.keys():
                if cls.properties[key].filterable:
                    setattr(cls, key, FilterableAttribute(key))
                else:
                    setattr(cls, key, NonFilterableAttribute(cls.__name__, key))

        super(FilterableMetaclass, cls).__init__(name, bases, dct)
{'fraction_non_alphanumeric': 0.1370473537604457, 'fraction_numerical': 0.0, 'mean_word_length': 2.5598149372108394, 'pattern_counts': {'":': 12, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '330792', 'n_tokens_mistral': 1626, 'n_tokens_neox': 1593, 'n_words': 505}