Skip to content

Commit

Permalink
Bumps version to 0.5.0. Adds is_data_attr and classproperty
Browse files Browse the repository at this point in the history
  • Loading branch information
RobRuana committed May 2, 2017
1 parent b8f57aa commit 01a3cf9
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 3 deletions.
7 changes: 7 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
This file describes user-visible changes between pockets versions.


Version 0.5.0 (2017-05-02)
--------------------------

* Adds pockets.decorators module
* Adds pockets.inspect.is_data_attr function


Version 0.4.2 (2017-04-30)
--------------------------

Expand Down
1 change: 1 addition & 0 deletions pockets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from __future__ import absolute_import
from pockets._version import __version__
from pockets.collections import *
from pockets.decorators import *
from pockets.inspect import *
from pockets.iterators import *
from pockets.logging import *
Expand Down
2 changes: 1 addition & 1 deletion pockets/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
# 1) we don't load dependencies by storing it in __init__.py
# 2) we can import it in setup.py for the same reason
# 3) we can import it into your module
__version__ = '0.4.2'
__version__ = '0.5.0'
45 changes: 45 additions & 0 deletions pockets/decorators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017 the Pockets team, see AUTHORS.
# Licensed under the BSD License, see LICENSE for details.

"""A pocket full of useful decorators!"""


__all__ = ['classproperty']


def classproperty(cls):
"""
Decorator to create a read-only class property similar to classmethod.
For whatever reason, the @property decorator isn't smart enough to
recognize @classmethods and behaves differently on them than on instance
methods. This decorator may be used like to create a class-level property,
useful for singletons and other one-per-class properties.
Note:
Class properties created by @classproperty are read-only. Any attempts
to write to the property will erase the @classproperty, and the
behavior of the underlying method will be lost.
>>> class MyClass(object):
... @classproperty
... def myproperty(cls):
... return '{0}.myproperty'.format(cls.__name__)
>>> MyClass.myproperty
'MyClass.myproperty'
"""
class _classproperty(property):
def __get__(self, cls, owner):
return self.fget.__get__(None, owner)()

def getter(self, fget):
raise AttributeError('@classproperty.getter is not supported')

def setter(self, fset):
raise AttributeError('@classproperty.setter is not supported')

def deleter(self, fdel):
raise AttributeError('@classproperty.deleter is not supported')
return _classproperty(classmethod(cls))
25 changes: 24 additions & 1 deletion pockets/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

__all__ = [
'collect_subclasses', 'collect_superclasses',
'collect_superclass_attr_names', 'resolve']
'collect_superclass_attr_names', 'is_data_attr', 'resolve']


def collect_subclasses(cls):
Expand Down Expand Up @@ -111,6 +111,29 @@ def collect_superclass_attr_names(cls, terminal_class=None, modules=None):
return list(attr_names)


def is_data_attr(obj):
"""
Returns True if `obj` is a "data like" object.
Strongly inspired by `inspect.classify_class_attrs`. This function is
useful when trying to determine if an object's attributes have meaningful
docstrings or not. The methods of an object can have meaningful docstrings,
whereas the attributes of an object cannot.
Args:
obj (object): The object in question.
Returns:
bool: True if `obj` is "data like", False otherwise.
"""
if isinstance(obj, (classmethod, staticmethod, property)) or \
inspect.ismethod(obj) or inspect.ismethoddescriptor(obj) or \
inspect.isfunction(obj) or inspect.isdatadescriptor(obj):
return False
else:
return True


def resolve(name, modules=None):
"""
Resolve a dotted name to an object (usually class, module, or function).
Expand Down
26 changes: 26 additions & 0 deletions tests/test_decorators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017 the Pockets team, see AUTHORS.
# Licensed under the BSD License, see LICENSE for details.

"""Tests for :mod:`pockets.decorators` module."""

from __future__ import absolute_import
import pytest
from pockets.decorators import classproperty


class A(object):
@classproperty
def prop(cls):
return '{0}.prop'.format(cls.__name__)

def meth(cls):
pass


def test_classproperty():
assert A.prop == 'A.prop'
assert A().prop == 'A.prop'
pytest.raises(AttributeError, classproperty(A.meth).getter, A.meth)
pytest.raises(AttributeError, classproperty(A.meth).setter, A.meth)
pytest.raises(AttributeError, classproperty(A.meth).deleter, A.meth)
48 changes: 47 additions & 1 deletion tests/test_inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@

import pockets
import pytest
from pockets.decorators import classproperty
from pockets.inspect import collect_subclasses, collect_superclasses, \
collect_superclass_attr_names, resolve
collect_superclass_attr_names, is_data_attr, resolve


class A(object):
Expand Down Expand Up @@ -93,6 +94,51 @@ def test_collect_superclass_attr_names(self):
'Z_attr'])


class TestIsDataAttr(object):

class DocClass(object):
"""is not data"""
clsattr = 'is data'

def __init__(self):
self.attr = 'is data'

@classproperty
def clsprop(cls):
"""is data"""
pass

@classmethod
def clsmeth(cls):
"""is not data"""
pass

@property
def prop(self):
"""is not data"""
pass

def meth(self):
"""is not data"""
pass

def test_is_data_attr(self):
assert is_data_attr('string literal')
assert is_data_attr(TestIsDataAttr.DocClass)
assert is_data_attr(TestIsDataAttr.DocClass.clsattr)
assert is_data_attr(TestIsDataAttr.DocClass.clsprop)
assert not is_data_attr(TestIsDataAttr.DocClass.prop)
assert not is_data_attr(TestIsDataAttr.DocClass.meth)
assert not is_data_attr(TestIsDataAttr.DocClass.clsmeth)
docobj = TestIsDataAttr.DocClass()
assert is_data_attr(docobj)
assert is_data_attr(docobj.attr)
assert is_data_attr(docobj.clsattr)
assert is_data_attr(docobj.prop)
assert not is_data_attr(docobj.meth)
assert not is_data_attr(docobj.clsmeth)


class TestResolve(object):
def test_none(self):
assert resolve(None) is None
Expand Down

0 comments on commit 01a3cf9

Please sign in to comment.