added pytest n fixes

This commit is contained in:
Tobias Weise 2025-06-16 23:57:11 +02:00
parent 11cafe8177
commit da946981b1
9 changed files with 165 additions and 107 deletions

3
.gitignore vendored
View File

@ -1 +1,4 @@
*.pyc *.pyc
*__pycache__
.pytest_cache
try.py

View File

@ -1,5 +1,6 @@
from .func import * from .func import function
#from .list import * #from .list import *
#from .string import * #from .string import *
#from .io import * #from .io import *

View File

@ -1,20 +0,0 @@
"""
def log(func):
def new_func(*args):
r = func(*args)
print(">>> " + str(func).split(" ")[1] + str(list(args)) + " -> " + str(r))
return r
return new_func
"""
def log(f):
def g(*args):
r = f(*args)
print("Calculating: %s(%s) = %s" % ( str(f).split(" ")[1], str(",".join(map(str,list(args)))), str(r)) )
return r
return g

View File

@ -2,16 +2,26 @@ from inspect import signature, Parameter, _empty
from functools import partial from functools import partial
from typing import Any from typing import Any
class Function:
#@staticmethod def flyweight(cls):
#def add_missing_signature(f):
def f(*args, **kwargs):
return cls(*args, **kwargs)
return f
#@flyweight
class function:
__match_args__ = ["signature", "is_pure"] __match_args__ = ["signature", "is_pure"]
@staticmethod @staticmethod
def compose(f, g): def compose(f, g):
if type(g) == Function: if type(g) == function:
g = g.original_func g = g.original_func
else: else:
#whatif lambda? #whatif lambda?
@ -25,13 +35,11 @@ class Function:
pass pass
#its a functools.partial func with no name... #its a functools.partial func with no name...
if type(f) == function:
if type(f) == Function:
f = f.original_func f = f.original_func
sig_g = signature(g) sig_g = signature(g)
g_req_params = [v for k, v in sig_g.parameters.items() if v.default == _empty] g_req_params = [v for k, v in sig_g.parameters.items() if v.default == _empty]
g_typed_req_params = [p if p.annotation == _empty else Parameter(name=p.name, annotation=Any, kind=Parameter.POSITIONAL_ONLY) for p in g_req_params] g_typed_req_params = [p if p.annotation == _empty else Parameter(name=p.name, annotation=Any, kind=Parameter.POSITIONAL_ONLY) for p in g_req_params]
def h(*args, **kwargs): def h(*args, **kwargs):
@ -39,7 +47,10 @@ class Function:
f_ret_type = signature(f).return_annotation f_ret_type = signature(f).return_annotation
h.__signature__ = sig_g.replace(parameters=g_typed_req_params, return_annotation=Any if f_ret_type == _empty else f_ret_type) h.__signature__ = sig_g.replace(parameters=g_typed_req_params, return_annotation=Any if f_ret_type == _empty else f_ret_type)
return Function(h) return function(h)
#TODO: implement fly weight pattern
def __init__(self, f): def __init__(self, f):
self.original_func = f self.original_func = f
@ -49,30 +60,33 @@ class Function:
self.is_pure = True self.is_pure = True
def __str__(self): def __str__(self):
return str(signature(self.original_func)) return str(signature(self.original_func)).replace(", /)", ")")
def __repr__(self): def __repr__(self):
return str(self) return str(self)
def __mul__(self, g): def __mul__(self, g):
return Function.compose(self, g) return function.compose(self, g)
def __rmul__(self, g): def __rmul__(self, g):
return Function.compose(g, self) return function.compose(g, self)
def __eq__(self, other):
return self.original_func == other.original_func
@property @property
def required_args(self): def required_args(self):
return [(k, v) for k, v in signature(self.original_func).parameters.items() if v.default == _empty] return [(k, v) for k, v in signature(self.original_func).parameters.items() if v.default == _empty]
def __call__(self, *args, **kwargs): def __call__(self, *args, **kwargs):
#currying: if less than the needed args are given return a partial function
if len(args) < len(self.required_args): if len(args) < len(self.required_args):
sig = signature(self.original_func) sig = signature(self.original_func)
f = partial(self.original_func, *args) f = partial(self.original_func, *args)
req_params = [v for k, v in sig.parameters.items() if v.default == _empty] req_params = [v for k, v in sig.parameters.items() if v.default == _empty]
remaining_params = req_params[len(args):] remaining_params = req_params[len(args):]
f.__signature__ = sig.replace(parameters=remaining_params, return_annotation=Any if sig.return_annotation == _empty else sig.return_annotation) f.__signature__ = sig.replace(parameters=remaining_params, return_annotation=Any if sig.return_annotation == _empty else sig.return_annotation)
return Function(f) return function(f)
r = self.original_func(*args, **kwargs) r = self.original_func(*args, **kwargs)
if self.is_pure: if self.is_pure:
@ -83,31 +97,9 @@ class Function:
@Function
def inc(x: int) -> int:
return x + 1
@Function
def multi(a: int, b: int) -> int:
return a * b
@Function
def add(a: int, b: int) -> int:
return a + b
#print(add(2,3))
#print((inc * add)(2,3))
#print((inc * add))
#print(add(99))
print((add(99) * multi(100) * add(2) * (lambda x: x + 1)))
print(Function.compose(lambda x: x + 1, lambda x: x * 3))
print(signature(Function.compose(lambda x: x + 1, lambda x: x * 3)))
#print(inspect.signature(add))
#print(inspect.signature(add).parameters)

View File

@ -1,49 +0,0 @@
#======= Decoration ========
def log(f):
def g(*args):
r = f(*args)
print(">> %s %s -> %s" % (str(f).split(" ")[1], str(list(args)), str(r)))
return r
return g
#========= Composition =========
def fix1(f, v):
def g(*args):
r = f(*([v]+list(args)))
return r
return g
def curry(func, numArgs):
if numArgs == 1: return func
def f1(v):
return curry(fix1(func, v), numArgs-1)
return f1
def comp(g,f):
def r(*args, **kwargs):
return g(f(*args, **kwargs))
return r
#======== Operations ===========
def add(a, b):
return a + b
def multi(a, b):
return a * b
def Monade():
#Monade, (M a -> (a -> M b) -> M a)
def returm(x):
return [x]
def bind(m, f):
return returm(list(map(f,m)))
return returm, bind

89
poetry.lock generated Normal file
View File

@ -0,0 +1,89 @@
# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand.
[[package]]
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
[[package]]
name = "iniconfig"
version = "2.1.0"
description = "brain-dead simple config-ini parsing"
optional = false
python-versions = ">=3.8"
files = [
{file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"},
{file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"},
]
[[package]]
name = "packaging"
version = "25.0"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.8"
files = [
{file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"},
{file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"},
]
[[package]]
name = "pluggy"
version = "1.6.0"
description = "plugin and hook calling mechanisms for python"
optional = false
python-versions = ">=3.9"
files = [
{file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"},
{file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"},
]
[package.extras]
dev = ["pre-commit", "tox"]
testing = ["coverage", "pytest", "pytest-benchmark"]
[[package]]
name = "pygments"
version = "2.19.1"
description = "Pygments is a syntax highlighting package written in Python."
optional = false
python-versions = ">=3.8"
files = [
{file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"},
{file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"},
]
[package.extras]
windows-terminal = ["colorama (>=0.4.6)"]
[[package]]
name = "pytest"
version = "8.4.0"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.9"
files = [
{file = "pytest-8.4.0-py3-none-any.whl", hash = "sha256:f40f825768ad76c0977cbacdf1fd37c6f7a468e460ea6a0636078f8972d4517e"},
{file = "pytest-8.4.0.tar.gz", hash = "sha256:14d920b48472ea0dbf68e45b96cd1ffda4705f33307dcc86c676c1b5104838a6"},
]
[package.dependencies]
colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""}
iniconfig = ">=1"
packaging = ">=20"
pluggy = ">=1.5,<2"
pygments = ">=2.7.2"
[package.extras]
dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"]
[metadata]
lock-version = "2.0"
python-versions = "^3.12"
content-hash = "b758df875e7c5835fb6160e4d2f49f9e3a2fef076e22613be6664aa81e2db2c2"

View File

@ -8,6 +8,7 @@ readme = "README.md"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.12" python = "^3.12"
pytest = "^8.4.0"
[build-system] [build-system]
@ -15,4 +16,3 @@ requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api" build-backend = "poetry.core.masonry.api"

1
tests/__init__.py Normal file
View File

@ -0,0 +1 @@

41
tests/test_basic.py Normal file
View File

@ -0,0 +1,41 @@
from inspect import signature
from func_py import function
@function
def inc(x: int) -> int:
return x + 1
@function
def multi(a: int, b: int) -> int:
return a * b
@function
def add(a: int, b: int) -> int:
return a + b
def test_abc():
#print(add(2,3))
#print((inc * add)(2,3))
#print((inc * add))
#print(add(99))
print((add(99) * multi(100) * add(2) * (lambda x: x + 1)))
print(function.compose(lambda x: x + 1, lambda x: x * 3))
print(signature(function.compose(lambda x: x + 1, lambda x: x * 3)))
#print(inspect.signature(add))
#print(inspect.signature(add).parameters)
assert True