ENH: Expose original func as PipeFunc.__call__ #910
ENH: Expose original func as PipeFunc.__call__ #910LennartGevers wants to merge 4 commits intopipefunc:mainfrom
PipeFunc.__call__ #910Conversation
|
Thank you for trying to fix this. I fully agree that if we could get this working, we should merge it. |
37efdf7 to
71aee6f
Compare
CodSpeed Performance ReportMerging #910 will not alter performanceComparing Summary
|
✅ PR Title Formatted CorrectlyThe title of this PR has been updated to match the correct format. Thank you! |
|
Hey @basnijholt, this branch is ~90% there, but I think that the current solution is inferior to an alternative but more intricate design that’s worth discussing. The main issue is that once you start using renames, defaults, or bound, the function’s signature changes at runtime and Python’s Example: @pipefunc("f", update_defaults={"b": 0.5})
def f(a: int, b: float = 2) -> int:
return a**b
f(4) # 16
f.run(4) # 2This works, but it feels weird that I think the key insight is that there are really two kinds of attributes: Right now they’re all handled in the same class, which complicates typing and behavior. So my idea is to split them:
We could alternatively overload pipefunc so it returns a Func when no signature-modifying args are given, and a PipeFunc otherwise. @overload
def pipefunc(..., renames=None, defaults=None, bound=None) -> Func: ...
@overload
def pipefunc(..., renames=..., defaults=..., bound=...) -> PipeFunc: ...But to be really honest, I think that a function which effectively has stateful behaviour is an anti-pattern. This should be reserved exclusively for OOP. So maybe we should only advocate instantiating Differentiating the @pipefunc("f",)
def f(a: int, b: float = 2) -> int:
return a**b
h = f
h.update_defaults({"b": 0.5})
f(4) #2Would do you think about that? @basnijholt |
Hi @basnijholt,
I gave it a shot to make the
@pipefuncdecorator less invasive, so that the annotations and docs are not removed, as mentioned in #902.Exposing the annotations is quite easy if done correctly with
ParamSpecfor the input annotations and a normalTypeVarfor the output annotation.Exposing the documentation is a bit subtle and I currently see two different approaches here.
In the case of
if the documentation should be readable when hovering the mouse over "function" on the last line, then the output annotation of the
pipefuncfunction needs to bebut in that case you will get a type checking error for things like
on the other hand, if we use the annotation
then
but then the documentation will only appear when the cursor is within the brackets (idk how to explain this, just check the demo.py file I pushed). I think that this would always be the standard behavior for PipeFuncs instantiated with
PipeFunc.__init__.I just wanted to push this draft for a quick review before diving into the subsequent changes that would still be needed so that this
PipeFuncimplementation also works within aPipeline.