Skip to content
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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

sveinse
Copy link
Collaborator

@sveinse sveinse commented Jul 10, 2024

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.

  • Add runtime test for self.network before using the network
  • PeriodicMessageTask.update() don't stop the task unless it's 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 not None

* 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-commenter
Copy link

codecov-commenter commented Jul 10, 2024

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

Attention: Patch coverage is 55.03356% with 67 lines in your changes missing coverage. Please review.

Project coverage is 67.72%. Comparing base (3aa509d) to head (3b66ae5).

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files

Impacted file tree graph

@@            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     
Files Coverage Δ
canopen/node/local.py 85.88% <100.00%> (+0.69%) ⬆️
canopen/node/remote.py 53.08% <100.00%> (ø)
canopen/node/base.py 83.33% <60.00%> (-16.67%) ⬇️
canopen/sdo/base.py 76.59% <57.14%> (-0.83%) ⬇️
canopen/emcy.py 68.42% <20.00%> (-3.41%) ⬇️
canopen/variable.py 53.33% <27.27%> (-2.79%) ⬇️
canopen/network.py 72.88% <50.00%> (-2.42%) ⬇️
canopen/nmt.py 86.71% <37.50%> (-6.52%) ⬇️
canopen/objectdictionary/__init__.py 79.57% <72.54%> (-3.11%) ⬇️
canopen/pdo/base.py 62.87% <33.33%> (-2.10%) ⬇️

@erlend-aasland
Copy link
Contributor

erlend-aasland commented Jul 10, 2024

This is great! Can you separate out all the behavioural changes in separate PRs1 accompanied with tests?

Footnotes

  1. one PR per change in behaviour

@erlend-aasland
Copy link
Contributor

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.

@sveinse
Copy link
Collaborator Author

sveinse commented Jul 10, 2024

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.

@erlend-aasland
Copy link
Contributor

Yes, it is time-consuming, and it requires multiple PRs. IMO it is definitely worth it in the long run.

@acolomb
Copy link
Collaborator

acolomb commented Jul 10, 2024

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 :-)

@sveinse
Copy link
Collaborator Author

sveinse commented Jul 10, 2024

How many pieces must this PR be split into?

@erlend-aasland
Copy link
Contributor

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

  1. For each issue, time is best spent on reaching a consensus for a desired solution before proposing a PR. Anything else is often a waste of reviewer time.

@erlend-aasland
Copy link
Contributor

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.

@erlend-aasland
Copy link
Contributor

erlend-aasland commented Jul 10, 2024

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.

That's fine; this is open source. Let me know if you want me to help out with any of the preliminary tasks.

@sveinse
Copy link
Collaborator Author

sveinse commented Jul 10, 2024

Please go ahead

Copy link
Collaborator

@acolomb acolomb left a 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]:
Copy link
Collaborator

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.

Comment on lines +90 to +91
if self.network is None:
raise RuntimeError("A Network is required")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Superseded by #525?

Comment on lines +13 to +20
# 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 """
Copy link
Collaborator

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Factored out to #534

Comment on lines -149 to +155
:class:`canopen.RemoteNode` or :class:`canopen.LocalNode` object.
:class:`canopen.RemoteNode` object.
Copy link
Collaborator

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Superseded by #534

Comment on lines -341 to +353
self._task.stop()
if self._task is not None:
self._task.stop()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Superseded by #534

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants