Skip to content

Commit

Permalink
Merge branch 'abc-methods'
Browse files Browse the repository at this point in the history
  • Loading branch information
Matt Hoffman committed Jan 9, 2015
2 parents 52be795 + 92d88a3 commit 75cc76e
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 30 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/htmlcov/
/mwhutils.egg-info/
/build/
/dist/
17 changes: 11 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ Implementation of a few linear-algebra and other such methods in python. This is
mostly useful for machine learning code. This package may also be renamed once
I/we find a more cohesive description.

[![Build Status](https://travis-ci.org/mwhoffman/mwhutils.svg?branch=master)]
(https://travis-ci.org/mwhoffman/mwhutils)
[![Coverage Status](https://coveralls.io/repos/mwhoffman/mwhutils/badge.png?branch=master)]
(https://coveralls.io/r/mwhoffman/mwhutils?branch=master)
[![Build Status][travis-shield]][travis]
[![Coverage Status][coveralls-shield]][coveralls]

[travis]: https://travis-ci.org/mwhoffman/mwhutils
[coveralls]: https://coveralls.io/r/mwhoffman/mwhutils
[travis-shield]: https://img.shields.io/travis/mwhoffman/mwhutils.svg?style=flat
[coveralls-shield]: https://img.shields.io/coveralls/mwhoffman/mwhutils.svg?style=flat

Installation
============
Expand All @@ -17,6 +20,8 @@ The easiest way to install this package is by running

pip install git+https://github.com/mwhoffman/mwhutils.git

Alternatively the package can be installed by cloning the repository and using
`setup.py` directly.
The package can also be installed by cloning the repository and running `python
setup.py install` from the main directory. Alternatively running `pip install -e
.` will install the package globally but allow it to be edited from the cloned
directory.

58 changes: 49 additions & 9 deletions mwhutils/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@

# global imports
from abc import ABCMeta as ABCMeta_
from abc import abstractmethod
from abc import abstractmethod, abstractproperty

# exported symbols
__all__ = ['ABCMeta', 'abstractmethod']
__all__ = ['ABCMeta', 'abstractmethod', 'abstractproperty',
'abstractclassmethod', 'abstractstaticmethod']


class ABCMeta(ABCMeta_):
Expand All @@ -22,16 +23,55 @@ class ABCMeta(ABCMeta_):
docstring.
"""
def __new__(mcs, name, bases, attrs):
# set up the new class.
cls = super(ABCMeta, mcs).__new__(mcs, name, bases, attrs)

# get all the methods that are abtract in one of our parents.
abstracts = dict(
(attr, getattr(base, attr))
for base in bases
for attr in getattr(base, '__abstractmethods__', set()))

for attr, value in attrs.items():
implements = (attr in abstracts and
not getattr(value, '__isabstractmethod__', False))
if implements and not getattr(value, '__doc__', False):
docstring = getattr(abstracts[attr], '__doc__', None)
setattr(value, '__doc__', docstring)
# loop through the methods that have an implementation in the class
# that we just constructed.
for attr in set(abstracts.keys()) - cls.__abstractmethods__:
parent = abstracts[attr]
child = getattr(cls, attr)
docstring = getattr(parent, '__doc__', None)

if docstring and not getattr(child, '__doc__', None):
if isinstance(child, property):
setattr(cls, attr, property(child.fget, child.fset,
child.fdel, docstring))
elif hasattr(child, '__func__'):
child.__func__.__doc__ = docstring
else:
child.__doc__ = docstring

return cls


class abstractclassmethod(classmethod):
"""
Decorator to create an abstract class method. This is a variation on the
decorator introduced in Python 3. It has since been deprecated since
classmethod can now be used to decorate an abstractmethod.
"""
__isabstractmethod__ = True

def __init__(self, function):
function.__isabstractmethod__ = True
super(abstractclassmethod, self).__init__(function)


class abstractstaticmethod(staticmethod):
"""
Decorator to create an abstract static method. This is a variation on the
decorator introduced in Python 3. It has since been deprecated since
staticmethod can now be used to decorate an abstractmethod.
"""
__isabstractmethod__ = True

return super(ABCMeta, mcs).__new__(mcs, name, bases, attrs)
def __init__(self, function):
function.__isabstractmethod__ = True
super(abstractstaticmethod, self).__init__(function)
81 changes: 66 additions & 15 deletions tests/test_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,84 @@
from __future__ import absolute_import
from __future__ import print_function

from mwhutils.abc import ABCMeta, abstractmethod
import numpy.testing as nt
import mwhutils.abc as abc


def test_abc():
"""Test creation of an abstract base class."""

class FooBase(object):
"""Base class."""
__metaclass__ = ABCMeta

@abstractmethod
def method1(self):
"""Test."""
pass
__metaclass__ = abc.ABCMeta

def method2(self):
"""Test."""
pass
def method(self):
"""Method"""

@abc.abstractmethod
def abstract_method1(self):
"""Method 1"""

@abc.abstractmethod
def abstract_method2(self):
"""Method 2"""

@abc.abstractclassmethod
def abstract_classmethod(cls):
"""Class method"""

@abc.abstractstaticmethod
def abstract_staticmethod():
"""Static method"""

@abc.abstractproperty
def abstract_property(self):
"""Property"""

class Foo(FooBase):
"""Child class."""
def method1(self):
pass

def method2(self):
def method(self):
"""New method"""
pass

instance = Foo()
assert instance.method1.__doc__ == "Test."
assert instance.method2.__doc__ is None
def abstract_method1(self):
return 1

def abstract_method2(self):
"""Method 3"""
return 2

@classmethod
def abstract_classmethod(cls):
return 3

@staticmethod
def abstract_staticmethod():
return 4

@property
def abstract_property(self):
return 5

# Make sure the abstract class can't be instantiated
nt.assert_raises(TypeError, FooBase)

# check the docstrings
assert Foo.method.__doc__ == "New method"
assert Foo.abstract_method1.__doc__ == "Method 1"
assert Foo.abstract_method2.__doc__ == "Method 3"
assert Foo.abstract_classmethod.__doc__ == "Class method"
assert Foo.abstract_staticmethod.__doc__ == "Static method"
assert Foo.abstract_property.__doc__ == "Property"

# Make sure the instance can be instantiated
foo = Foo()

# check the implementations
assert foo.abstract_method1() == 1
assert foo.abstract_method2() == 2
assert foo.abstract_classmethod() == 3
assert foo.abstract_staticmethod() == 4
assert foo.abstract_property == 5

0 comments on commit 75cc76e

Please sign in to comment.