Permalink
Browse files

initial commit

  • Loading branch information...
0 parents commit ff2ad0c5958bbff0b914e2b74098940b95ee4504 @rfk committed May 28, 2011
Showing with 268 additions and 0 deletions.
  1. +10 −0 .gitignore
  2. +5 −0 ChangeLog.txt
  3. +19 −0 LICENSE.txt
  4. +5 −0 MANIFEST.in
  5. +31 −0 README.rst
  6. +95 −0 autosuper/__init__.py
  7. +62 −0 autosuper/tests.py
  8. +41 −0 setup.py
10 .gitignore
@@ -0,0 +1,10 @@
+*.pyc
+*.pyo
+*~
+*.swp
+build/
+dist/
+.coverage
+cover
+.tox
+MANIFEST
5 ChangeLog.txt
@@ -0,0 +1,5 @@
+
+v0.1.0:
+
+ * initial release; you might say *everything* has changed.
+
19 LICENSE.txt
@@ -0,0 +1,19 @@
+Copyright (c) 2011 Ryan Kelly
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
5 MANIFEST.in
@@ -0,0 +1,5 @@
+
+include README.rst
+include LICENSE.txt
+include ChangeLog.txt
+
31 README.rst
@@ -0,0 +1,31 @@
+
+
+autosuper: backport the magical zero-argument super() to python2
+=================================================================
+
+This is an (awful, hacky, wtf-were-you-thinking) attempt to port the magical
+zero-argument super() call from python3 to python2.
+
+In standard python2 usage of the super() builtin, you have to repeat both the
+class and instance objects when you call super, like this:
+
+ class Hello(Base):
+ def hello(self):
+ super(Hello,self).hello()
+
+Using autosuper, you can get the friendlier behaviour from python3 where it
+just figures out the correct call at runtime:
+
+ class Hello(Base):
+ def hello(self):
+ super().hello()
+
+Of course, you can still explicitly pass in the arguments if you want to do
+something strange. Sometimes you really do want that, e.g. to skip over
+some classes in the method resolution order.
+
+How does it work? By inspecting the calling frame to determine the function
+object being executed and the object on which it's being called, and then
+walking the object's __mro__ chain to find out where that function was
+defined. Yuck, but it seems to work...
+
95 autosuper/__init__.py
@@ -0,0 +1,95 @@
+"""
+
+autosuper: backport the magical zero-argument super() to python2
+=================================================================
+
+This is an (awful, hacky, wtf-were-you-thinking) attempt to port the magical
+zero-argument super() call from python3 to python2.
+
+In standard python2 usage of the super() builtin, you have to repeat both the
+class and instance objects when you call super, like this:
+
+ class Hello(Base):
+ def hello(self):
+ super(Hello,self).hello()
+
+Using autosuper, you can get the friendlier behaviour from python3 where it
+just figures out the correct call at runtime:
+
+ class Hello(Base):
+ def hello(self):
+ super().hello()
+
+Of course, you can still explicitly pass in the arguments if you want to do
+something strange. Sometimes you really do want that, e.g. to skip over
+some classes in the method resolution order.
+
+How does it work? By inspecting the calling frame to determine the function
+object being executed and the object on which it's being called, and then
+walking the object's __mro__ chain to find out where that function was
+defined. Yuck, but it seems to work...
+
+"""
+
+
+__ver_major__ = 0
+__ver_minor__ = 1
+__ver_patch__ = 0
+__ver_sub__ = ""
+__version__ = "%d.%d.%d%s" % (__ver_major__,__ver_minor__,__ver_patch__,__ver_sub__)
+
+
+import sys
+import __builtin__
+
+_builtin_super = __builtin__.super
+
+_sentinel = object()
+
+def _auto_super(typ=_sentinel,type_or_obj=_sentinel):
+ """Like buildin super(), but capable of magic.
+
+ This acts just like the builtin super() function, but if you don't give
+ it any arguments then it tries to infer them at runtime.
+ """
+ # Infer the correct call if used without arguments.
+ if typ is _sentinel:
+ # We'll need to do some frame hacking.
+ f = sys._getframe(1)
+ # Get the first positional argument of the function.
+ try:
+ type_or_obj = f.f_locals[f.f_code.co_varnames[0]]
+ except (IndexError,KeyError,):
+ raise RuntimeError("super() used in a function with no args")
+ # Get the MRO so we can crawl it.
+ try:
+ mro = type_or_obj.__mro__
+ except AttributeError:
+ try:
+ mro = type_or_obj.__class__.__mro__
+ except AttributeError:
+ raise RuntimeError("super() used with a non-newstyle-class")
+ # Now, find the class owning the currently-executing method.
+ for typ in mro:
+ for meth in typ.__dict__.itervalues():
+ if not isinstance(meth,type(_auto_super)):
+ continue
+ if meth.func_code is f.f_code:
+ # Aha! Found you.
+ break
+ else:
+ # Not found, move on to the next class in the MRO.
+ continue
+ # Found, break out of the search loop.
+ break
+ else:
+ raise RuntimeError("super() called outside a method")
+ # Now just dispatch to builtin super.
+ if type_or_obj is not _sentinel:
+ return _builtin_super(typ,type_or_obj)
+ return _builtin_super(typ)
+
+
+__builtin__.super = _auto_super
+
+
62 autosuper/tests.py
@@ -0,0 +1,62 @@
+"""
+
+ autosuper.tests: testcases for autosuper module.
+
+"""
+
+import os
+import unittest
+
+import autosuper
+
+class TestAutoSuperDocs(unittest.TestCase):
+
+ def test_readme_matches_docstring(self):
+ """Ensure that the README is in sync with the docstring.
+
+ This test should always pass; if the README is out of sync it just
+ updates it with the contents of autosuper.__doc__.
+ """
+ dirname = os.path.dirname
+ readme = os.path.join(dirname(dirname(__file__)),"README.rst")
+ if not os.path.isfile(readme):
+ f = open(readme,"wb")
+ f.write(autosuper.__doc__.encode())
+ f.close()
+ else:
+ f = open(readme,"rb")
+ if f.read() != autosuper.__doc__:
+ f.close()
+ f = open(readme,"wb")
+ f.write(autosuper.__doc__.encode())
+ f.close()
+
+
+
+class TestAutoSuper(unittest.TestCase):
+
+ def test_basic_diamond(self):
+ class Base(object):
+ def calc(self,value):
+ return 2 * value
+ class Sub1(Base):
+ def calc(self,value):
+ return 7 + super().calc(value)
+ class Sub2(Base):
+ def calc(self,value):
+ return super().calc(value) - 1
+ class Diamond(Sub1,Sub2):
+ def calc(self,value):
+ return 3 * super().calc(value)
+
+ b = Base()
+ s1 = Sub1()
+ s2 = Sub2()
+ d = Diamond()
+ for x in range(10):
+ self.assertEquals(b.calc(x),2*x)
+ self.assertEquals(s1.calc(x),7+(2*x))
+ self.assertEquals(s2.calc(x),(2*x)-1)
+ self.assertEquals(d.calc(x),3*(7+((2*x)-1)))
+
+
41 setup.py
@@ -0,0 +1,41 @@
+
+import sys
+setup_kwds = {}
+
+from distutils.core import setup
+
+import autosuper
+
+NAME = "autosuper"
+PACKAGES = ["autosuper"]
+VERSION = autosuper.__version__
+DESCRIPTION = "backport the magical zero-argument super() to python2"
+LONG_DESC = autosuper.__doc__
+AUTHOR = "Ryan Kelly"
+AUTHOR_EMAIL = "ryan@rfk.id.au"
+URL="http://github.com/rfk/autosuper"
+LICENSE = "MIT"
+KEYWORDS = "super mro"
+CLASSIFIERS = [
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 2",
+ "License :: OSI Approved",
+ "License :: OSI Approved :: MIT License",
+ "Development Status :: 4 - Beta",
+ "Intended Audience :: Developers",
+]
+
+setup(name=NAME,
+ version=VERSION,
+ author=AUTHOR,
+ author_email=AUTHOR_EMAIL,
+ url=URL,
+ description=DESCRIPTION,
+ long_description=LONG_DESC,
+ license=LICENSE,
+ keywords=KEYWORDS,
+ packages=PACKAGES,
+ classifiers=CLASSIFIERS,
+ **setup_kwds
+ )
+

0 comments on commit ff2ad0c

Please sign in to comment.