Open vmarkovtsev opened 5 years ago
I think that this refactoring issue is an important one to do. We do not have time to implement UIMA approach for Python, so, I think that the easiest solution will be to create a new class AnnotatedVirtualNode
:
class AnnotatedVirtualNode:
def __init__():
self.node = None # type: VirtualNode
self.y = None # type: Sequence[int]
self.y_pred = None # type: Sequence[int]
self.parent = None # AnnotatedVirtualNode
# etc
And put here all annotations we may need. After that, we agreed that VirtualNode
is immutable and that is.
It is a simple approach and maybe I missed some important details why it does not suit well, what do you think @m09?
This is mostly what we currently have. Some problems include:
Optional[…]
everywhere)Overall it's very far from a good annotation pattern. But indeed it might still be good to fix our current VirtualNode in this way (ie remove the cases that are not add-only in the codebase)
ok, I knew that I just did not see the problem in all colors. :)
AnnotatedVirtualNode
v2:
class Annotation:
def __init__(self):
self._annotations = {}
def __getitem__(self, item: str):
return self._annotations[item]
def __setitem__(self, key: str, value):
# We can also add a list of known annotations with their types.
# So we can check unexpected annotations and if it is known its type.
if key in self._annotations:
raise KeyError(("Annotation %s exists with value %s. It is not possible to overwrite "
"annotation") % (key, repr(self[key])))
self._annotations[key] = value
# I think it will be convenient to have such properties.
@property
def y(self):
try:
return self._annotations["y"]
except KeyError:
raise KeyError("There is no `y` annotation vnode %s")
# etc
class AnnotatedVirtualNode:
def __init__(self):
# plus read-only properties
self._node = None # type: VirtualNode
self._annotations = Annotation()
Also we can add a check function to know if this node is annotated as expected. Should be easy to introduce into the codebase and covers all your points.
Do you see any drawbacks?
Sadly this does not solve the biggest problem (potentially different number/location of annotations for each type). More generally, I don't think there is a shortcut that can reap the benefits of a proper annotation pattern while needing only a small refactoring.
Sorry, @m09, I did not get the problem about "different number/location of annotations for each type".
Not sure what do you mean by "type" and "location" and why we cannot have a set of 5 possible annotations that will cover all our needs (or maybe even two for now: y
and predicted
with y_new
and rule_number
)
You did not mention this problem, but we can also have special decorators for functions to check that vnode sequence has required annotations
By type I just mean a different annotation (so for example y and predicted would be different annotations). By different numbers and locations I mean what I already mentioned: atomic classes cannot be attributes of the same object than y and predicted because there are many more of them (they annotate different locations of the code).
In a nutshell: what's needed is to have a separate list of objects for each type of annotation and that is why it's a pain to refactor. The specifics of the objects that represent annotations are not really important (they do matter for the add-only aspect and general performance/usability though).
Ah, now I see the problem about vnodes sequence with atomic classes and vnodes sequence with merged classes.
However, I think that we can consider these sequences as completely different. Moreover, the first one is kind of internal and appears only inside FeatureExtractor._parce_vnodes()
function. Maybe it is not the best solution to use the same VirtualNode
class for both because the vnode meanings are different, but I think it is a minor issue. As a result of FeatureExtractor._parce_vnodes()
we have unchangeable vnodes sequence and just add more annotations on the way: y_original
, y_filtered
, y_predicted
, etc.
I think it is also a good idea to introduce a special class for Sequence[VirtualNode]
. VirtualCode
for example. with such class, we will be able to avoid pass all vnodes
, vnodes_y
, parents
, etc independently from function to function, but will work with one solid object what will make our life easier.
@m09 do not get my arguing wrong. We just do not have enough time to create good annotation tooling (+debugging +recode everything). So I think about a good intermediate solution that is not hard to implement but have almost all good properties we want.
Don't worry, I don't get it wrong :relaxed: It's just that we should probably close this issue and open a new one with different refactoring ideas since those won't be close to implementing annotations. We should keep this pattern in mind for our next project though.
Renaming some of the attributes and adding a few new ones to be more explicit is indeed a way forward :+1:
Ok, we need @vmarkovtsev to have a decision here.
Actually approach I described in https://github.com/src-d/style-analyzer/issues/521#issuecomment-465242981 perfectly consistent with issue description so I think we can keep it, maybe with renaming.
It's not perfectly consistent since it doesn't implement the pattern that we wanted to implement. TBH I'm not even sure it helps compared to current codebase. The point of having separate annotations is that a given piece of code only considers a given type of annotation and has no filtering/validation to do. If we don't have that we miss the gist of the pattern.
ok, I see. let's discuss it tomorrow at the meeting.
Discussed at the meeting. Actions:
We have a quick call with @m09 and he ok with this structure. Hugo, any additional comments are welcome. to_vnodes
method should be removed as soon as we rewrite everything with annotations.
I start to code proper implementation for these classes and after that, I begin style-analyzer code migration.
We should use correct names for classes. I am not sure about RawData
and AnnotatedData
.
Next steps:
@vmarkovtsev @m09 I suggest to close this issue and open another one when we need to continue code refactoring with annotation
Inspired by UIMA. We never remove annotations, only add.
...