Initial commit

This commit is contained in:
2024-01-23 14:32:21 +01:00
commit 2898a6bd38
12 changed files with 493 additions and 0 deletions

View File

@ -0,0 +1,68 @@
# https://www.codewars.com/kata/5f24315eff32c4002efcfc6a
import inspect
# https://docs.python.org/3.8/reference/datamodel.html#customizing-class-creation
# https://snarky.ca/unravelling-pythons-classes/
# https://gist.github.com/tmr232/c7214d62607ba1fc0e0a2e82d7e041cd
class DispatchProxy:
def __init__(self, name):
self.registry = {}
self.name = name
def register(self, f):
print(f"register {inspect.signature(f)}")
argcount = f.__code__.co_argcount
self.registry[argcount] = f
"""
def decorator(func):
argcount = f.__code__.co_argcount
self.registry[argcount] = f
return func
return decorator
"""
def __call__(self, *args):
argcount = len(args) + 1 # add 1 because the first argument of a method is "self"
if argcount in self.registry:
resolved = self.registry[argcount]
print(f"resolved: {resolved.__name__}{inspect.signature(resolved)}")
return resolved(self, *args)
else:
raise AttributeError(f"No matching function for args: {args}")
class MyNamespace(dict):
def __getitem__(self, key):
#(f"get {key}")
return super().__getitem__(key)
def __setitem__(self, key, value):
#sign = inspect.signature(value) if callable(value) else value
#print(f"set {key} {value}")
# already exists => overload
is_method = callable(value) and not value.__name__.startswith("__")
if is_method and key in self:
m = self[key]
if not isinstance(m, DispatchProxy):
m = DispatchProxy(self)
m.register(self[key])
m.register(value)
super().__setitem__(key, m)
else:
super().__setitem__(key, value)
class Meta(type):
@classmethod
def __prepare__(cls, name, bases):
return MyNamespace()
def __new__(cls, name, bases, dct):
x = super().__new__(cls, name, bases, dct)
return x
def __init__(self, name, bases, attrs):
super(Meta, self).__init__(name, bases, attrs)

View File

@ -0,0 +1,160 @@
import codewars_test as test
from overload import Meta
cng_msg = 'Dont change the properties of class.'
sld_work = 'Function overloading should work.'
@test.describe('Python Recipes #1 : Function Overloading')
def _():
# implement meta class -----
def build(): # returning class to regenerate accordingly.
class Overload(metaclass=Meta):
CLS_VAR = 42
def __init__(self):
self.a = 1
self.no = 'This is "No parameter" function.'
self.single = 'This is "Single parameter" function'
self.two = 'This is "Two parameter" function'
self.three = 'This is "Three parameter" function'
def foo(self):
return self.no
def foo(self, x):
return self.single + ':' + str(x)
def foo(self, x, y):
return self.two + ':' + str(x) + ',' + str(y)
def foo(self, x, y, z):
return self.three + ':' + str(x) + ',' + str(y) + ',' + str(z)
def extra(self):
return 'This is extra method.'
return Overload
@test.describe('Sample tests')
def _():
overload = build()
obj = overload()
@test.it('Verifying access')
def _():
test.assert_equals(obj.a, 1, cng_msg)
test.assert_equals(obj.no, 'This is "No parameter" function.', cng_msg)
test.assert_equals(obj.single, 'This is "Single parameter" function', cng_msg)
test.assert_equals(obj.two, 'This is "Two parameter" function', cng_msg)
test.assert_equals(obj.three, 'This is "Three parameter" function', cng_msg)
test.assert_equals(obj.extra(), 'This is extra method.', cng_msg)
test.assert_equals(obj.CLS_VAR, 42, cng_msg)
@test.it('Verifying overloading feature')
def _():
test.assert_equals(obj.foo(), 'This is "No parameter" function.', sld_work)
test.assert_equals(obj.foo(1), 'This is "Single parameter" function:1', sld_work)
test.assert_equals(obj.foo(1,2), 'This is "Two parameter" function:1,2', sld_work)
test.assert_equals(obj.foo(1,2,3), 'This is "Three parameter" function:1,2,3', sld_work)
@test.it('Verifying overloading feature after adding new methods :1')
def _():
def foo(self, a, b, c, d):
return self.a + a + b + c + d
def foo1(self, a, b):
return 'Overwritten'
overload.foo = foo
test.assert_equals(obj.foo(), 'This is "No parameter" function.', sld_work)
test.assert_equals(obj.foo(1), 'This is "Single parameter" function:1', sld_work)
test.assert_equals(obj.foo(1,2), 'This is "Two parameter" function:1,2', sld_work)
test.assert_equals(obj.foo(1,2,3), 'This is "Three parameter" function:1,2,3', sld_work)
test.assert_equals(obj.foo(1,2,3,4), 11, sld_work)
overload.foo = foo1
test.assert_equals(obj.foo(1,2), 'Overwritten', sld_work)
@test.it('Verifying overloading feature after adding new methods :2')
def _():
def boo1(self):
return self.a
def boo2(self, a):
return self.a + a
def unique(self, a, b):
return a + b
overload.boo = boo1
overload.boo = boo2
obj.nothing = '123'
overload.unique = unique
test.assert_equals(obj.boo(), 1, sld_work)
test.assert_equals(obj.boo(1), 2, sld_work)
test.assert_equals(obj.unique(1, 2), 3, sld_work)
test.assert_equals(obj.nothing, '123', sld_work)
@test.describe('Verify the absence of unwanted sharing of methods between different classes')
def _():
class Unshare(metaclass=Meta):
def foo(self,a,b): return 'unshared!'
unsh=Unshare()
@test.it('The new foo function in Unshare class should be used')
def _():
test.assert_equals(unsh.foo(1,2), 'unshared!')
@test.it('Previous implementations of "foo" in other classes shouldn\'t be visible from a freshly built class')
def _():
test.expect_error("Your function didn't raise any exception", lambda: unsh.foo(1), Exception)
test.expect_error("Your function should raise AttributeError", lambda: unsh.foo(1), AttributeError)
@test.it('Previous method with the same signature in other classes shouldn\'t be affected')
def _():
test.assert_equals(obj.foo(1,2), 'Overwritten')
@test.it('Class level variables are still working as expected')
def _():
def over(): obj.CLS_VAR=-42
def write(): obj.HAHA=msg
test.expect_no_error("Class level variables can be overriden", over)
test.assert_equals(obj.CLS_VAR, -42, "Class variable Overload.CLS_VAR hasn't been overriden")
msg = 'This is another class variable'
test.expect_no_error("New class level variables can be defined", write)
test.assert_equals(obj.HAHA, msg, "New class level variables can be defined and accessed")
@test.it('Undefined methods or variables should raise AttributeError')
def _():
test.expect_error('Undefined attributes/mehtods should raise AttributeError',
lambda: overload.NEW_CLS_VAR, AttributeError)
test.expect_error('Undefined attributes/mehtods should raise AttributeError',
lambda: obj.ABCD, AttributeError)
test.expect_error('Undefined attributes/mehtods should raise AttributeError',
lambda: obj.randomFuncName(), AttributeError)
@test.it('undefined overloaded methods (wrong number of arguments should raise AttributeError)')
def _():
test.expect_error('Undefined attributes/mehtods should raise AttributeError',
lambda: overload.foo(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1), AttributeError)