Skip to content

Commit

Permalink
Modified get_callable_name() to work with a wider variety of callables
Browse files Browse the repository at this point in the history
  • Loading branch information
agronholm committed Oct 9, 2014
1 parent 43c8eee commit 5c69150
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 20 deletions.
22 changes: 14 additions & 8 deletions apscheduler/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,20 +194,26 @@ def get_callable_name(func):
:rtype: str
"""

f_self = getattr(func, '__self__', None) or getattr(func, 'im_self', None)
# the easy case (on Python 3.3+)
if hasattr(func, '__qualname__'):
return func.__qualname__

# class methods, bound and unbound methods
f_self = getattr(func, '__self__', None) or getattr(func, 'im_self', None)
if f_self and hasattr(func, '__name__'):
if isinstance(f_self, type):
# class method
clsname = getattr(f_self, '__qualname__', None) or f_self.__name__
return '%s.%s' % (clsname, func.__name__)
# bound method
return '%s.%s' % (f_self.__class__.__name__, func.__name__)
f_class = f_self if isinstance(f_self, type) else f_self.__class__
else:
f_class = getattr(func, 'im_class', None)

if f_class and hasattr(func, '__name__'):
return '%s.%s' % (f_class.__name__, func.__name__)

# class or class instance
if hasattr(func, '__call__'):
# class
if hasattr(func, '__name__'):
# function, unbound method or a class with a __call__ method
return func.__name__

# instance of a class with a __call__ method
return func.__class__.__name__

Expand Down
26 changes: 14 additions & 12 deletions tests/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import pytest
import pytz
import six
import sys

from apscheduler.util import (
asint, asbool, astimezone, convert_to_datetime, datetime_to_utc_timestamp, utc_timestamp_to_datetime,
Expand Down Expand Up @@ -163,9 +164,9 @@ def test_datetime_repr(input, expected):
class TestGetCallableName(object):
@pytest.mark.parametrize('input,expected', [
(asint, 'asint'),
(DummyClass.staticmeth, 'staticmeth'),
(DummyClass.staticmeth, 'DummyClass.staticmeth' if hasattr(DummyClass, '__qualname__') else 'staticmeth'),
(DummyClass.classmeth, 'DummyClass.classmeth'),
(DummyClass.meth, 'meth'),
(DummyClass.meth, 'meth' if sys.version_info[:2] == (3, 2) else 'DummyClass.meth'),
(DummyClass().meth, 'DummyClass.meth'),
(DummyClass, 'DummyClass'),
(DummyClass(), 'DummyClass')
Expand All @@ -178,26 +179,27 @@ def test_bad_input(self):


class TestObjToRef(object):
@pytest.mark.parametrize('input', [DummyClass.meth, DummyClass.staticmeth, partial(DummyClass.meth)],
ids=['bound method', 'static method', 'partial/bound method'])
@pytest.mark.parametrize('input', [partial(DummyClass.meth)], ids=['partial/bound method'])
def test_no_ref_found(self, input):
exc = pytest.raises(ValueError, obj_to_ref, input)
assert 'Cannot determine the reference to ' in str(exc.value)

@pytest.mark.parametrize('input,expected', [
pytest.mark.skipif(sys.version_info[:2] == (3, 2), reason="Unbound methods can't be resolved on Python 3.2")(
(DummyClass.meth, 'tests.test_util:DummyClass.meth')
),
(DummyClass.classmeth, 'tests.test_util:DummyClass.classmeth'),
pytest.mark.skipif(sys.version_info < (3, 3), reason="Requires __qualname__ (Python 3.3+)")(
(DummyClass.InnerDummyClass.innerclassmeth, 'tests.test_util:DummyClass.InnerDummyClass.innerclassmeth')
),
pytest.mark.skipif(sys.version_info < (3, 3), reason="Requires __qualname__ (Python 3.3+)")(
(DummyClass.staticmeth, 'tests.test_util:DummyClass.staticmeth')
),
(timedelta, 'datetime:timedelta'),
], ids=['class method', 'timedelta'])
], ids=['unbound method', 'class method', 'inner class method', 'static method', 'timedelta'])
def test_valid_refs(self, input, expected):
assert obj_to_ref(input) == expected

@minpython(3, 3)
def test_inner_class_method(self):
"""Tests that a reference to a class method of an inner class can be discovered."""

assert obj_to_ref(DummyClass.InnerDummyClass.innerclassmeth) == \
'tests.test_util:DummyClass.InnerDummyClass.innerclassmeth'


class TestRefToObj(object):
def test_valid_ref(self):
Expand Down

0 comments on commit 5c69150

Please sign in to comment.