-
Notifications
You must be signed in to change notification settings - Fork 2
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
Draft: Add a decorator method to record function calls with parameters #97
Conversation
This looks quite neat. I assume this only works for class functions of the core class right now, no? Can we also make this work for methods form other classes? In particular, it would be great if we also could track the final settings selected in the interactive tools. It also currently fails the tests, but this I suppose is quite easy to fix. |
I've fixed the issues, progressed the workflow recorder through to the subclasses, added storing of the function class, and added tracking to the most relevant workflow items. |
Oh sorry. I wasn't sure why I was getting an error to merge changes currently but now it makes sense. Anyways, you can see that I made changes to the module to track any function calls within a single python session. You're welcome to adjust the processor class accordingly, or I can also do it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great!
Its similar to what was attempted in hextof-processor it seems, but much better implemented!
Would only be nice to include testing for it.
Very nice addition!
from typing import List | ||
|
||
|
||
@dataclass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although I'm a big fan of the "dataclasses" module, to be consistent over the whole package, either I would stick to the way you were generating special methods (very easy) or adopt dataclasses everywhere (too much work).
In any case, LGTM.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm could you elaborate on the special methods? I just thought this is a very good usecase for a dataclass. Perhaps, if you give examples of where else it could be used in our code, it could be implemented. I personally like the clarity they bring to the codebase.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Special methods are, for example, all double under methods such as the ones used to initialize a class (def __init__(self, ...)
.
When I'm writing a class I tend to use the dataclass
decorator because it automatically takes care of some of those special methods that are boring to write :)
As far as I can see you only use it here and, from my point of view, it breaks consistency with all the other classes already implemented throughout all the library.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My thought was to use the dataclasses for classes that only hold data and the processing elsewhere. I especially see a good usecase with the Metadata as dataclasses can easily have entries like value and unit (using field). Of course, it's possible to do with dicts but it's not possible to add features to them.
Perhaps I had a wrong idea about dataclasses and you can correct me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I said, I tend to use them because it's a handy decorator. But it only does that (avoiding to write boilerplate code).
My point was that you tend to use dictionaries, lists and such in other classes, so personally, I would continue with such style also in here. I don't see any problems adding new entries to dictionaries or lists.
But, that's my personal view. I tend to be consistent with the code I write ;)
Yes, that's a fair point. I will do so once everything's been resolved.
Thanks! |
I don't understand how your new version works. I disbled the changes in all other files, but I get: TypeError: load() missing 1 required positional argument: 'self' When I am trying to test it. The previous version off course does not work for non-class functions, but actuall I did not see a need for this when checking the workflow. |
I'll put here an example:
should return
If we also want to include the classname, changing I made the changes I did before you did, after reading your comment
So now the wrapper is fully independant from any modules or classes and keeps the state. Before, to keep the state, we'd need to store it seperately in each class. |
Okay I see where the error occurs. It seems if we leave the arguments to default, it complains. I'll try to find a fix. |
Have a look at 6b57ead there I passed the list of calls along to each other class, so that they can access this, similar to the MetaHandler. I think that works sufficiently well, if fixing the class method issue is difficult. |
I believe the issue is now resolved. Currently, the default arguments are not stored in the list. It is possible to do that, if that is a useful feature. Perhaps to easily understand what parameter values were used. |
) | ||
return self.func(*args, **kwargs) | ||
return self.func(self, *args, **kwargs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you test that? I understand that this will pass the MethodCall instance instead of the one of the calling function.
At least when trying to instantiate the sed processor now, I get:
AttributeError: 'CallTracker' object has no attribute 'loader'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I did test that, with the same example I provided above.
You are not wrong but the update_wrapper argument basically makes the MethodCall instance into a Foo instance, from what I understood. I used it in the functional wrapper approach as well (but it uses a different function there).
I will try to get it to work with loader and other methods we have.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@daviddoji if you have a better understanding of decorators, would be very helpful :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Never played too much with decorators (just silly ones than do timing). Maybe have a look at some examples in here: https://wiki.python.org/moin/PythonDecoratorLibrary
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also think that you should return self.func(*args,**kwargs) as the decorator should then just operate the function as normal.
It seems to me that adding self adds an argument to the function before calling...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately that does not pass the self argument to the method, in the case where you don't provide all the arguments (default ones). This one worked for me for a test case but apparently isn't for the real situation. So definitely buggy and I need to understand better the handling.
I think it is not related to default arguments. This example reproduces the problem:
Output:
|
Apparently the "self" object passed to decorated functions is that of CallTracker, rather than the parent class. |
The previous version I marked in the commit above (or the following one with linting) should do what we want without these problems, at least it looked like it to me. Only downside I see is that it only works for class methods, but it was anyways the idea to provide wrappers for all relevant functions, no? |
Sorry for not continuing this request. I was a bit busy with exams, but seeing that Steinn has a different proposal, we can hold it off for the time being? @steinnymir or do you suggest, I finish this feature? |
I am not yet sure how the new version might connect to this. My first impression is it would no longer be required, but I cannot yet say with certainty. |
Not relevant with the new architecture. |
Since, the new architecture will take time still, I thought we could finish this off and add it to sed. Possibly the branch is so old that making a new branch would be better. But opening for discussion. |
Related to #87