Skip to content

Commit

Permalink
versionary: add get_applicable method
Browse files Browse the repository at this point in the history
which adds the possibility to select a version based on passed
minium and maximum versions requirements,
so we do not need to know what versions are actually implemented and
can select the highest available version within the given boundaries.

If no version was found a new exception NoApplicableVersion is raised

Signed-off-by: Konrad Weihmann <[email protected]>
  • Loading branch information
priv-kweihmann committed Mar 30, 2023
1 parent 47be3b0 commit ce0c2aa
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 5 deletions.
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]}

13 changes: 12 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,17 @@ def test_decorator_for_classes_without_number():
assert MyClass.v2().hello() == 4


def test_decorator_for_classes_get_applicable():
"""
Test the decorator for 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_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):
version_table = getattr(cls._modref, '__version_table__')
try:
best_version = max([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

0 comments on commit ce0c2aa

Please sign in to comment.