Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

versionary: add get_applicable method #3

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ Keep the list in alfabetical order please.

* Devhouse Spindle {[email protected]}
* Marco Vellinga {[email protected]}
* Konrad Weihmann {[email protected]}

23 changes: 22 additions & 1 deletion tests/test_decorator.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from pytest import raises

from versionary.decorators import versioned
from versionary.exceptions import DuplicateVersionException, InheritanceException, InvalidVersionException
from versionary.exceptions import DuplicateVersionException, InheritanceException, InvalidVersionException, NoApplicableVersion

from .utils.mixed_members import MixedClass, mixed_func
from .utils.versioned_members import MyClass, MyInheritanceClass, my_function, your_function
Expand All @@ -24,6 +24,27 @@ def test_decorator_for_classes_without_number():
assert MyClass.v2().hello() == 4


def test_decorator_for_classes_get_applicable_max():
"""
Test the decorator for latest classes via get_applicable.
"""
assert MyClass.get_applicable()().hello() == 4
assert MyClass.get_applicable(minver=2)().hello() == 4
assert MyClass.get_applicable(maxver=1)().hello() == 3
with raises(NoApplicableVersion):
MyClass.get_applicable(minver=100)().hello()

def test_decorator_for_classes_get_applicable_min():
"""
Test the decorator for earliest classes via get_applicable.
"""
assert MyClass.get_applicable(func=min)().hello() == 3
assert MyClass.get_applicable(minver=2, func=min)().hello() == 4
assert MyClass.get_applicable(maxver=1, func=min)().hello() == 3
with raises(NoApplicableVersion):
MyClass.get_applicable(minver=100, func=min)().hello()


def test_decorator_for_class_inheritance_without_number():
"""
Test the decorator for classes that inherit without a number.
Expand Down
2 changes: 1 addition & 1 deletion tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def test_create_proxy_class():
"""
base_name = 'base_name'

proxy = create_proxy_class(base_name)
proxy = create_proxy_class(base_name, 123)

message = 'Cannot call `base_name` directly, use versioned attributes instead (`base_name`.vX)'

Expand Down
2 changes: 1 addition & 1 deletion versionary/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def wrap(member):
validate_inheritance_for_class(member)

# Get or create the proxy class to return.
proxy = version_members.get('proxy', create_proxy_class(name))
proxy = version_members.get('proxy', create_proxy_class(name, module))

# Add new version method to this proxy class.
setattr(proxy, '%s%s' % (PROXY_VERSION_TAG, version), staticmethod(member))
Expand Down
7 changes: 7 additions & 0 deletions versionary/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,10 @@ class NotCallableException(Exception):
Raised when trying to call the ProxyClass which shouldn't be called.
"""
pass


class NoApplicableVersion(Exception):
"""
Raised when no version matching the criteria was found.
"""
pass
14 changes: 12 additions & 2 deletions versionary/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from six import with_metaclass

from .exceptions import InvalidVersionException, NotCallableException
from .exceptions import InvalidVersionException, NotCallableException, NoApplicableVersion

CLASS, FUNCTION = 0, 1
CLASS_VERSION_TAG = 'V'
Expand Down Expand Up @@ -37,7 +37,7 @@ def determine_member_type(member):
return member_type


def create_proxy_class(base_name):
def create_proxy_class(base_name, mod):
"""
Function to create a proxy class to be able to set attributes to.
This is used to make my_func.v1() or MyClass.v1() possible.
Expand Down Expand Up @@ -83,7 +83,17 @@ def __getattr__(cls, name):
raise AttributeError('%r has no attribute %r' %
(cls._base_name, name))

def get_applicable(cls, minver=0, maxver=10e9, func=max):
version_table = getattr(cls._modref, '__version_table__')
try:
best_version = func([x for x in version_table[cls._base_name]
['members'].keys() if x <= maxver and x >= minver])
return version_table[cls._base_name]['members'][best_version]
except ValueError:
raise NoApplicableVersion()

setattr(ProxyType, '_base_name', base_name)
setattr(ProxyType, '_modref', mod)

class ProxyClass(with_metaclass(ProxyType)):
"""
Expand Down