commit 2c4595e3278c4b4b4175c1dbf6d00a5b02fc65f2 Author: Mahmoud Hashemi Date: Tue Feb 19 18:04:47 2013 -0800 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a4fe3f1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +*.py[cod] + +# emacs +*~ +._* +.\#* +\#*\# + +# C extensions +*.so + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg +lib +lib64 + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox +nosetests.xml + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject diff --git a/README.md b/README.md new file mode 100644 index 0000000..74d9a2a --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# Boltons + +Like builtins, but boltons. Stuff that should probably be in the +standard library, frankly speaking. diff --git a/compat.py b/compat.py new file mode 100644 index 0000000..7deb84e --- /dev/null +++ b/compat.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +import sys + +IS_PY2 = sys.version_info[0] == 2 +IS_PY3 = sys.version_info[0] == 3 + + +if IS_PY2: + from StringIO import StringIO + unicode, str, bytes, basestring = unicode, str, str, basestring +elif IS_PY3: + from io import StringIO + unicode, str, bytes, basestring = str, bytes, bytes, str +else: + raise NotImplementedError('welcome to the future, I guess. (report this)') diff --git a/funcutils.py b/funcutils.py new file mode 100644 index 0000000..653b4e7 --- /dev/null +++ b/funcutils.py @@ -0,0 +1,78 @@ +from functools import partial +from types import MethodType + +from itertools import chain + + +def mro_items(obj_type): + # handle slots? + return chain.from_iterable([ct.__dict__.iteritems() + for ct in obj_type.__mro__]) + + +def dir_dict(obj): + # separate function for handling descriptors on types? + ret = {} + for k in dir(obj): + ret[k] = getattr(obj, k) + return ret + + +class InstancePartial(partial): + def __get__(self, obj, obj_type): + return MethodType(self, obj, obj_type) + + +class CachedInstancePartial(partial): + def __init__(self, func, *a, **kw): + self.__name__ = None + self.__doc__ = func.__doc__ + self.__module__ = func.__module__ + + def __get__(self, obj, obj_type): + name = self.__name__ + if name is None: + for k, v in mro_items(obj_type): + if v is self: + self.__name__ = name = k + if obj is None: + return MethodType(self, obj, obj_type) + try: + # since this is a data descriptor, this block + # is probably only hit once (per object) + return obj.__dict__[name] + except KeyError: + obj.__dict__[name] = ret = MethodType(self, obj, obj_type) + return ret + + +class Greeter(object): + def __init__(self, greeting): + self.greeting = greeting + + def greet(self, excitement='.'): + return self.greeting.capitalize() + excitement + + partial_greet = InstancePartial(greet, excitement='!') + cached_partial_greet = CachedInstancePartial(greet, excitement='...') + + def native_greet(self): + return self.greet(';') + + +class SubGreeter(Greeter): + pass + + +def main(): + g = SubGreeter('hello') + print g.greet() + print g.native_greet() + print g.partial_greet() + print g.cached_partial_greet() + print CachedInstancePartial(g.greet, excitement='s')() + import pdb;pdb.set_trace() + + +if __name__ == '__main__': + main() diff --git a/iterutils.py b/iterutils.py new file mode 100644 index 0000000..22e3cc5 --- /dev/null +++ b/iterutils.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- + + +def is_scalar(obj): + return not hasattr(obj, '__iter__') or isinstance(obj, basestring) + + +def split(src, key=None, maxsplit=None): + """ + Splits an iterable based on a separator, iterable of separators, or + function that evaluates to True when a separator is encountered. + + Frankly, this feature should be part of the list builtin. + + TODO: This works with iterators but could itself be an generator. + """ + if maxsplit is not None: + maxsplit = int(maxsplit) + if maxsplit == 0: + return [src] + + if callable(key): + key_func = key + elif not is_scalar(key): + key = set(key) + key_func = lambda x: x in key + else: + key_func = lambda x: x == key + + ret = [] + cur_list = [] + for s in src: + if key_func(s): + ret.append(cur_list) + cur_list = [] + if maxsplit is not None and len(ret) >= maxsplit: + key_func = lambda x: False + else: + cur_list.append(s) + ret.append(cur_list) + + if key is None: + # If sep is none, str.split() "groups" separators + # check the str.split() docs for more info + return [x for x in ret if x] + else: + return ret