René's URL Explorer Experiment


Title: Analysis of some mypy adoption problems and the introduction of type: ignore comments · Issue #652 · canopen-python/canopen · GitHub

Open Graph Title: Analysis of some mypy adoption problems and the introduction of type: ignore comments · Issue #652 · canopen-python/canopen

X Title: Analysis of some mypy adoption problems and the introduction of type: ignore comments · Issue #652 · canopen-python/canopen

Description: Analysis of type: ignore Comments and Potential Fixes This issue explains why multiple type: ignore comments were introduced during the mypy adoption (PRs #647, #649, #650, #651) and what API changes or refactorings would be needed to el...

Open Graph Description: Analysis of type: ignore Comments and Potential Fixes This issue explains why multiple type: ignore comments were introduced during the mypy adoption (PRs #647, #649, #650, #651) and what API chang...

X Description: Analysis of type: ignore Comments and Potential Fixes This issue explains why multiple type: ignore comments were introduced during the mypy adoption (PRs #647, #649, #650, #651) and what API chang...

Opengraph URL: https://github.com/canopen-python/canopen/issues/652

X: @github

direct link

Domain: github.com


Hey, it has json ld scripts:
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Analysis of some mypy adoption problems and the introduction of type: ignore comments","articleBody":"# Analysis of `type: ignore` Comments and Potential Fixes\n\nThis issue explains why multiple `type: ignore` comments were introduced\nduring the mypy adoption (PRs #647, #649, #650, #651) and what API changes\nor refactorings would be needed to eliminate them.\n\n## Root Cause: Dynamically Typed Value Pipeline\n\nThe core issue is that `ODVariable.decode_raw()` returns\n`Union[int, float, str, bytes, bytearray]` and `encode_raw()` accepts the same\nunion. This \"wide\" union type propagates through the entire `Variable` property\nchain (`raw`, `phys`, `desc`, `bits`), making it impossible for mypy to know\nwhich concrete type is in play at any given point.\n\nIn practice, the concrete type is determined by `ODVariable.data_type` at\nruntime — but mypy cannot track this correlation.\n\n---\n\n## Per-File Analysis\n\n### `canopen/objectdictionary/__init__.py` (7 ignores)\n\n#### 1. `encode_raw()` — string operations on Union type\n```python\nreturn value.encode(\"ascii\")   # type: ignore[union-attr]\nreturn value.encode(\"utf_16_le\")  # type: ignore[union-attr]\nreturn bytes(value)            # type: ignore[arg-type]\n```\n**Why:** After the `isinstance(value, (bytes, bytearray))` guard, `value` is\nnarrowed to `Union[int, float, str]`. The `VISIBLE_STRING` / `UNICODE_STRING`\nbranches assume `value` is `str`, but mypy doesn't correlate `data_type` with\nthe value type.\n\n**Fix:** Use `@overload` to create typed variants of `encode_raw()`:\n```python\n@overload\ndef encode_raw(self, value: str) -\u003e bytes: ...\n@overload\ndef encode_raw(self, value: int) -\u003e bytes: ...\n@overload\ndef encode_raw(self, value: bytes) -\u003e bytes: ...\n```\nOr split into `encode_string()`, `encode_number()`, `encode_bytes()`.\nThis is a significant API change.\n\n#### 2. `encode_raw()` — min/max comparisons\n```python\nif self.min is not None and value \u003c self.min:  # type: ignore[operator]\nif self.max is not None and value \u003e self.max:  # type: ignore[operator]\n```\n**Why:** At this point `value` is `Union[int, float, str]`, but the comparison\nonly makes sense for numbers. The `str` type doesn't support `\u003c`/`\u003e` with `int`.\n\n**Fix:** Add an explicit `isinstance(value, (int, float))` guard before the\ncomparisons. This would be safe and backward-compatible:\n```python\nif isinstance(value, (int, float)):\n    if self.min is not None and value \u003c self.min:\n        ...\n```\n\n#### 3. `encode_phys()` — division and return\n```python\nvalue = int(round(value / self.factor))  # type: ignore[operator]\nreturn value  # type: ignore[return-value]\n```\n**Why:** `value` is `Union[int, bool, float, str, bytes]` but division only\nworks on numbers. The `return value` may still be `str` or `bytes` when\n`data_type` is not in `INTEGER_TYPES`.\n\n**Fix:** Change `encode_phys()` to accept and return `Union[int, float]` only.\nCallers that pass `str`/`bytes` would need to go through `encode_raw()` instead.\nThis is an API change.\n\n#### 4. File handling in `export_od()` / `import_od()`\n```python\ndest.close()     # type: ignore[union-attr]\nfilename = source.name  # type: ignore[union-attr]\n```\n**Why:** `dest` is `Union[str, TextIO, None]` — after the `opened_here` guard,\nit's always `TextIO`. `source` after `hasattr(source, \"read\")` is `TextIO`.\n\n**Fix:** Introduce a local variable with explicit narrowing:\n```python\nif opened_here:\n    assert isinstance(dest, TextIO)\n    dest.close()\n```\nOr restructure using a context manager pattern. This is a safe, non-breaking\nchange but requires some refactoring of the function flow.\n\n---\n\n### `canopen/variable.py` (6 ignores)\n\n#### 5. `raw` property flows into type-specific methods\n```python\nvalue = self.od.decode_phys(self.raw)   # type: ignore[arg-type]\nvalue = self.od.decode_desc(self.raw)   # type: ignore[arg-type]\nself.raw = self.od.encode_desc(desc)    # type: ignore[assignment]\n```\n**Why:** `self.raw` returns `Union[int, bool, float, str, bytes]`, but\n`decode_phys()` expects `int` and `decode_desc()` expects `int`.\n\n**Fix:** This is a consequence of the root cause. Possible approaches:\n1. Make `decode_phys()` / `decode_desc()` accept the full Union and do\n   runtime validation internally.\n2. Introduce a `Variable.raw_int` property that returns `int` and use it\n   in `phys` and `desc` properties.\n3. Use `Generic` types parameterized by `data_type` — very complex.\n\n#### 6. `Bits` class raw type\n```python\nreturn self.variable.od.decode_bits(self.raw, ...)  # type: ignore[arg-type]\nself.raw = self.variable.od.encode_bits(self.raw, ...)  # type: ignore[arg-type]\n```\n**Why:** `Bits.raw` is `Union[int, bool, float, str, bytes]` (copied from\n`Variable.raw`), but `decode_bits()` / `encode_bits()` expect `int`.\n\n**Fix:** `Bits` should store `raw` as `int` and cast on read/write:\n```python\ndef read(self):\n    raw = self.variable.raw\n    assert isinstance(raw, int)\n    self.raw: int = raw\n```\n\n#### 7. `write()` method — desc assignment\n```python\nself.desc = value  # type: ignore[assignment]\n```\n**Why:** `value` is `Union[int, bool, float, str, bytes]` but `desc` setter\nexpects `str`.\n\n**Fix:** Add type narrowing or runtime validation in `write()`:\n```python\nelif fmt == \"desc\":\n    if not isinstance(value, str):\n        raise TypeError(\"desc format requires str value\")\n    self.desc = value\n```\n\n---\n\n### `canopen/network.py` (2 ignores)\n\n#### 8. Node construction with Optional OD\n```python\nnode = RemoteNode(node, object_dictionary)  # type: ignore[arg-type]\nnode = LocalNode(node, object_dictionary)   # type: ignore[arg-type]\n```\n**Why:** `object_dictionary` is `Union[str, ObjectDictionary, None]`, but\nnode constructors don't accept `None`.\n\n**Fix:** Make `RemoteNode` / `LocalNode` constructors accept `None` for\n`object_dictionary` and create an empty `ObjectDictionary()` internally.\nOr add proper validation in `add_node()` / `create_node()`:\n```python\nif object_dictionary is None:\n    raise ValueError(\"object_dictionary is required when node is an int\")\n```\nNote: The current code intentionally allows `None` to flow through — e.g.\nwhen `upload_eds` fails, the node is still created with `None` OD. This is\na design decision that would need discussion.\n\n---\n\n### `canopen/sdo/base.py` (1 ignore)\n\n#### 9. `SdoArray.__len__()` return type\n```python\nreturn self[0].raw  # type: ignore[return-value]\n```\n**Why:** `self[0].raw` is `Union[int, bool, float, str, bytes]` but\n`__len__` must return `int`.\n\n**Fix:** Cast explicitly:\n```python\ndef __len__(self) -\u003e int:\n    value = self[0].raw\n    assert isinstance(value, int)\n    return value\n```\n\n---\n\n## Summary of Required Changes\n\n| Priority | Change | Impact | Breaking? |\n|----------|--------|--------|-----------|\n| Low | `encode_raw()` min/max guards | 2 ignores removed | No |\n| Low | `Bits.raw` as `int` | 2 ignores removed | No |\n| Low | `write()` desc validation | 1 ignore removed | No |\n| Low | `SdoArray.__len__` cast | 1 ignore removed | No |\n| Low | File handling narrowing | 2 ignores removed | No |\n| Medium | `decode_phys`/`decode_desc` accept Union | 3 ignores removed | Minor |\n| Medium | Node constructor accept None | 2 ignores removed | Minor |\n| High | `encode_raw` overloads or split | 3 ignores removed | Yes |\n| High | `encode_phys` type narrowing | 2 ignores removed | Yes |\n\nThe **low priority** changes (10 ignores) can be done without any API changes.\nThe **medium/high priority** changes (8 ignores) require API discussion and\npotentially affect downstream users.\n","author":{"url":"https://github.com/bizfsc","@type":"Person","name":"bizfsc"},"datePublished":"2026-04-29T19:25:22.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":0},"url":"https://github.com/652/canopen/issues/652"}

route-pattern/_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format)
route-controllervoltron_issues_fragments
route-actionissue_layout
fetch-noncev2:3aba9783-a74c-b98f-634e-35f9fffa3f52
current-catalog-service-hash81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114
request-idDE0E:3081D4:4B813B6:63B4EAF:6A5D9BCC
html-safe-nonce69fc017faeb37f7180697aad53fe7b30182d6cd9a2384847a30ee4238f038c46
visitor-payloadeyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJERTBFOjMwODFENDo0QjgxM0I2OjYzQjRFQUY6NkE1RDlCQ0MiLCJ2aXNpdG9yX2lkIjoiMzQ5NzQxODg1OTM3MjYxNTE2IiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0=
visitor-hmac4cbfc028e519442cd220726f88206d9e488e25f7c544a4383ac4eba2a7b41003
hovercard-subject-tagissue:4353131946
github-keyboard-shortcutsrepository,issues,copilot
google-site-verificationApib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I
octolytics-urlhttps://collector.github.com/github/collect
analytics-location///voltron/issues_fragments/issue_layout
fb:app_id1401488693436528
apple-itunes-appapp-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/canopen-python/canopen/652/issue_layout
twitter:imagehttps://opengraph.githubassets.com/5d57b7b2e6b8dc249001b9b0cc327b9f921f601ff84097e4407d6427883a6f42/canopen-python/canopen/issues/652
twitter:cardsummary_large_image
og:imagehttps://opengraph.githubassets.com/5d57b7b2e6b8dc249001b9b0cc327b9f921f601ff84097e4407d6427883a6f42/canopen-python/canopen/issues/652
og:image:altAnalysis of type: ignore Comments and Potential Fixes This issue explains why multiple type: ignore comments were introduced during the mypy adoption (PRs #647, #649, #650, #651) and what API chang...
og:image:width1200
og:image:height600
og:site_nameGitHub
og:typeobject
og:author:usernamebizfsc
hostnamegithub.com
expected-hostnamegithub.com
None5290d7e14309ad1e76106a9c4237bd1041517e83ea182c8ab756752cb0c6940b
turbo-cache-controlno-preview
go-importgithub.com/canopen-python/canopen git https://github.com/canopen-python/canopen.git
octolytics-dimension-user_id200581454
octolytics-dimension-user_logincanopen-python
octolytics-dimension-repository_id68737600
octolytics-dimension-repository_nwocanopen-python/canopen
octolytics-dimension-repository_publictrue
octolytics-dimension-repository_is_forkfalse
octolytics-dimension-repository_network_root_id68737600
octolytics-dimension-repository_network_root_nwocanopen-python/canopen
turbo-body-classeslogged-out env-production page-responsive
disable-turbofalse
browser-stats-urlhttps://api.github.com/_private/browser/stats
browser-errors-urlhttps://api.github.com/_private/browser/errors
release9c975978430e9ad293956f2bbdaf153b1bd84a99
ui-targetfull
theme-color#1e2327
color-schemelight dark

Links:

Skip to contenthttps://github.com/canopen-python/canopen/issues/652#start-of-content
https://github.com/
Sign in https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fcanopen-python%2Fcanopen%2Fissues%2F652
GitHub CopilotWrite better code with AIhttps://github.com/features/copilot
GitHub Copilot appDirect agents from issue to mergehttps://github.com/features/ai/github-app
MCP RegistryNewIntegrate external toolshttps://github.com/mcp
ActionsAutomate any workflowhttps://github.com/features/actions
CodespacesInstant dev environmentshttps://github.com/features/codespaces
IssuesPlan and track workhttps://github.com/features/issues
Code ReviewManage code changeshttps://github.com/features/code-review
GitHub Advanced SecurityFind and fix vulnerabilitieshttps://github.com/security/advanced-security
Code securitySecure your code as you buildhttps://github.com/security/advanced-security/code-security
Secret protectionStop leaks before they starthttps://github.com/security/advanced-security/secret-protection
Why GitHubhttps://github.com/why-github
Documentationhttps://docs.github.com
Bloghttps://github.blog
Changeloghttps://github.blog/changelog
Marketplacehttps://github.com/marketplace
View all featureshttps://github.com/features
Enterpriseshttps://github.com/enterprise
Small and medium teamshttps://github.com/team
Startupshttps://github.com/enterprise/startups
Nonprofitshttps://github.com/solutions/industry/nonprofits
App Modernizationhttps://github.com/solutions/use-case/app-modernization
DevSecOpshttps://github.com/solutions/use-case/devsecops
DevOpshttps://github.com/solutions/use-case/devops
CI/CDhttps://github.com/solutions/use-case/ci-cd
View all use caseshttps://github.com/solutions/use-case
Healthcarehttps://github.com/solutions/industry/healthcare
Financial serviceshttps://github.com/solutions/industry/financial-services
Manufacturinghttps://github.com/solutions/industry/manufacturing
Governmenthttps://github.com/solutions/industry/government
View all industrieshttps://github.com/solutions/industry
View all solutionshttps://github.com/solutions
AIhttps://github.com/resources/articles?topic=ai
Software Developmenthttps://github.com/resources/articles?topic=software-development
DevOpshttps://github.com/resources/articles?topic=devops
Securityhttps://github.com/resources/articles?topic=security
View all topicshttps://github.com/resources/articles
Customer storieshttps://github.com/customer-stories
Events & webinarshttps://github.com/resources/events
Ebooks & reportshttps://github.com/resources/whitepapers
Business insightshttps://github.com/solutions/executive-insights
GitHub Skillshttps://skills.github.com
Documentationhttps://docs.github.com
Customer supporthttps://support.github.com
Community forumhttps://github.com/orgs/community/discussions
Trust centerhttps://github.com/trust-center
Partnershttps://github.com/partners
View all resourceshttps://github.com/resources
GitHub SponsorsFund open source developershttps://github.com/open-source/sponsors
Security Labhttps://securitylab.github.com
Maintainer Communityhttps://maintainers.github.com
Acceleratorhttps://github.com/open-source/accelerator
GitHub Starshttps://stars.github.com
Archive Programhttps://archiveprogram.github.com
Topicshttps://github.com/topics
Trendinghttps://github.com/trending
Collectionshttps://github.com/collections
Enterprise platformAI-powered developer platformhttps://github.com/enterprise
GitHub Advanced SecurityEnterprise-grade security featureshttps://github.com/security/advanced-security
Copilot for BusinessEnterprise-grade AI featureshttps://github.com/features/copilot/copilot-business
Premium SupportEnterprise-grade 24/7 supporthttps://github.com/enterprise/premium-support
Pricinghttps://github.com/pricing
Search syntax tipshttps://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax
documentationhttps://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax
Sign in https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fcanopen-python%2Fcanopen%2Fissues%2F652
Sign up https://github.com/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fvoltron%2Fissues_fragments%2Fissue_layout&source=header-repo&source_repo=canopen-python%2Fcanopen
Reloadhttps://github.com/canopen-python/canopen/issues/652
Reloadhttps://github.com/canopen-python/canopen/issues/652
Reloadhttps://github.com/canopen-python/canopen/issues/652
Please reload this pagehttps://github.com/canopen-python/canopen/issues/652
canopen-python https://github.com/canopen-python
canopenhttps://github.com/canopen-python/canopen
Notifications https://github.com/login?return_to=%2Fcanopen-python%2Fcanopen
Fork 359 https://github.com/login?return_to=%2Fcanopen-python%2Fcanopen
Star 560 https://github.com/login?return_to=%2Fcanopen-python%2Fcanopen
Code https://github.com/canopen-python/canopen
Issues 53 https://github.com/canopen-python/canopen/issues
Pull requests 20 https://github.com/canopen-python/canopen/pulls
Discussions https://github.com/canopen-python/canopen/discussions
Actions https://github.com/canopen-python/canopen/actions
Projects https://github.com/canopen-python/canopen/projects
Security and quality 0 https://github.com/canopen-python/canopen/security
Insights https://github.com/canopen-python/canopen/pulse
Code https://github.com/canopen-python/canopen
Issues https://github.com/canopen-python/canopen/issues
Pull requests https://github.com/canopen-python/canopen/pulls
Discussions https://github.com/canopen-python/canopen/discussions
Actions https://github.com/canopen-python/canopen/actions
Projects https://github.com/canopen-python/canopen/projects
Security and quality https://github.com/canopen-python/canopen/security
Insights https://github.com/canopen-python/canopen/pulse
Analysis of some mypy adoption problems and the introduction of type: ignore commentshttps://github.com/canopen-python/canopen/issues/652#top
https://github.com/bizfsc
bizfschttps://github.com/bizfsc
on Apr 29, 2026https://github.com/canopen-python/canopen/issues/652#issue-4353131946
#647https://github.com/canopen-python/canopen/pull/647
#649https://github.com/canopen-python/canopen/pull/649
#650https://github.com/canopen-python/canopen/pull/650
#651https://github.com/canopen-python/canopen/pull/651
https://github.com
Termshttps://docs.github.com/site-policy/github-terms/github-terms-of-service
Privacyhttps://docs.github.com/site-policy/privacy-policies/github-privacy-statement
Securityhttps://github.com/security
Statushttps://www.githubstatus.com/
Communityhttps://github.community/
Docshttps://docs.github.com/
Contacthttps://support.github.com?tags=dotcom-footer

Viewport: width=device-width


URLs of crawlers that visited me.