-
Notifications
You must be signed in to change notification settings - Fork 197
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
Add mypy support and fixup project to give no errors #512
base: master
Are you sure you want to change the base?
Conversation
* Permissive mypy configuration as starting point * Add minimal type annotations to get no mypy errors * Add runtime test for self.network before using the network * Network.add_node() doesn't accept LocalNode * PeriodicMessageTask.update() don't stop the task unless its running * Variable.desc ensure that the object is int * Variable.read() fail with ValueError unless a valid fmt is used * Variable.write() ensure the description is a string * BaseNode.__init__() fail if no node_id is provided * ObjectDictionary.__getitem__() when splitting "." only return if the object is not an ODVariable * ODRecord.__eq__(), ODArray.__eq__() and ODVariable.__eq__() test type of other before comparing * ODVariable.encode_raw(), .decode_phys(), .encode_phys() add type tests of ensure the input is of correct type * PdoMap various methods: ensure necessary attributes are set
Codecov ReportAttention: Patch coverage is
❗ Your organization needs to install the Codecov GitHub app to enable full functionality. Additional details and impacted files@@ Coverage Diff @@
## master #512 +/- ##
==========================================
- Coverage 68.78% 67.72% -1.06%
==========================================
Files 26 26
Lines 3117 3198 +81
Branches 526 556 +30
==========================================
+ Hits 2144 2166 +22
- Misses 835 878 +43
- Partials 138 154 +16
|
Just to be explicit: in #358 there is an agreement on not mixing type annotations with other types of changes, including bug fixes and behavioural changes. |
Yes, its possible to separate it out. While I see the intentions of separating out the type annotations from the behavioral changes, I'm not sure I like where this is going. Type annotating an existing project and behavioral changes goes iteratively hand in hand. Annotate something and an error pop up. Fix an error, and another annotation error appears. And so on. The sheer volume of PRs per behavioral change will be too great for me to handle. I won't be able to dedicate that much time to this. This PR is just a minor extract of what is to come ....at least if we're aiming for strict mypy. So perhaps we need to go back to #358 and really discuss what ambitions we have for type annotations to begin with. |
Yes, it is time-consuming, and it requires multiple PRs. IMO it is definitely worth it in the long run. |
Yes, please let's first reach consensus on that discussion about the ambitions and the roadmap. Will chime in there soon, but I can't afford looking at it every day right now. Please have a little patience :-) |
How many pieces must this PR be split into? |
From a quick glance at these changes, ISTM something like this is a sane approach1:
I might have missed some; there's a lot of different types of changes here. Footnotes
|
When the above preparations have landed, it will be time to introduce annotations, which should then be a much easier PR to review, as it would do only one thing: introduce annotations. |
That's fine; this is open source. Let me know if you want me to help out with any of the preliminary tasks. |
Please go ahead |
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.
Gradually working through this. I had some of these fixes made locally as well, so trying to reconcile the bits and factor out into smaller PRs. I haven't checked all touched files yet, will do so as time permits.
This should be merged with master, especially after #525 is sanctioned and merged. It will reduce the diff a lot, making it easier to reason about.
@@ -52,7 +53,7 @@ def reset(self): | |||
|
|||
def wait( | |||
self, emcy_code: Optional[int] = None, timeout: float = 10 | |||
) -> "EmcyError": | |||
) -> Optional[EmcyError]: |
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.
Cosmetics, let's keep these separate from the mypy error fixing. There are lots of things like this to do, but better to keep a PR focused and revisit such cleanups later. IMHO.
if self.network is None: | ||
raise RuntimeError("A Network is required") |
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.
Superseded by #525?
# Type checkers don't like this conditional logic, so it is only run when | ||
# not type checking | ||
if not TYPE_CHECKING: | ||
# Do not fail if python-can is not installed | ||
can = None | ||
CanError = Exception | ||
class Listener: | ||
""" Dummy listener """ |
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.
Fixed in master, I believe?
@@ -45,7 +51,7 @@ def __init__(self, bus: Optional[can.BusABC] = None): | |||
#: List of :class:`can.Listener` objects. | |||
#: Includes at least MessageListener. | |||
self.listeners = [MessageListener(self)] | |||
self.notifier = None | |||
self.notifier: Optional[can.Notifier] = None |
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.
Factored out to #534
:class:`canopen.RemoteNode` or :class:`canopen.LocalNode` object. | ||
:class:`canopen.RemoteNode` object. |
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 think what is wrong here is the return type annotation. It's perfectly alright to add an existing LocalNode
to a network, isn't it?
@@ -335,10 +345,12 @@ def update(self, data: bytes) -> None: | |||
old_data = self.msg.data | |||
self.msg.data = new_data | |||
if hasattr(self._task, "modify_data"): | |||
assert self._task is not None # This will never be None, but mypy needs this |
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.
Superseded by #534
self._task.stop() | ||
if self._task is not None: | ||
self._task.stop() |
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.
Superseded by #534
With reference to #358 this PR is the starting point of adding typing with mypy support in canopen. It sets a recommended permissive starting point for mypy and it contains the absolute minimal code change required to pass all mypy errors.
It does contain behavioral changes that were required to pass the tests. The majority of these fixes are more graceful handling of errors that would occur anyways, so the behavioral change is what error they produce.
self.network
before using the networkPeriodicMessageTask.update()
don't stop the task unless it's runningVariable.desc
ensure that the object is intVariable.read()
fail with ValueError unless a valid fmt is usedVariable.write()
ensure the description is a stringBaseNode.__init__()
fail if no node_id is providedObjectDictionary.__getitem__()
when splitting "." only return if the object is not anODVariable
ODRecord.__eq__()
,ODArray.__eq__()
andODVariable.__eq__()
test type of other before comparingODVariable.encode_raw()
,.decode_phys()
,.encode_phys()
add type tests of ensure the input is of correct typePdoMap
various methods: ensure necessary attributes are not None