180 lines
6.2 KiB
Python
180 lines
6.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
from __future__ import absolute_import
|
|
import sys
|
|
from textblob.compat import basestring, implements_to_string, PY2, binary_type
|
|
|
|
|
|
class ComparableMixin(object):
|
|
|
|
'''Implements rich operators for an object.'''
|
|
|
|
def _compare(self, other, method):
|
|
try:
|
|
return method(self._cmpkey(), other._cmpkey())
|
|
except (AttributeError, TypeError):
|
|
# _cmpkey not implemented, or return different type,
|
|
# so I can't compare with "other". Try the reverse comparison
|
|
return NotImplemented
|
|
|
|
def __lt__(self, other):
|
|
return self._compare(other, lambda s, o: s < o)
|
|
|
|
def __le__(self, other):
|
|
return self._compare(other, lambda s, o: s <= o)
|
|
|
|
def __eq__(self, other):
|
|
return self._compare(other, lambda s, o: s == o)
|
|
|
|
def __ge__(self, other):
|
|
return self._compare(other, lambda s, o: s >= o)
|
|
|
|
def __gt__(self, other):
|
|
return self._compare(other, lambda s, o: s > o)
|
|
|
|
def __ne__(self, other):
|
|
return self._compare(other, lambda s, o: s != o)
|
|
|
|
|
|
class BlobComparableMixin(ComparableMixin):
|
|
|
|
'''Allow blob objects to be comparable with both strings and blobs.'''
|
|
|
|
def _compare(self, other, method):
|
|
if isinstance(other, basestring):
|
|
# Just compare with the other string
|
|
return method(self._cmpkey(), other)
|
|
return super(BlobComparableMixin, self)._compare(other, method)
|
|
|
|
|
|
@implements_to_string
|
|
class StringlikeMixin(object):
|
|
|
|
'''Make blob objects behave like Python strings.
|
|
|
|
Expects that classes that use this mixin to have a _strkey() method that
|
|
returns the string to apply string methods to. Using _strkey() instead
|
|
of __str__ ensures consistent behavior between Python 2 and 3.
|
|
'''
|
|
|
|
def __repr__(self):
|
|
'''Returns a string representation for debugging.'''
|
|
class_name = self.__class__.__name__
|
|
text = self.__unicode__().encode("utf-8") if PY2 else str(self)
|
|
ret = '{cls}("{text}")'.format(cls=class_name,
|
|
text=text)
|
|
return binary_type(ret) if PY2 else ret
|
|
|
|
def __str__(self):
|
|
'''Returns a string representation used in print statements
|
|
or str(my_blob).'''
|
|
return self._strkey()
|
|
|
|
def __len__(self):
|
|
'''Returns the length of the raw text.'''
|
|
return len(self._strkey())
|
|
|
|
def __iter__(self):
|
|
'''Makes the object iterable as if it were a string,
|
|
iterating through the raw string's characters.
|
|
'''
|
|
return iter(self._strkey())
|
|
|
|
def __contains__(self, sub):
|
|
'''Implements the `in` keyword like a Python string.'''
|
|
return sub in self._strkey()
|
|
|
|
def __getitem__(self, index):
|
|
'''Returns a substring. If index is an integer, returns a Python
|
|
string of a single character. If a range is given, e.g. `blob[3:5]`,
|
|
a new instance of the class is returned.
|
|
'''
|
|
if isinstance(index, int):
|
|
return self._strkey()[index] # Just return a single character
|
|
else:
|
|
# Return a new blob object
|
|
return self.__class__(self._strkey()[index])
|
|
|
|
def find(self, sub, start=0, end=sys.maxsize):
|
|
'''Behaves like the built-in str.find() method. Returns an integer,
|
|
the index of the first occurrence of the substring argument sub in the
|
|
sub-string given by [start:end].
|
|
'''
|
|
return self._strkey().find(sub, start, end)
|
|
|
|
def rfind(self, sub, start=0, end=sys.maxsize):
|
|
'''Behaves like the built-in str.rfind() method. Returns an integer,
|
|
the index of he last (right-most) occurence of the substring argument
|
|
sub in the sub-sequence given by [start:end].
|
|
'''
|
|
return self._strkey().rfind(sub, start, end)
|
|
|
|
def index(self, sub, start=0, end=sys.maxsize):
|
|
'''Like blob.find() but raise ValueError when the substring
|
|
is not found.
|
|
'''
|
|
return self._strkey().index(sub, start, end)
|
|
|
|
def rindex(self, sub, start=0, end=sys.maxsize):
|
|
'''Like blob.rfind() but raise ValueError when substring is not
|
|
found.
|
|
'''
|
|
return self._strkey().rindex(sub, start, end)
|
|
|
|
def startswith(self, prefix, start=0, end=sys.maxsize):
|
|
"""Returns True if the blob starts with the given prefix."""
|
|
return self._strkey().startswith(prefix, start, end)
|
|
|
|
def endswith(self, suffix, start=0, end=sys.maxsize):
|
|
"""Returns True if the blob ends with the given suffix."""
|
|
return self._strkey().endswith(suffix, start, end)
|
|
|
|
# PEP8 aliases
|
|
starts_with = startswith
|
|
ends_with = endswith
|
|
|
|
def title(self):
|
|
"""Returns a blob object with the text in title-case."""
|
|
return self.__class__(self._strkey().title())
|
|
|
|
def format(self, *args, **kwargs):
|
|
"""Perform a string formatting operation, like the built-in
|
|
`str.format(*args, **kwargs)`. Returns a blob object.
|
|
"""
|
|
return self.__class__(self._strkey().format(*args, **kwargs))
|
|
|
|
def split(self, sep=None, maxsplit=sys.maxsize):
|
|
"""Behaves like the built-in str.split().
|
|
"""
|
|
return self._strkey().split(sep, maxsplit)
|
|
|
|
def strip(self, chars=None):
|
|
"""Behaves like the built-in str.strip([chars]) method. Returns
|
|
an object with leading and trailing whitespace removed.
|
|
"""
|
|
return self.__class__(self._strkey().strip(chars))
|
|
|
|
def upper(self):
|
|
"""Like str.upper(), returns new object with all upper-cased characters.
|
|
"""
|
|
return self.__class__(self._strkey().upper())
|
|
|
|
def lower(self):
|
|
"""Like str.lower(), returns new object with all lower-cased characters.
|
|
"""
|
|
return self.__class__(self._strkey().lower())
|
|
|
|
def join(self, iterable):
|
|
"""Behaves like the built-in `str.join(iterable)` method, except
|
|
returns a blob object.
|
|
|
|
Returns a blob which is the concatenation of the strings or blobs
|
|
in the iterable.
|
|
"""
|
|
return self.__class__(self._strkey().join(iterable))
|
|
|
|
def replace(self, old, new, count=sys.maxsize):
|
|
"""Return a new blob object with all the occurence of `old` replaced
|
|
by `new`.
|
|
"""
|
|
return self.__class__(self._strkey().replace(old, new, count))
|