JSON-Rohbericht maschinenlesbar
{
"data": {
"repo": {
"topics": [
"can",
"canopen",
"python"
],
"is_fork": false,
"size_kb": 854,
"has_wiki": true,
"homepage": "http://canopen.readthedocs.io/",
"languages": {
"Shell": 903,
"Python": 393830
},
"pushed_at": "2026-06-28T16:01:52Z",
"created_at": "2016-09-20T17:42:18Z",
"owner_type": "Organization",
"updated_at": "2026-07-21T02:00:12Z",
"description": "CANopen for Python",
"is_archived": false,
"is_disabled": false,
"license_spdx": "MIT",
"default_branch": "master",
"license_spdx_raw": "MIT",
"primary_language": "Python",
"significant_languages": [
"Python"
]
},
"owner": {
"blog": null,
"name": null,
"type": "Organization",
"login": "canopen-python",
"company": null,
"location": null,
"followers": 2,
"avatar_url": "https://avatars.githubusercontent.com/u/200581454?v=4",
"created_at": "2025-02-24T08:54:04Z",
"is_verified": null,
"public_repos": 1,
"account_age_days": 517
},
"license": {
"state": "standard",
"spdx_id": "MIT",
"raw_spdx": "MIT",
"file_present": true,
"scorecard_found": true,
"profile_has_license": true
},
"activity": {
"releases": [
{
"tag": "v2.4.1",
"kind": "patch",
"published_at": "2025-08-05T11:11:01Z"
},
{
"tag": "v2.4.0",
"kind": "minor",
"published_at": "2025-08-05T10:03:28Z"
},
{
"tag": "v2.3.0",
"kind": "minor",
"published_at": "2024-06-13T20:35:27Z"
},
{
"tag": "v2.2.0",
"kind": "minor",
"published_at": "2023-10-09T18:44:08Z"
},
{
"tag": "v2.1.0",
"kind": "minor",
"published_at": "2023-01-03T14:27:22Z"
},
{
"tag": "v2.0.0",
"kind": "major",
"published_at": "2022-02-12T11:40:27Z"
},
{
"tag": "v1.2.1",
"kind": "patch",
"published_at": "2021-02-20T09:02:35Z"
},
{
"tag": "v1.2.0",
"kind": "minor",
"published_at": "2020-11-09T19:25:02Z"
},
{
"tag": "v1.1.0",
"kind": "minor",
"published_at": "2020-03-05T19:45:23Z"
},
{
"tag": "v1.0.0",
"kind": "major",
"published_at": "2019-10-03T18:33:06Z"
},
{
"tag": "v0.9.0",
"kind": "minor",
"published_at": "2019-05-04T11:43:00Z"
},
{
"tag": "v0.8.1",
"kind": "patch",
"published_at": "2019-03-06T12:53:29Z"
},
{
"tag": "v0.8.0",
"kind": "minor",
"published_at": "2018-11-21T21:06:14Z"
},
{
"tag": "v0.7.1",
"kind": "patch",
"published_at": "2018-09-03T17:51:44Z"
},
{
"tag": "v0.7.0",
"kind": "minor",
"published_at": "2018-08-05T18:32:13Z"
},
{
"tag": "v0.6.0",
"kind": "minor",
"published_at": "2018-03-12T18:41:13Z"
},
{
"tag": "v0.5.1",
"kind": "patch",
"published_at": "2017-10-19T18:07:03Z"
},
{
"tag": "v0.5.0",
"kind": "minor",
"published_at": "2017-09-27T17:40:21Z"
},
{
"tag": "v0.4.0",
"kind": "minor",
"published_at": "2017-01-13T11:48:46Z"
},
{
"tag": "v0.3.0",
"kind": "minor",
"published_at": "2016-11-13T19:13:48Z"
},
{
"tag": "v0.3.0.dev5",
"kind": "other",
"published_at": "2016-09-26T06:52:37Z"
},
{
"tag": "v0.3.0.dev4",
"kind": "other",
"published_at": "2016-09-26T06:53:55Z"
}
],
"recent_commits": [
{
"oid": "14b3336bc973023e82325426adf6d71046ebcf55",
"body": "- Fix bug where an exception in one EMCY callback would abort all\n subsequent callbacks; now logs the exception and continues\n- Add test for callback exception handling (verifies fix)\n- Add tests for EmcyConsumer initialization, multiple callback order,\n error reset variants, wait timeout edge cas\n[…]\nhelper function, including the emcy_received condition\nlock to avoid race conditions.\n\nCo-authored-by: André Colomb <src@andre.colomb.de>\nCo-authored-by: Frieder Schüler <frieder.schueler@bizerba.com>",
"is_bot": false,
"headline": "emcy: Catch callback exceptions and expand test coverage (#659)",
"author_name": "Frieder Schüler",
"author_login": "friederschueler",
"committed_at": "2026-06-28T15:28:19Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "3b57fed8d3e5fd54b9fca2f6ebe344f346952cb3",
"body": "When importing an EDS/DCF file, any key that is not part of the CiA 306\nstandard field set is now collected into a custom_options dict on the object.\nThis allows applications to round-trip vendor-specific metadata without\nlosing it.\n\n* Use getattr/setattr instead of __dict__ access for array entry attributes.\n\nCo-authored-by: FedericoSpada <FedericoSpada@users.noreply.github.com>\nCo-authored-by: André Colomb <src@andre.colomb.de>",
"is_bot": false,
"headline": "eds: Support custom_options for unknown EDS fields (#653)",
"author_name": "Frieder Schüler",
"author_login": "bizfsc",
"committed_at": "2026-06-27T21:25:29Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "2a4544aa4e960a373f35ffe5921bde931be2410e",
"body": "…(#658)\n\n* eds: fix LowLimit/HighLimit parsing for all signed integer types\n\n- Reimplement _calc_bit_length() (only handled INTEGER8/16/32/64)\n using the same structure types used in ODVariable.\n- Log a warning instead of silently ignoring invalid limit values\n- Add EDS test entries in sample.eds f\n[…]\n\n* Renamed _convert_variable → _decode_from_eds and _revert_variable → _encode_to_eds\n\nCo-authored-by: André Colomb <src@andre.colomb.de>\nCo-authored-by: Frieder Schüler <frieder.schueler@bizerba.com>",
"is_bot": false,
"headline": "eds: Fix signed integer limit parsing for all bit widths, log errors …",
"author_name": "Frieder Schüler",
"author_login": "friederschueler",
"committed_at": "2026-06-27T20:44:59Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "ea0efa4db5ddf8266d7acf1b64ce41d7534e944a",
"body": "The NMT state transition from INITIALISING to PRE-OPERATIONAL causes\nthe heartbeat to start automatically if the parameter is non-zero in\nobject 0x1017:00. This check however raises an exception if the\nobject does not even exist.\n\nCover this state in tests and explicitly ignore a KeyError when asking\nfor the object.",
"is_bot": false,
"headline": "nmt: Fix NmtSlave on a node without a heartbeat producer time. (#672)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2026-06-24T08:35:07Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "9aab2d430034ba4fbc74325890bdf29db8cd68ae",
"body": "* Correct type handling in decode_bits() and encode_bits().\n\nContrary to the existing type hints, the methods can handle strings\nfor lookup as well as an arbitrary collection of integer bit offsets.\nCorrect the hints to reflect that and check at runtime to only lookup\nfrom strings.\n\nCorrect return t\n[…]\ncted types for indexing the Bits.\n\n* Test non-string keys for bit access.\n\n* Fix slice-based access without step, useful exception without stop.\n\n* Fix slice-based access without start, cover in test.",
"is_bot": false,
"headline": "Correct type handling for bitwise variable access (#663)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2026-05-22T12:27:36Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "6688d6399279609f2e4b4cee2b5034f0291b32b6",
"body": "Use a simple integer zero as dummy value for \"unset\" in instances.\nThis fixes some type checker errors. Add type hints for functions\ninvolving those values.\n\nFix test coverage for unset and unknown data type.",
"is_bot": false,
"headline": "Clarify ODVariable.data_type cannot be None (#666)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2026-05-22T11:19:51Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "fc5621bf0bb4f2031c835bc60ec10579499a5b19",
"body": "* variable: Value description in log message only for integers.\n\n* Raise TypeError when requesting value description of non-int data.\n\nAccording to the type hints, the mapping mechanism was only ever\nintended for integers (emulating an enumeration). Avoids a later\nDictionaryError for the missing ke\n[…]\nStyle fix (black).\n\nBetter overview if not on one line.\n\n* Avoid else after raise.\n\nSimplify by using the walrus operator.\n\n* Extend test coverage for ODVariable.\n\n* Extend test coverage for Variable.",
"is_bot": false,
"headline": "variable: Fix type-related issues with desc property (#667)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2026-05-22T09:33:28Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "1d6bd8d1f55593e16cc98dc3662c0e6f9a9df39a",
"body": "* Fix too narrow type annotations in emcy and network.\n* Return None explicitly where return type expects it.\n* Annotate ODVariable and ODArray parent attribute as for ODRecord.\n\nCo-authored-by: Frieder Schüler <frieder.schueler@bizerba.com>",
"is_bot": false,
"headline": "Fix some wrong type hints and mypy errors (#664)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2026-05-12T23:06:07Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "62cf726b59d3dd22810921f367170897a64f7216",
"body": "- State402 enum and state decoding from statusword\n- Command word generation for state transitions\n- Next-state calculation through the state machine\n- Operation mode switching and reading\n- TPDO callback handling for statusword updates\n- Lookup table consistency checks\n- Full state machine transitions simulating drive controlword /\n statusword exchange\n\nCo-authored-by: André Colomb <src@andre.colomb.de>",
"is_bot": false,
"headline": "Add unit tests for CiA 402 profile (#644)",
"author_name": "Frieder Schüler",
"author_login": "bizfsc",
"committed_at": "2026-05-12T18:43:06Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "7f8347f40e6794b3b3240436df3672733d691a79",
"body": "* network: Avoid notifier re-creating in connect function\n\n* test_network: Add regression tests for notifier lifecycle\n\nAdd tests to verify that:\n- connect() does not recreate an existing notifier\n- disconnect() releases the notifier\n- disconnect() releases the notifier even when check() raises\n\nCo-authored-by: Frieder Schüler <frieder.schueler@bizerba.com>",
"is_bot": false,
"headline": "network: Avoid notifier re-creating in connect function (#621)",
"author_name": "xndcn",
"author_login": "xndcn",
"committed_at": "2026-05-08T12:16:32Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "a4e61d7715b571c186cbe3f2a5d9de5ec964f88a",
"body": "* eds: Centralize object type logic in build_variable.\n\nPass object type to build_variable() instead of the boolean is_domain\nparameter. Add type hints to aid in validating all call sites have\nbeen adjusted.\n\n* Remove unnecessary duplicate lookup from ConfigParser.\n\n* Fix mypy errors in build_varia\n[…]\nsumed\nonly None type) on the OD variable classes, to accept strings from\neds.\n\nSilence mypy in import_eds() for a custom attribute addition.\n\n* Respect line-length in build_variable + small style fix.",
"is_bot": false,
"headline": "eds: Style and type fixes around build_variable() (#661)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2026-05-07T12:34:49Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "929f4d40e2a85b3498bcc850ccc8eb86a547e19b",
"body": "Co-authored-by: Christian Sandberg <christiansandberg@users.noreply.github.com>\nCo-authored-by: André Colomb <src@andre.colomb.de>",
"is_bot": false,
"headline": "doc: Should check unconfigured state before running fastscan (#290)",
"author_name": "Youngmin Kim",
"author_login": "ymkim92",
"committed_at": "2026-05-07T10:39:30Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "9dbb9fd909ac64fd2075cfb1b09412ae9420ce27",
"body": "The BaseNode.id attribute can be initialized either from the given\nnode_id parameter, or from the Object Dictionary if zero was passed.\nThe None value as mentioned in the docstring actually violates the\ntype hint, so drop that suggestion. Make sure the inferred type of\nthe self.id attribute does no\n[…]\n docstring for LocalNode as well, to document the expected\nnode_id \"invalid\" value. And because there was none at all before,\ninclude a bit of functional description to set the expectations\nstraight.",
"is_bot": false,
"headline": "Make sure any BaseNode always has a valid Node ID (#657)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2026-05-07T09:36:20Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "35f4b7fbfdf3623931459203784ee0634026ac14",
"body": "Line-length 96 is a compomise because 80 is problematic with existing\ncode, but we want a bit of headroom for the next common limit of 100.\n\nString \"normalization\" is complete noise in most cases, that could be\nenabled again after some general cleanup.\n\nMake sure that ruff agrees with the previously used import ordering\nstyle from isort.",
"is_bot": false,
"headline": "Add some basic rules for ruff linter and black formatting (#660)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2026-05-07T07:54:43Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "5dd8e6f723932e4dcdb02a8d32412525cbfc3ad4",
"body": "Fix encode_phys precision loss for int64 with factor=1",
"is_bot": false,
"headline": "Merge pull request #648 from bizfsc/fix/encode-phys-int64-precision",
"author_name": "Frieder Schüler",
"author_login": "friederschueler",
"committed_at": "2026-05-06T20:08:26Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "b73daa6f9efb0e9799ab48baba2869a16e122d66",
"body": "Memorized DOMAIN ObjectType for a proper export",
"is_bot": false,
"headline": "Merge pull request #655 from FedericoSpada/export_domain_objects",
"author_name": "Frieder Schüler",
"author_login": "friederschueler",
"committed_at": "2026-05-06T20:04:51Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "aaa495e70f23206aafcadd38b73643de6dbafff8",
"body": null,
"is_bot": false,
"headline": "Small changes requested by the maintainers",
"author_name": "Federico Spada",
"author_login": "FedericoSpada",
"committed_at": "2026-05-06T19:50:14Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "1eba930eb9ba9b49d4aa5bfc8bf6b5b699a9aaf1",
"body": null,
"is_bot": false,
"headline": "Add test for float 1.0 factor promoting to float type.",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2026-05-06T09:26:39Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "43ae8926e48528ab3b44a66bb339d7a717514dd0",
"body": null,
"is_bot": false,
"headline": "Reword docstrings to state facts, not changes.",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2026-05-06T09:21:59Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c05b49d0cd76830df4a474645d7570db976340f1",
"body": "* Add unit tests for Variable class.\n* Raise ValueError on invalid fmt argument.\n\nCo-authored-by: Frieder Schüler <frieder.schueler@bizerba.com>",
"is_bot": false,
"headline": "Add unit tests for Variable class (#656)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2026-05-05T20:29:17Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "473fbd7356a4cbf5f773d3c418413c1f6d76a66b",
"body": "Change method signatures to accept 'object' as required by the\nLiskov substitution principle (Mapping/Container/object supertypes).\n\nFor __eq__, return NotImplemented for incompatible types instead of\nraising AttributeError. For __contains__ in SdoArray, add isinstance\ncheck to handle non-int arguments gracefully.",
"is_bot": false,
"headline": "Fix __contains__ and __eq__ override signatures (#649)",
"author_name": "Frieder Schüler",
"author_login": "bizfsc",
"committed_at": "2026-05-05T19:32:56Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "67bed3c3ad1e31dd292fd3ebb25130adaf255e5c",
"body": "- Replace List[int] with list[int] (PEP 585, supported since Python 3.9)\n- Add type annotations for untyped variables (var-annotated)\n- Ignore typing on import for 'canmatrix':\nThis should have the advantage of affecting various type checkers, not\nonly mypy. When the upstream canmatrix package does support type\nhints (work is underway), this place is likely to get noticed and the\ncomment removed.\n\nCo-authored-by: André Colomb <src@andre.colomb.de>",
"is_bot": false,
"headline": "Fix trivial mypy errors (#647)",
"author_name": "Frieder Schüler",
"author_login": "bizfsc",
"committed_at": "2026-05-05T19:19:24Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "f1c01e45ce4227aa9360096ad7fa9708d7802a7e",
"body": "Changed to save whether a Variable has ObjectType==DOMAIN while importing an EDS, so that it is possible to export it unchanged.",
"is_bot": false,
"headline": "Implemented the management for DOMAIN Objects",
"author_name": "Federico Spada",
"author_login": "FedericoSpada",
"committed_at": "2026-05-04T20:44:40Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "2133067bf03691cefc6b9cc41a90218424edd70c",
"body": "Skip the float division in encode_phys when factor is 1 (the default).\nPreviously, `value /= self.factor` always performed float division,\ncausing up to 10 bits of precision loss for large int64 values\n(e.g. 0x55554444AAAABBBB became 0x55554444AAAABC00).\n\nUse `!= 1` for the comparison since we are c\n[…]\nthe original bug)\n- int type preservation with factor=1\n- rounding with integer factor != 1 (existing behavior preserved)\n- float factor (existing behavior preserved)\n\nBased on the discussion in #611.",
"is_bot": false,
"headline": "Fix encode_phys precision loss for int64 with factor=1",
"author_name": "Frieder Schüler",
"author_login": "bizfsc",
"committed_at": "2026-04-29T11:59:25Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "4e789fe6627b2bd5e4f0133bdff883ea84e60200",
"body": "- Switch state global/selective commands\n- Node ID and bit timing configuration\n- Activate bit timing and store configuration\n- LSS address inquiry (vendor/product/revision/serial)\n- Timeout and error handling\n- Fast scan protocol\n- Obsolete method aliases\n- LssError exception\n\nCo-authored-by: André Colomb <src@andre.colomb.de>",
"is_bot": false,
"headline": "Add unit tests for LSS master (#643)",
"author_name": "Frieder Schüler",
"author_login": "bizfsc",
"committed_at": "2026-04-29T08:26:06Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "80c6da32e5d79a636a18475d1bfe49d23c0cb827",
"body": "Also use comparison instead of range() for a minor performance\nimprovement.",
"is_bot": false,
"headline": "eds: Fix typo in manufacturer_idices → manufacturer_indices (#642)",
"author_name": "Frieder Schüler",
"author_login": "bizfsc",
"committed_at": "2026-04-29T06:04:49Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "2be54f3e2827de6488ef2c642aac35cfe068ff93",
"body": null,
"is_bot": false,
"headline": "Mention ValueError cause in transmit() docstring.",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2026-04-23T21:09:12Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "baba4a94482fbd71c26c7a9ee4afd2aa2b4c2607",
"body": null,
"is_bot": false,
"headline": "Mention ValueError cause in docstring.",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2026-04-23T21:05:43Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "fe494bba8f2365179308e839192f9fb28ad04ef2",
"body": null,
"is_bot": false,
"headline": "pdo: Handle None cob_id correctly in __repr__ (#640)",
"author_name": "Yash Chaudhary",
"author_login": "stoicfeats",
"committed_at": "2026-04-23T21:05:15Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "cb1622a931f391fcf6bf3f68718fc48be8efcfc8",
"body": "The fallback behavior in PdoMaps.__getitem__() tried looking up by\nparameter record index even when no com_offset or map_offset was\ndefined. This caused a possibly wrong PdoMap object to be\nreturned. For example with 0x15FF, the lookup for 0x1600 would\nsucceed.\n\nAdd a test to make sure that an nonexistant parameter record index for\n0x15FF actually does raise a KeyError. Skip the fallback lookup by\nparameter index if no record offsets are defined.",
"is_bot": false,
"headline": "pdo: Fix wrongfully returned object for missing index (#636)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2026-04-07T11:00:15Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "9685880e19f348c0d282ff33296b6902104ef96c",
"body": "The extended access patterns by communication parameter record index\nonly work for TPDOs. The lower bound check on the number range\nexcludes anything below 0x1600, thus RPDO communication parameters do\nnot work.\n\nExtend test to cover access via 0x1400 and 0x1600 yielding the same\nobject.",
"is_bot": false,
"headline": "pdo: Fix lower bound check in access by index (#628)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2026-03-12T19:07:40Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "db9110e9cd0545b2aa7426243f169d0f863a7640",
"body": "Move the ARR, DOMAIN, RECORD, VAR constant definitions to their own\n\"objectcodes\" module and add missing ones from the standard. Rename\nARR to ARRAY. Add a reverse lookup function code2str() for\nconvenience.\n\nAdjust all uses within the eds module.",
"is_bot": false,
"headline": "eds: Factor out object code constants.",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2025-10-26T20:41:26Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "7c6dee5a1b040a3de8ea18263f7d8dad9120e076",
"body": "Import the OD* classes directly, as the repeatedly used\nobjectdictionary.OD* naming is redundant.",
"is_bot": false,
"headline": "eds: Refactor OD classes imports.",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2025-10-26T20:22:35Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "e58a65318eee4b0e3befb1a9f244be7173a0960d",
"body": "Fix mixed up mapping parameter record index numbers in class PDO. The\naccess by this index number mixed up the two number ranges. The CiA\n301 standard defines:\n\n* Object 1600h to 17FFh: RPDO mapping parameter\n* Object 1A00h to 1BFFh: TPDO mapping parameter\n\nThe test was also wrong, because it added\n[…]\nwell,\ninstead of overwriting it with a custom dictionary. To allow this\ndummy usage, adjust its constructor to skip generating entries. Add\nsome explanation why relative indices cannot be used here.",
"is_bot": false,
"headline": "Fix and extend PDO selection by record IDs (fixes #607) (#613)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2025-09-22T19:39:22Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "593f0621f41ce3f94c09322ca8414c7e649b128e",
"body": "* Raise KeyError for zero index.\n* Properly test different access type return values.\n\nAugment test_pdo_getitem() to not only check the mapped object values.\nWhat's more important is the type of object returned, and whether it\nis the correct object (identical to other access method results).",
"is_bot": false,
"headline": "PdoBase: Forbid getting item with zero index (fixes #581) (#609)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2025-09-22T19:18:56Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "54ac5c2d5ca0324c528f2b5356be06be2b3def1f",
"body": "Reduce the amount of code within the critical section holding the\nlock. Unpack and log first. Update timestamp and state while locked\nand notify condition waiters. Invoke callbacks after releasing the\nlock.",
"is_bot": false,
"headline": "NmtMaster: Delay heartbeat callbacks after state update (#601)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2025-09-08T21:13:57Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "829f4628634f1a6f169edfe14254b6aa54a398f5",
"body": "…) (#600)\n\nRaise a RuntimeError when a SYNC transmission task has previously been\nstarted. Make sure to clear the internal _task attribute when\nstopping, to make the check work.",
"is_bot": false,
"headline": "SyncProducer: Prevent from starting another periodic task (fixes #582…",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2025-09-08T21:07:39Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "be9c56d4fe696bb3d2cd63834b3a1245773976b0",
"body": "* Disregard OD variable description in SdoClient.upload().\n\nThe upload method should behave as a raw producer of byte data.\nInterpretation of the returned data based on the Object Dictionary is\nthe responsibility of the SdoVariable class, which delegates to the\ngeneric ODVariable decoding functions.\n[…]\nonly incompatible change.\n\n* Test expedited upload with 8 byte padded frame.\n\nThe existing test response is actually wrong, since the standard\nmandates four bytes \"data\" regardless of the other flags.",
"is_bot": false,
"headline": "Disregard OD variable description in SdoClient.upload() (#592)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2025-08-18T20:51:27Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "1582ba5b0d5f8f7365d86432ceaca5137b4f4670",
"body": "* Update license metadata.\n\nAvoid deprecation warnings with modern setuptools, which will stop\nsupporting a table as the project.license option in pyproject.toml\nsoon. Require a modern version >= 77 to make use of the alternative\noptions.\n\n* Modernize Dict and List type annotations.\n\nSince Python 3.9 we can use the built-in types directly.\n\n* Simplify type import.\n\n* Modernize Iterator type annotations.\n\nSince Python 3.9 the collections.abc module can be used directly for\ntyping.",
"is_bot": false,
"headline": "build: Bump minimum Python version to 3.9 (fixes #585) (#610)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2025-08-18T20:42:24Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "317fd3d61513b33ddb1aaf8a3ea2c23384a275ee",
"body": "Commit 1fc63b5f4835eba80462c7892a2cd00f786ec9ce tried to limit the\nbuilt packages to ignore other arbitrary top-level directories. But\nin turn, it disabled the auto-discovery in setuptools completely.\nThis results in build warnings for the missing sub-packages that would\nhave been auto-discovered.\n\nSwitch to an explicit setuptools.packages.find configuration which\nstill limits to the canopen top-level package.",
"is_bot": false,
"headline": "build: Fix package auto-discovery (ref #468) (#608)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2025-08-05T11:06:12Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "b50616b57afc9ef048469b2064b1b8e9bc0434c3",
"body": "Partially reverts commit 62e9c1f851cb8721355d22c8571f2b41599c1b36.\nThese RST roles are not supported in the basic renderer used on PyPI.\nThus the package is rejected for upload.",
"is_bot": false,
"headline": "Remove Sphinx roles from README.rst",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2025-08-05T10:47:38Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "49905727b26fd273eecdb534946e67021d4666ff",
"body": "* Define constants for all standardized SDO abort codes.\n* Type annotations for constants (Final).\n* Use constants in SDO client and server.\n* Keep literals in tests, but comment and format.",
"is_bot": false,
"headline": "sdo: Define constants for all standardized abort codes (#596)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2025-08-05T09:38:56Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "d35ce74e197bbc88e5a3b796ef5963154421e968",
"body": "…(#606)",
"is_bot": false,
"headline": "eds: Avoid float formatting in BaudRate_xxx export keys (fixes #605) …",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2025-07-27T20:47:31Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "0130eb88b0c137bdd41cdaa25463d020ae3df6c9",
"body": "SDO abort messages were sent in some cases when a response times out\nor has an unexpected server command specifier. But not consistently,\nthus the following cases are now added:\n\n Mismatched scs:\n ReadableStream, after upload segment request\n WritableStream, after download initiate \n[…]\ne).\n\n* Avoid duplicate SDO abort messages on timeout in _block_ack()\n and _end_upload().\n\n* Add docstring to read_response() and detail exceptions.\n\nCo-authored-by: samsam <samfire.pilote@gmail.com>",
"is_bot": false,
"headline": "sdo.client: Add missing abort messages (#594)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2025-07-13T12:40:40Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "09f6e9e8e9497108790328aed9839f2552acdbae",
"body": "* Skip saving and log read-only PDO parameters.\n\nFor PDO communication or mapping parameters which are read-only on a\nnode, the PdoMap.save() method currently aborts with an SDO exception.\nHowever, individual parameters may simply not support configuration\nchanges. Rely on the Object Dictionary to \n[…]\nries, log\nwhat is not being saved.\n\n* Switch to a LocalNode in TestPDO to not require a network.\n\n* Test PdoMap.save() skipping readonly entries.\n\nCo-authored-by: Erlend E. Aasland <erlend@python.org>",
"is_bot": false,
"headline": "Gracefully handle readonly entries in PdoMap.save() (fixes #541) (#593)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2025-06-22T20:17:37Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "8d9aa6c67c07bfe39baae1d02b058e644de23bdc",
"body": "Co-authored-by: André Colomb <github.com@andre.colomb.de>",
"is_bot": false,
"headline": "Prevent warnings if SDO setup fails (#590)",
"author_name": "Svein Seldal",
"author_login": "sveinse",
"committed_at": "2025-06-18T15:14:36Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "80349e1326787fe4793858e0477c798489352029",
"body": "A data size of zero bytes cannot be encoded in the \"number of bytes\nthat do not contain data\" semantics, as that would require n=4, which\nis however limited to two bits (maximum value 3). So the SDO\nexpedited upload protocol simply cannot convey that condition. The\ncurrent implementation however s\n[…]\notocol violation by responding with an SDO abort code of\n0800 0024h, No data available.\n\nAdd a test case for this condition, verifying that the client raises\nthe appropriate SdoAbortedError exception.",
"is_bot": false,
"headline": "Abort SDO server upload when the object has zero length (#587)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2025-06-17T16:37:27Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "de0e644b7d2c6ce11294be40c1da4ab33a15206b",
"body": "If the lookup by name returns an object (not None), the boolean\nexpression still checks whether that object is equivalent to boolean\nTrue. This needlessly calls the object's __len__() method, while what\nwe're interested in is just whether an object was found.",
"is_bot": false,
"headline": "Fix OD item lookup evaluating subobject's length (fixes #588) (#589)",
"author_name": "Svein Seldal",
"author_login": "sveinse",
"committed_at": "2025-06-15T18:53:02Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "7fb914a12e239afbb8acc4134e9eb5a6afdbcb9d",
"body": "When an OD entry is found in SdoClient.upload(), the truncation is\ncurrently skipped only for those types explicitly listed as variable\nlength data (domain, strings). That causes unknown types to be\ntruncated to whatever their length indicates, which is usually one\nbyte.\n\nInvert the condition to ch\n[…]\nknown size,\na.k.a. those listed in STRUCT_TYPES where the required length can be\ndeduced from the structure format.\n\nRe-enable the unit tests which were skipped based on the previously\nbuggy behavior.",
"is_bot": false,
"headline": "Avoid truncating unknown types in SDO upload (fixes #436) (#591)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2025-06-15T18:20:23Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "7ddb19b2ca7b7a85b9cef3090249b00293dd4b4a",
"body": "We are waiting for the sequence number to be the same as the last good\nknown sequence number, to start considering the messages. However,\nthis is wrong because the SDO server will start sending the discarded\nsegments at the start of a new block, which means the sequence number\nrestarts from 1.\n\nRes\n[…]\nssion.\n\nAdd a block retransmit test. This test passes with this implementation\nbut will fail with an invalid CRC without fix for discarded segments.\n\nCo-authored-by: André Colomb <src@andre.colomb.de>",
"is_bot": false,
"headline": "Fix check for retransmission of discarded block segments (#546)",
"author_name": "Samuel Lee",
"author_login": "samsamfire",
"committed_at": "2025-06-11T07:55:03Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "c0a4ae50b0487d5d683e6eeff1fd3c3069747a02",
"body": "Co-authored-by: André Colomb <src@andre.colomb.de>",
"is_bot": false,
"headline": "Correctly apply offset to custom timestamp value (fixes #563) (#576)",
"author_name": "Svein Seldal",
"author_login": "sveinse",
"committed_at": "2025-06-11T07:20:46Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "09c1ddd4094358081dd4a79803dacf744ab72ecf",
"body": "Co-authored-by: Erlend E. Aasland <erlend@python.org>",
"is_bot": false,
"headline": "Add some type annotations for the nmt module (#586)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2025-06-09T20:09:10Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "bc917f057500cfa66cbe9b24fa80e268a40f7907",
"body": "Co-authored-by: André Colomb <src@andre.colomb.de>",
"is_bot": false,
"headline": "Type annotate everywhere Network is used (#575)",
"author_name": "Svein Seldal",
"author_login": "sveinse",
"committed_at": "2025-06-09T12:01:06Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "cf9f33ea88cbade2ee08037926c5a653396e6fab",
"body": "Estalish a minimal mypy config that ensures annotations are compatible\nwith Python 3.8 (the oldest supported Python version).\n\nAdd a forgiving CI job that does not affect the status of the GitHub\nworkflow run.\n\nCo-authored-by: André Colomb <src@andre.colomb.de>",
"is_bot": false,
"headline": "Add mypy CI and config (ref #358) (#518)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2025-06-09T11:50:47Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "e840449fbf9ddf810d14899279cb72e61a2447f0",
"body": "* Fix multiple assosciations and removals of networks\n* Simplify cleanup logic in Network.unsubscribe().\n\nCo-authored-by: André Colomb <src@andre.colomb.de>",
"is_bot": false,
"headline": "Fix multiple associations and removals of networks (#573)",
"author_name": "Svein Seldal",
"author_login": "sveinse",
"committed_at": "2025-04-28T20:36:54Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "f1a71da20b918760ace20c62297f41132cd6618c",
"body": null,
"is_bot": false,
"headline": "Bump Codecov action to version 5.",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2025-04-27T18:37:45Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "48cca7c655f8c932b1f761a8edc11d0702397fab",
"body": null,
"is_bot": false,
"headline": "Update GitHub URLs to new organization",
"author_name": "Christian Sandberg",
"author_login": "christiansandberg",
"committed_at": "2025-04-06T10:25:00Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "5382f245d8ede61b113c6a648cb99b199667f0cd",
"body": null,
"is_bot": false,
"headline": "Updated debian packaging for reprepro (#557)",
"author_name": "Tony Baltovski",
"author_login": "tonybaltovski",
"committed_at": "2025-02-20T20:20:22Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ae71853be7fb42870bb5763066b0c3c50d015669",
"body": "Previously, when attempting a write of \"\" to a VISIBLE_STRING, the\r\nexpedited SDO request would not be sent and the transaction would\r\ntimeout. To resolve this, if the transaction size is known to be 0, a\r\nsegmented transfer is used.",
"is_bot": false,
"headline": "Fix SDO writes of empty strings (#551)",
"author_name": "Thomas Willson",
"author_login": "willson556",
"committed_at": "2025-01-05T22:01:16Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ffbd10f44da89e407622c82b8587fcc64e3f2f49",
"body": "Follow recommendations from flake8 regarding import order and\ngrouping. Put two blank lines after the top import block in each\nfile.\n\nImport CanError directly from can package in canopen.sdo.client\ninstead of from canopen.network, where it was not even used.",
"is_bot": false,
"headline": "Clean up imports ordering.",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2024-10-18T07:20:29Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c781a225f60b1b5d39cfdad713d3d5bb4799c844",
"body": "… (#525)\n\nAdd an _UnitializedNetwork class and a singleton\r\n_UNINITIALIZED_NETWORK instance. It can replace the dummy \"None\"\r\nvalue for attribute initializations, which can then be properly typed\r\nas Network to avoid static type checking errors.\r\n\r\nThis has the benefit of not needing `self.network \n[…]\nge (accessing a network without associating it\r\nfirst), which a static checker cannot detect reliably. The dummy\r\nclass provides a descriptive exception message when any attribute is\r\naccessed on it.",
"is_bot": false,
"headline": "Add a dummy object to designate \"uninitialized\" networks (fixes #511)…",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2024-10-17T19:36:11Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "6bc90a817f049b52fbeb1201c699e90ca36a6136",
"body": "The count entry itself is not part of the data (according to CiA 301),\r\nthus should not be yielded from an iterator. That matches the\r\nbehavior of SdoArray, which also yields only the array contents.\r\n\r\nNote that the basis of returned record sub-objects is still the subset\r\ndescribed by the OD, whi\n[…]\nicated by the record's\r\nsubindex 0. Thus the count (and iteration set) is reduced by one\r\nelement only if the subindex 0 was actually part of this subset.\r\n\r\n* No need to expect test failure anymore.",
"is_bot": false,
"headline": "Skip the \"highest subindex\" entry when iterating over SdoRecord (#538)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2024-08-17T21:05:14Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "273bab17400f0eb14c5e9e7714fa88de3fce2e7f",
"body": "* Remove one of the sub-objects in a record for testing.\r\n\r\nAdjust the EDS test accordingly, since the record length only counts\r\nsub-objects that have an actual description.\r\n\r\n* Flesh out the Pre-defined error field object in sample.eds.\r\n\r\nSwitch from CompactSubObj to actual sub-entries. Leave o\n[…]\n SdoArray dynamically generated member variables.\r\n\r\n* Add test for SdoArray length and iteration count.\r\n\r\n* Add test for SdoRecord length and iteration count.\r\n\r\n* Expect failure on last added test.",
"is_bot": false,
"headline": "Tests: Verify some basic SDO record and array assumptions (#539)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2024-08-17T20:59:54Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "210374a857b8afbb9fb2bf2a93d374f4e0f70563",
"body": "* Speed up NMT tests\r\n\r\n* Make test_emcy, test_eds, and test_od executable scripts\r\n\r\n* Harden and speed up test_network_send_periodic()\r\n\r\n* Shorten heartbeat period in test_local\r\n\r\n* Join as cleanup action\r\n\r\n* Fix test_network_send_periodic() periodicity test",
"is_bot": false,
"headline": "Tests: speed up test suite (#537)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-08-15T06:46:09Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "1b7c06f57ba8a46afbddfa2b4ca29196e63a465d",
"body": "* Annotate PeriodicMessageTask in NmtMaster and NmtSlave.\r\n\r\n* Type annotate Network.notifier.\r\n\r\n* Remove duplicate initialization of PeriodicMessageTask._task.\r\n\r\nThe attribute is set within the method directly following. So there\r\nis no need to initialize it to None before, only to confuse type\r\ncheckers.",
"is_bot": false,
"headline": "Fix some type checker errors in network module (#534)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2024-08-14T21:17:36Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "6ef4f5e35d84a7d776d1758842bc8138215e9715",
"body": "* Add configurable timeouts for Network.notifier.\r\n\r\nMove the hard-coded 1 second maximum cycle duration passed during\r\ncan.Notifier construction to a class variable NOTIFIER_CYCLE.\r\nExplicitly specify the timeout to stop the notifier in another class\r\nvariable NOTIFIER_SHUTDOWN_TIMEOUT, matching th\n[…]\nuring tests.\r\n\r\nExplicitly set the NOTIFIER_SHUTDOWN_TIMEOUT to zero in all Network\r\ninstances created for unit tests.\r\n\r\nThis reduces total test running time from ~30 to ~9 seconds when run\r\nlocally.",
"is_bot": false,
"headline": "Make Network.notifier timeouts customizable (fixes #530) (#536)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2024-08-14T10:47:13Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "932c6212bf077596dbfec91fd6ca66308142b972",
"body": "Also:\r\n- bump GitHub checkout action to v4\r\n- bump GitHub setup-python action to v5\r\n- ignore README and LICENSE files\r\n- invoke pip according to best practise\r\n\r\nFollow-up of #498",
"is_bot": false,
"headline": "CI: Add requirements-dev.txt and add pip cache (#533)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-08-12T10:59:10Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "4301c4bfed020565bfdb939deaf30b8d49de5727",
"body": "* Tests: harden test_emcy_consumer_wait\r\n\r\nWait a little bit longer before dispatching the EMCY packet, and start\r\nthe one-shot timer just before invoking the wait() call.\r\n\r\n* Better resource management",
"is_bot": false,
"headline": "Tests: harden test_emcy_consumer_wait (#532)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-08-12T10:01:01Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "83594ac89ee8f07d3bf346f3ceeab3b52d954625",
"body": null,
"is_bot": false,
"headline": "Tests: improve SYNC coverage (#531)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-08-12T08:22:00Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "78f6dded0b714e542dd0c5bac7af73b8012b499c",
"body": null,
"is_bot": false,
"headline": "Docs: remove unused Sphinx config 'html_static_path' (#521)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-08-12T07:45:51Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "5004363e011b5f22b5e16825328189c81889a205",
"body": null,
"is_bot": false,
"headline": "Tests: improve EMCY coverage (#529)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-08-12T07:27:54Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "28ee18c14a628671ebeb6105f3cd1ed02af02cc4",
"body": "The .active functionality was purged on June 9, 2017\r\nwith commit a28ef2762.",
"is_bot": false,
"headline": "Remove unused NodeScanner.active member (#527)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-08-11T20:50:49Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "d75c5f09b11b1ffee200bd8813efac5c8f0127e8",
"body": "This fixes up the tests added with #517 (commit\r\nd1c28e51b93c7d45fce40187672e7ab7a0616cac).",
"is_bot": false,
"headline": "Make node scanner tests more deterministic (#526)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-08-11T20:40:47Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "d1c28e51b93c7d45fce40187672e7ab7a0616cac",
"body": null,
"is_bot": false,
"headline": "Tests: improve coverage of NodeScanner (#517)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-08-06T20:08:49Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ecf216a760c8f0016826bb0c4f8e1f7944951d21",
"body": "- take into account that payloads with DATA1 may be in flight when we\r\n update the task\r\n- test task.stop()\r\n- use more lenient timeout\r\n- use threading.Condition for improved readability\r\n- wait for periodicity to be established before validation; some\r\n platforms are a little bit flaky\r\n\r\nCo-authored-by: André Colomb <src@andre.colomb.de>",
"is_bot": false,
"headline": "Tests: harden TestNetwork and TestNmt even more (#508)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-08-06T20:03:04Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "7d7e2f17c6aeef6aea2e6970f3f80064a3b8b2cc",
"body": null,
"is_bot": false,
"headline": "Tests: improve coverage of Network (#515)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-08-06T19:43:06Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "a196e1e3b62bddcfd62cea9fc5293efe8a8cc0a4",
"body": "Always require dependencies to be installed.\r\n\r\nResolved #509",
"is_bot": false,
"headline": "Purge import hack from network.py (#510)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-07-10T14:47:57Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "55d870e2d4fd1f36141712ebad318e6b611ea50c",
"body": "Windows fails with a permission error if the temporary file is already\r\nopened.\r\n\r\nFixes #501",
"is_bot": false,
"headline": "Tests: make sure temporary files are pre-closed (#507)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-07-10T14:44:01Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "3aa509db9fea8094516e5eb717be3df182f0751a",
"body": "- make tests deterministic\r\n- use cleanup hooks\r\n- check the periodicity of heartbeats, by number of triggers\r\n- roughly check the interval",
"is_bot": false,
"headline": "Tests: harden TestNetwork (#505)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-07-09T11:06:57Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "5207b322907b9d3fa2a4343ed17dc4d89fe26109",
"body": "Fixes #500",
"is_bot": false,
"headline": "Ensure NmtBase.state always returns a string (#506)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-07-09T10:58:36Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "fa74236b3f3312c03d09e72de3fa6d3cf077fbec",
"body": null,
"is_bot": false,
"headline": "Add tests for NMT base and NMT master (#504)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-07-09T10:15:33Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c4560daa37bec67a663046834a42b8058b5ceedc",
"body": null,
"is_bot": false,
"headline": "Build docs in CI (fixes #497) (#498)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-07-09T06:54:25Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "6bd39ba8f1fef7e32732fcc61ae548aa2392b7e0",
"body": "The canmatrix optional dependency was removed on Oct 10, 2021 with commit\r\nc46228f. It is now added back as an optional dependency, using the same\r\nname as previously: db_export.\r\n\r\nTo install the dependency:\r\n\r\n$ python3 -m pip install 'canopen[db_export]'\r\n\r\nResolves #488",
"is_bot": false,
"headline": "Add back PdoBase.export() dependency (canmatrix) (#493)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-07-09T06:51:33Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "08eba81f519e637e0c20d58560ca9d6637e0adfe",
"body": "Give NMT tests their own file, and establish a util.py for the test\r\nsuite for common stuff like the location of sample EDS files.\r\n\r\nConsistently use SAMPLE_EDS instead of EDS_PATH.",
"is_bot": false,
"headline": "Tests: establish test/util.py and test/test_nmt.py (#503)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-07-08T13:42:45Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c413eaffd0e13389512cfdf43dd51694842a427b",
"body": null,
"is_bot": false,
"headline": "Git: correctly ignore docs build directory (#499)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-07-08T10:41:48Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "62e9c1f851cb8721355d22c8571f2b41599c1b36",
"body": "- add formatting to Git branch names\r\n- add formatting to Python module names\r\n- spell out Python version support",
"is_bot": false,
"headline": "README: Add formatting and amend doc build instructions (#494)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-07-06T17:44:05Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "36c8488febc20e1d351135c90dd8966ab1d07dfb",
"body": "Handle PDO-related objects through the associated methods in the PdoBase class when initializing a RemoteNode from an ObjectDictionary with provided values (DCF file usually). This avoids the locally cached mapping information to get out of sync with the PDO configuration parameters, and thus elimi\n[…]\n.read() method must honor DCF-provided values for this purpose. It previously only respected the OD default values when called with from_od=True.\r\n\r\nCo-authored-by: André Colomb <src@andre.colomb.de>",
"is_bot": false,
"headline": "Add support for loading DCF configuration to remote node (#427)",
"author_name": "meifonglow",
"author_login": "meifonglow",
"committed_at": "2024-07-04T20:45:12Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "02ebc0c44fac791ca44f232c6d6ac1950617bfab",
"body": null,
"is_bot": false,
"headline": "Add tests for bitrate parsing in import_eds() (#495)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-07-04T10:40:47Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "f1315d3df4354cf02ac2acc70b53b91761c2f242",
"body": "The CiA306 spec is not quite clear on which fields are mandatory\r\nwithin the DeviceComissioning section. Thus it should be assumed that\r\nthe two options handled here could be missing. Instead of raising an\r\nexception, ignore the absence of the Baudrate setting.",
"is_bot": false,
"headline": "Gracefully handle missing Baudrate setting in DCF (#492)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2024-07-03T22:28:33Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "6a4ca1184e219c64768fbb0eb8434b04320d4803",
"body": "Only for DCF-files (which include a DeviceComissioning section), the Node-ID from the file is stored in the ObjectDictionary instance. When given an override value via the `import_od(..., node_id=...)` argument, that was however not applied to the OD attribute, but only used for $NODEID interpolati\n[…]\nn.\r\n\r\nMake sure the interpolated values match the stored node_id in this case.\r\n\r\nAlso handle a missing NodeID option within the DeviceComissioning section gracefully, instead of raising an exception.",
"is_bot": false,
"headline": "Save the passed node ID to the resulting OD in import_od() (#484)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-07-03T22:08:24Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "2938a908fb3cb93a65db5d30a7d889a08f66c956",
"body": "The bustype keyword has long been replaced by \"interface\" and the\r\nformer is deprecated in python-can. The Network class passes it\r\nthrough without caring for the name, but documentation and tests\r\nreferred to the older name regularly.",
"is_bot": false,
"headline": "Adapt to renamed \"bustype\" parameter on can.Bus (#489)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2024-07-03T21:20:47Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "cc37aee4e172a5a83f9f9dc956cc66d82bd1a8b7",
"body": "Fixes #485",
"is_bot": false,
"headline": "Bail early if an unsupported doctype is passed to export_od() (#486)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-07-03T09:40:54Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "92a6e11168f5072403a8ec03a7de0195854674cb",
"body": null,
"is_bot": false,
"headline": "Remove temporary files created in EDS tests (#482)",
"author_name": "Svein Seldal",
"author_login": "sveinse",
"committed_at": "2024-07-02T15:25:11Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "65bfaab0fd4d826be125dc0045834b179e47fb22",
"body": null,
"is_bot": false,
"headline": "Reduce diff",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-07-02T11:56:05Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "71da1bc38f56e602d95686adec22abd3ee0d3939",
"body": "Clean-up post #474",
"is_bot": false,
"headline": "Remove temporary files created in EDS tests",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-07-02T11:54:49Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "e3af0eb31bdfde2203d56f1dd953dbd23b4b6609",
"body": "* Test export_eds() to stdout\r\n* Use StringIO as a context manager iso. addCleanup\r\n* Expand the test to cover most paths\r\n* Expand test for unknown doctypes\r\n* Use subTest for better debugging",
"is_bot": false,
"headline": "Increase export_eds() test coverage (#474)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-07-02T11:26:36Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "028a57f49510803eeef4965b95f3aa76ce18e648",
"body": "Resolves #472\r\n\r\n* Expose and improve the docstrings of export_od and import_id\r\n* export_od(od, None, doctype) does not return a string; it writes to stdout\r\n* Clarify 'doc_type' details\r\n* Add exception info",
"is_bot": false,
"headline": "Expose and improve the docstrings of `export_od` and `import_id` (#473)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-07-02T11:21:46Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "d141e13970ce5bb5e696a97b92131356d34092bc",
"body": "Spell out how it works and link to PdoBase.read()\r\n\r\nMoreover, add return annotation to make sure that's included in the\r\nrendered docs.",
"is_bot": false,
"headline": "Improve the RemoteNode.load_configuration() docs (#477)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-07-02T11:19:58Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "503918c9bd9ff0499a604c7805e7627ca38fb332",
"body": "Fixes #475\r\n\r\n* Test the offending suffix is part of the error message\r\n* Make the exception message more helpful",
"is_bot": false,
"headline": "Raise ValueError for unknown formats in the import/export OD APIs (#476)",
"author_name": "Erlend E. Aasland",
"author_login": "erlend-aasland",
"committed_at": "2024-07-02T10:32:52Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "5734f37ee758e1d025701b4d40e0b89a57db2317",
"body": "* Reintroduce PdoMap and PdoVariable to public API of sub-package pdo.\r\n\r\nWith commit 0285f582beb344de708ce8308274a99bc04988a1, the Map class\r\nwas inadvertently removed from the imports directly available within\r\nthe canopen.pdo package. However, it is referenced as\r\ncanopen.pdo.PdoMap in the p402 \n[…]\nc API.\r\n\r\n* Use type hints for PdoMap in BaseNode402.\r\n\r\nReplace the sphinx parameter type documentation with a proper type\r\nhint annotation. Same for the tpdo_pointers and rpdo_pointers\r\nattributes.",
"is_bot": false,
"headline": "Export PdoMap and PdoVariable classes in pdo module (#464)",
"author_name": "André Colomb",
"author_login": "acolomb",
"committed_at": "2024-07-02T10:04:26Z",
"body_truncated": true,
"is_coding_agent": false
}
],
"releases_count": 22,
"commits_last_year": 43,
"latest_release_at": "2025-08-05T11:11:01Z",
"latest_release_tag": "v2.4.1",
"releases_from_tags": false,
"days_since_last_push": 28,
"active_weeks_last_year": 15,
"days_since_latest_release": 355,
"mean_days_between_releases": 237
},
"community": {
"has_readme": true,
"has_license": true,
"has_description": true,
"has_contributing": false,
"health_percentage": 37,
"has_issue_template": false,
"has_code_of_conduct": false,
"has_pull_request_template": false
},
"ecosystem": {
"packages": [
{
"name": "canopen",
"exists": true,
"license": null,
"keywords": [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3 :: Only",
"Topic :: Scientific/Engineering"
],
"ecosystem": "pypi",
"matches_repo": true,
"registry_url": "https://pypi.org/project/canopen/",
"is_deprecated": false,
"latest_version": "2.4.1",
"repository_url": "https://github.com/canopen-python/canopen",
"versions_count": 42,
"total_downloads": null,
"dependents_count": null,
"deprecation_note": null,
"maintainers_count": null,
"monthly_downloads": 857519,
"first_published_at": "2016-09-20T18:01:29.141719Z",
"latest_published_at": "2025-08-05T11:11:39.476697Z",
"latest_version_yanked": null,
"days_since_latest_publish": 355
}
]
},
"popularity": {
"forks": 359,
"stars": 562,
"watchers": 26,
"fork_history": {
"days": [
{
"date": "2016-10-22",
"count": 1
},
{
"date": "2016-11-01",
"count": 1
},
{
"date": "2016-11-03",
"count": 1
},
{
"date": "2016-11-21",
"count": 1
},
{
"date": "2016-12-13",
"count": 1
},
{
"date": "2016-12-22",
"count": 1
},
{
"date": "2017-01-09",
"count": 1
},
{
"date": "2017-04-13",
"count": 1
},
{
"date": "2017-05-08",
"count": 1
},
{
"date": "2017-07-06",
"count": 1
},
{
"date": "2017-07-27",
"count": 1
},
{
"date": "2017-08-09",
"count": 1
},
{
"date": "2017-09-01",
"count": 2
},
{
"date": "2017-09-15",
"count": 1
},
{
"date": "2017-10-09",
"count": 1
},
{
"date": "2017-11-04",
"count": 1
},
{
"date": "2017-11-06",
"count": 1
},
{
"date": "2018-01-03",
"count": 1
},
{
"date": "2018-01-09",
"count": 1
},
{
"date": "2018-01-11",
"count": 1
},
{
"date": "2018-02-01",
"count": 1
},
{
"date": "2018-03-22",
"count": 1
},
{
"date": "2018-03-30",
"count": 1
},
{
"date": "2018-04-03",
"count": 1
},
{
"date": "2018-04-18",
"count": 1
},
{
"date": "2018-06-19",
"count": 1
},
{
"date": "2018-07-17",
"count": 1
},
{
"date": "2018-07-24",
"count": 1
},
{
"date": "2018-07-26",
"count": 1
},
{
"date": "2018-08-07",
"count": 2
},
{
"date": "2018-08-17",
"count": 1
},
{
"date": "2018-09-05",
"count": 1
},
{
"date": "2018-10-26",
"count": 1
},
{
"date": "2018-10-28",
"count": 1
},
{
"date": "2018-11-06",
"count": 1
},
{
"date": "2018-11-27",
"count": 1
},
{
"date": "2018-12-26",
"count": 1
},
{
"date": "2019-01-08",
"count": 1
},
{
"date": "2019-01-14",
"count": 1
},
{
"date": "2019-02-16",
"count": 1
},
{
"date": "2019-03-07",
"count": 1
},
{
"date": "2019-03-14",
"count": 2
},
{
"date": "2019-03-22",
"count": 1
},
{
"date": "2019-03-28",
"count": 1
},
{
"date": "2019-04-02",
"count": 1
},
{
"date": "2019-05-17",
"count": 1
},
{
"date": "2019-05-21",
"count": 1
},
{
"date": "2019-06-06",
"count": 1
},
{
"date": "2019-06-12",
"count": 1
},
{
"date": "2019-06-13",
"count": 1
},
{
"date": "2019-06-23",
"count": 1
},
{
"date": "2019-07-24",
"count": 1
},
{
"date": "2019-08-23",
"count": 1
},
{
"date": "2019-08-24",
"count": 1
},
{
"date": "2019-08-27",
"count": 1
},
{
"date": "2019-09-04",
"count": 1
},
{
"date": "2019-09-09",
"count": 1
},
{
"date": "2019-09-14",
"count": 1
},
{
"date": "2019-10-20",
"count": 1
},
{
"date": "2019-10-21",
"count": 1
},
{
"date": "2019-11-08",
"count": 1
},
{
"date": "2019-11-14",
"count": 2
},
{
"date": "2019-11-19",
"count": 1
},
{
"date": "2020-01-20",
"count": 1
},
{
"date": "2020-01-28",
"count": 1
},
{
"date": "2020-01-30",
"count": 1
},
{
"date": "2020-03-19",
"count": 1
},
{
"date": "2020-04-07",
"count": 1
},
{
"date": "2020-04-15",
"count": 1
},
{
"date": "2020-04-24",
"count": 1
},
{
"date": "2020-04-29",
"count": 1
},
{
"date": "2020-05-13",
"count": 1
},
{
"date": "2020-05-18",
"count": 1
},
{
"date": "2020-06-02",
"count": 1
},
{
"date": "2020-08-11",
"count": 1
},
{
"date": "2020-09-22",
"count": 1
},
{
"date": "2020-09-30",
"count": 1
},
{
"date": "2020-10-11",
"count": 1
},
{
"date": "2020-10-31",
"count": 1
},
{
"date": "2020-11-02",
"count": 1
},
{
"date": "2020-11-20",
"count": 1
},
{
"date": "2021-01-13",
"count": 1
},
{
"date": "2021-01-17",
"count": 1
},
{
"date": "2021-01-20",
"count": 1
},
{
"date": "2021-02-08",
"count": 1
},
{
"date": "2021-02-17",
"count": 1
},
{
"date": "2021-02-18",
"count": 1
},
{
"date": "2021-02-24",
"count": 1
},
{
"date": "2021-03-03",
"count": 1
},
{
"date": "2021-03-09",
"count": 1
},
{
"date": "2021-03-22",
"count": 1
},
{
"date": "2021-04-05",
"count": 1
},
{
"date": "2021-04-07",
"count": 1
},
{
"date": "2021-04-14",
"count": 1
},
{
"date": "2021-04-29",
"count": 1
},
{
"date": "2021-05-02",
"count": 1
},
{
"date": "2021-06-09",
"count": 1
},
{
"date": "2021-07-27",
"count": 1
},
{
"date": "2021-08-05",
"count": 1
},
{
"date": "2021-09-02",
"count": 1
},
{
"date": "2021-09-06",
"count": 1
},
{
"date": "2021-10-11",
"count": 1
},
{
"date": "2021-10-23",
"count": 1
},
{
"date": "2021-10-28",
"count": 1
},
{
"date": "2021-11-03",
"count": 1
},
{
"date": "2021-11-09",
"count": 1
},
{
"date": "2021-11-11",
"count": 1
},
{
"date": "2021-11-12",
"count": 1
},
{
"date": "2021-11-27",
"count": 1
},
{
"date": "2021-12-09",
"count": 1
},
{
"date": "2021-12-10",
"count": 1
},
{
"date": "2021-12-31",
"count": 1
},
{
"date": "2022-01-06",
"count": 1
},
{
"date": "2022-02-11",
"count": 2
},
{
"date": "2022-03-08",
"count": 2
},
{
"date": "2022-03-15",
"count": 1
},
{
"date": "2022-03-22",
"count": 1
},
{
"date": "2022-04-03",
"count": 1
},
{
"date": "2022-05-04",
"count": 1
},
{
"date": "2022-05-07",
"count": 1
},
{
"date": "2022-05-11",
"count": 1
},
{
"date": "2022-05-12",
"count": 1
},
{
"date": "2022-05-13",
"count": 1
},
{
"date": "2022-05-17",
"count": 1
},
{
"date": "2022-05-20",
"count": 1
},
{
"date": "2022-06-14",
"count": 1
},
{
"date": "2022-07-08",
"count": 1
},
{
"date": "2022-08-06",
"count": 1
},
{
"date": "2022-08-25",
"count": 1
},
{
"date": "2022-08-26",
"count": 1
},
{
"date": "2022-08-30",
"count": 1
},
{
"date": "2022-09-01",
"count": 1
},
{
"date": "2022-09-14",
"count": 1
},
{
"date": "2022-09-22",
"count": 1
},
{
"date": "2022-11-03",
"count": 1
},
{
"date": "2023-01-23",
"count": 1
},
{
"date": "2023-01-29",
"count": 1
},
{
"date": "2023-01-30",
"count": 1
},
{
"date": "2023-02-02",
"count": 1
},
{
"date": "2023-02-09",
"count": 1
},
{
"date": "2023-02-22",
"count": 1
},
{
"date": "2023-02-27",
"count": 1
},
{
"date": "2023-03-06",
"count": 1
},
{
"date": "2023-03-14",
"count": 1
},
{
"date": "2023-03-16",
"count": 1
},
{
"date": "2023-04-28",
"count": 1
},
{
"date": "2023-05-09",
"count": 1
},
{
"date": "2023-05-18",
"count": 1
},
{
"date": "2023-05-24",
"count": 1
},
{
"date": "2023-05-26",
"count": 1
},
{
"date": "2023-06-06",
"count": 1
},
{
"date": "2023-06-19",
"count": 1
},
{
"date": "2023-07-06",
"count": 1
},
{
"date": "2023-07-07",
"count": 1
},
{
"date": "2023-07-19",
"count": 1
},
{
"date": "2023-08-24",
"count": 2
},
{
"date": "2023-09-01",
"count": 1
},
{
"date": "2023-10-18",
"count": 1
},
{
"date": "2023-10-24",
"count": 1
},
{
"date": "2023-10-28",
"count": 1
},
{
"date": "2023-11-14",
"count": 1
},
{
"date": "2023-11-28",
"count": 1
},
{
"date": "2023-11-29",
"count": 1
},
{
"date": "2023-12-02",
"count": 1
},
{
"date": "2023-12-19",
"count": 1
},
{
"date": "2024-01-08",
"count": 1
},
{
"date": "2024-02-28",
"count": 1
},
{
"date": "2024-03-10",
"count": 1
},
{
"date": "2024-03-20",
"count": 1
},
{
"date": "2024-03-28",
"count": 1
},
{
"date": "2024-04-24",
"count": 1
},
{
"date": "2024-05-20",
"count": 1
},
{
"date": "2024-06-04",
"count": 1
},
{
"date": "2024-06-29",
"count": 1
},
{
"date": "2024-07-10",
"count": 1
},
{
"date": "2024-07-15",
"count": 2
},
{
"date": "2024-07-29",
"count": 1
},
{
"date": "2024-10-05",
"count": 1
},
{
"date": "2024-10-28",
"count": 1
},
{
"date": "2024-11-25",
"count": 1
},
{
"date": "2025-02-12",
"count": 1
},
{
"date": "2025-02-25",
"count": 1
},
{
"date": "2025-03-02",
"count": 1
},
{
"date": "2025-04-04",
"count": 1
},
{
"date": "2025-04-23",
"count": 1
},
{
"date": "2025-06-17",
"count": 1
},
{
"date": "2025-07-21",
"count": 2
},
{
"date": "2025-08-05",
"count": 1
},
{
"date": "2025-08-06",
"count": 1
},
{
"date": "2025-08-20",
"count": 1
},
{
"date": "2025-09-01",
"count": 1
},
{
"date": "2025-09-02",
"count": 1
},
{
"date": "2025-09-10",
"count": 1
},
{
"date": "2025-09-14",
"count": 1
},
{
"date": "2025-09-21",
"count": 1
},
{
"date": "2025-09-27",
"count": 1
},
{
"date": "2025-11-07",
"count": 1
},
{
"date": "2025-12-01",
"count": 1
},
{
"date": "2025-12-09",
"count": 1
},
{
"date": "2026-01-05",
"count": 3
},
{
"date": "2026-01-30",
"count": 1
},
{
"date": "2026-02-27",
"count": 2
},
{
"date": "2026-03-05",
"count": 2
},
{
"date": "2026-03-06",
"count": 2
},
{
"date": "2026-03-07",
"count": 5
},
{
"date": "2026-03-08",
"count": 3
},
{
"date": "2026-03-09",
"count": 6
},
{
"date": "2026-03-10",
"count": 5
},
{
"date": "2026-03-12",
"count": 3
},
{
"date": "2026-03-13",
"count": 2
},
{
"date": "2026-03-14",
"count": 1
},
{
"date": "2026-03-15",
"count": 3
},
{
"date": "2026-03-16",
"count": 1
},
{
"date": "2026-03-17",
"count": 2
},
{
"date": "2026-03-18",
"count": 5
},
{
"date": "2026-03-19",
"count": 4
},
{
"date": "2026-03-20",
"count": 2
},
{
"date": "2026-03-21",
"count": 7
},
{
"date": "2026-03-22",
"count": 2
},
{
"date": "2026-03-23",
"count": 2
},
{
"date": "2026-03-24",
"count": 4
},
{
"date": "2026-03-25",
"count": 1
},
{
"date": "2026-03-26",
"count": 6
},
{
"date": "2026-03-27",
"count": 3
},
{
"date": "2026-03-28",
"count": 1
},
{
"date": "2026-03-29",
"count": 2
},
{
"date": "2026-03-30",
"count": 1
},
{
"date": "2026-03-31",
"count": 2
},
{
"date": "2026-04-01",
"count": 3
},
{
"date": "2026-04-02",
"count": 4
},
{
"date": "2026-04-03",
"count": 2
},
{
"date": "2026-04-04",
"count": 1
},
{
"date": "2026-04-06",
"count": 2
},
{
"date": "2026-04-07",
"count": 5
},
{
"date": "2026-04-08",
"count": 1
},
{
"date": "2026-04-09",
"count": 1
},
{
"date": "2026-04-10",
"count": 2
},
{
"date": "2026-04-11",
"count": 1
},
{
"date": "2026-04-12",
"count": 1
},
{
"date": "2026-04-13",
"count": 1
},
{
"date": "2026-04-14",
"count": 2
},
{
"date": "2026-04-15",
"count": 2
},
{
"date": "2026-04-16",
"count": 3
},
{
"date": "2026-04-17",
"count": 3
},
{
"date": "2026-04-18",
"count": 2
},
{
"date": "2026-04-20",
"count": 1
},
{
"date": "2026-04-21",
"count": 3
},
{
"date": "2026-04-22",
"count": 1
},
{
"date": "2026-04-23",
"count": 2
},
{
"date": "2026-04-24",
"count": 1
},
{
"date": "2026-04-27",
"count": 1
},
{
"date": "2026-04-28",
"count": 1
},
{
"date": "2026-04-29",
"count": 2
},
{
"date": "2026-04-30",
"count": 3
},
{
"date": "2026-05-06",
"count": 2
},
{
"date": "2026-05-07",
"count": 1
},
{
"date": "2026-05-11",
"count": 1
},
{
"date": "2026-05-12",
"count": 1
},
{
"date": "2026-05-20",
"count": 1
},
{
"date": "2026-05-21",
"count": 1
},
{
"date": "2026-06-23",
"count": 1
}
],
"complete": true,
"collected": 350,
"total_forks": 359
},
"star_history": null,
"open_issues_and_prs": 73
},
"ai_readiness": {
"has_nix": false,
"example_dirs": [
"examples"
],
"has_llms_txt": false,
"has_dockerfile": false,
"has_mcp_signal": false,
"bootstrap_files": [
"doc/Makefile"
],
"api_schema_files": [],
"has_devcontainer": false,
"typecheck_configs": [
"canopen/py.typed"
],
"toolchain_manifests": [],
"largest_source_bytes": 40222,
"source_files_sampled": 49,
"oversized_source_files": 0,
"agent_instruction_files": [],
"agent_instruction_max_bytes": null
},
"dependencies": {
"manifests": [
"doc/requirements.txt",
"pyproject.toml",
"requirements-dev.txt",
"setup.py"
],
"advisories": {
"error": null,
"scope": "published_package",
"source": "osv",
"findings": [],
"collected": true,
"malicious": [],
"truncated": false,
"by_severity": {},
"advisory_count": 0,
"affected_count": 0,
"assessed_count": 4,
"malicious_count": 0,
"assessed_package": "pypi:canopen@2.4.1",
"unassessed_count": 0,
"direct_affected_count": 0
},
"ecosystems": [
"pypi"
],
"dependencies": [
{
"name": "python-can",
"manifest": "pyproject.toml",
"ecosystem": "pypi",
"version_constraint": ">= 3.0.0"
}
],
"all_dependencies": {
"error": null,
"source": "github-sbom",
"packages": [
{
"name": "python-can",
"direct": true,
"version": null,
"ecosystem": "pypi"
},
{
"name": "canmatrix",
"direct": false,
"version": null,
"ecosystem": "pypi"
},
{
"name": "furo",
"direct": false,
"version": null,
"ecosystem": "pypi"
},
{
"name": "mypy",
"direct": false,
"version": null,
"ecosystem": "pypi"
},
{
"name": "pytest",
"direct": false,
"version": null,
"ecosystem": "pypi"
},
{
"name": "pytest-cov",
"direct": false,
"version": null,
"ecosystem": "pypi"
},
{
"name": "setuptools",
"direct": false,
"version": null,
"ecosystem": "pypi"
},
{
"name": "setuptools-scm",
"direct": false,
"version": null,
"ecosystem": "pypi"
},
{
"name": "sphinx",
"direct": false,
"version": null,
"ecosystem": "pypi"
},
{
"name": "sphinx-autodoc-typehints",
"direct": false,
"version": null,
"ecosystem": "pypi"
}
],
"collected": true,
"truncated": false,
"total_count": 10,
"direct_count": 1,
"indirect_count": 9
}
},
"maintainership": {
"issues": {
"open_prs": 20,
"merged_prs": 230,
"open_issues": 53,
"closed_ratio": 0.84,
"closed_issues": 278,
"closed_unmerged_prs": 56
},
"bus_factor": 2,
"bot_contributors": 0,
"top_contributors": [
{
"type": "User",
"login": "christiansandberg",
"commits": 317,
"avatar_url": "https://avatars.githubusercontent.com/u/3647996?v=4"
},
{
"type": "User",
"login": "acolomb",
"commits": 113,
"avatar_url": "https://avatars.githubusercontent.com/u/6996887?v=4"
},
{
"type": "User",
"login": "af-silva",
"commits": 38,
"avatar_url": "https://avatars.githubusercontent.com/u/11874980?v=4"
},
{
"type": "User",
"login": "erlend-aasland",
"commits": 31,
"avatar_url": "https://avatars.githubusercontent.com/u/13780613?v=4"
},
{
"type": "User",
"login": "fredrikhoyer",
"commits": 29,
"avatar_url": "https://avatars.githubusercontent.com/u/35274532?v=4"
},
{
"type": "User",
"login": "sveinse",
"commits": 25,
"avatar_url": "https://avatars.githubusercontent.com/u/13770135?v=4"
},
{
"type": "User",
"login": "friederschueler",
"commits": 8,
"avatar_url": "https://avatars.githubusercontent.com/u/4674229?v=4"
},
{
"type": "User",
"login": "bizfsc",
"commits": 7,
"avatar_url": "https://avatars.githubusercontent.com/u/202658520?v=4"
},
{
"type": "User",
"login": "tuwuhs",
"commits": 5,
"avatar_url": "https://avatars.githubusercontent.com/u/2083418?v=4"
},
{
"type": "User",
"login": "luminize",
"commits": 5,
"avatar_url": "https://avatars.githubusercontent.com/u/2206842?v=4"
}
],
"contributors_sampled": 57,
"top_contributor_share": 0.488
},
"quality_signals": {
"has_ci": true,
"has_tests": true,
"ci_workflows": [
"pr-linters.yaml",
"pythonpackage.yml",
"pythonpublish.yml"
],
"has_docs_dir": true,
"linter_configs": [],
"has_editorconfig": false,
"has_linter_config": false,
"has_precommit_config": false
},
"security_signals": {
"lockfiles": [],
"scorecard": {
"checks": [
{
"name": "Binary-Artifacts",
"score": 10,
"reason": "no binaries found in the repo",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#binary-artifacts"
},
{
"name": "Branch-Protection",
"score": null,
"reason": "internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#branch-protection"
},
{
"name": "CI-Tests",
"score": 10,
"reason": "23 out of 23 merged PRs checked by a CI test -- score normalized to 10",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#ci-tests"
},
{
"name": "CII-Best-Practices",
"score": 0,
"reason": "no effort to earn an OpenSSF best practices badge detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#cii-best-practices"
},
{
"name": "Code-Review",
"score": 7,
"reason": "Found 19/25 approved changesets -- score normalized to 7",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#code-review"
},
{
"name": "Contributors",
"score": 10,
"reason": "project has 11 contributing companies or organizations",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#contributors"
},
{
"name": "Dangerous-Workflow",
"score": 10,
"reason": "no dangerous workflow patterns detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dangerous-workflow"
},
{
"name": "Dependency-Update-Tool",
"score": 0,
"reason": "no update tool detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dependency-update-tool"
},
{
"name": "Fuzzing",
"score": 0,
"reason": "project is not fuzzed",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#fuzzing"
},
{
"name": "License",
"score": 10,
"reason": "license file detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#license"
},
{
"name": "Maintained",
"score": 10,
"reason": "26 commit(s) and 10 issue activity found in the last 90 days -- score normalized to 10",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#maintained"
},
{
"name": "Packaging",
"score": 10,
"reason": "packaging workflow detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
},
{
"name": "Pinned-Dependencies",
"score": 0,
"reason": "dependency not pinned by hash detected -- score normalized to 0",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#pinned-dependencies"
},
{
"name": "SAST",
"score": 0,
"reason": "SAST tool is not run on all commits -- score normalized to 0",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#sast"
},
{
"name": "Security-Policy",
"score": 0,
"reason": "security policy file not detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#security-policy"
},
{
"name": "Signed-Releases",
"score": null,
"reason": "no releases found",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#signed-releases"
},
{
"name": "Token-Permissions",
"score": 0,
"reason": "detected GitHub workflow tokens with excessive permissions",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#token-permissions"
},
{
"name": "Vulnerabilities",
"score": 9,
"reason": "1 existing vulnerabilities detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
}
],
"commit": "14b3336bc973023e82325426adf6d71046ebcf55",
"ran_at": "2026-07-26T21:17:15Z",
"aggregate_score": 5.5,
"scorecard_version": "v5.5.0"
},
"has_codeql_workflow": false,
"has_security_policy": false,
"has_dependabot_config": false
},
"contribution_flow": {
"collected": true,
"ci_last_run_at": "2026-06-28T15:28:58Z",
"oldest_open_prs": [
{
"number": 267,
"created_at": "2021-09-06T23:22:48Z",
"last_comment_at": "2024-06-04T21:02:54Z",
"last_comment_author": "acolomb"
},
{
"number": 298,
"created_at": "2022-03-08T07:29:23Z",
"last_comment_at": "2026-05-04T09:33:30Z",
"last_comment_author": "bizfsc"
},
{
"number": 343,
"created_at": "2023-01-15T21:18:27Z",
"last_comment_at": "2023-03-15T21:25:47Z",
"last_comment_author": "samsamfire"
},
{
"number": 352,
"created_at": "2023-03-15T21:22:02Z",
"last_comment_at": "2026-05-06T08:44:45Z",
"last_comment_author": "acolomb"
},
{
"number": 359,
"created_at": "2023-03-27T13:58:16Z",
"last_comment_at": "2026-06-10T21:43:55Z",
"last_comment_author": "sveinse"
},
{
"number": 386,
"created_at": "2023-07-07T21:08:14Z",
"last_comment_at": "2023-08-02T13:00:22Z",
"last_comment_author": "Obsnold"
},
{
"number": 419,
"created_at": "2024-04-05T13:09:41Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 454,
"created_at": "2024-06-04T13:30:49Z",
"last_comment_at": "2026-04-16T21:28:01Z",
"last_comment_author": "acolomb"
},
{
"number": 524,
"created_at": "2024-07-29T20:47:57Z",
"last_comment_at": "2024-08-12T19:53:05Z",
"last_comment_author": "codecov-commenter"
},
{
"number": 547,
"created_at": "2024-11-07T20:55:02Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 559,
"created_at": "2025-02-18T08:06:00Z",
"last_comment_at": "2026-05-22T11:59:41Z",
"last_comment_author": "codecov"
},
{
"number": 603,
"created_at": "2025-07-21T09:29:33Z",
"last_comment_at": "2025-07-21T09:41:08Z",
"last_comment_author": "codecov"
},
{
"number": 614,
"created_at": "2025-09-11T16:58:48Z",
"last_comment_at": "2026-03-24T21:02:15Z",
"last_comment_author": "zaporozhets"
},
{
"number": 651,
"created_at": "2026-04-29T19:01:25Z",
"last_comment_at": "2026-05-12T07:43:02Z",
"last_comment_author": "bizfsc"
},
{
"number": 654,
"created_at": "2026-05-04T08:44:40Z",
"last_comment_at": "2026-06-27T21:38:16Z",
"last_comment_author": "acolomb"
},
{
"number": 668,
"created_at": "2026-05-26T08:32:51Z",
"last_comment_at": "2026-05-26T08:34:28Z",
"last_comment_author": "acolomb"
},
{
"number": 669,
"created_at": "2026-05-26T14:48:30Z",
"last_comment_at": "2026-06-28T21:24:55Z",
"last_comment_author": "acolomb"
},
{
"number": 673,
"created_at": "2026-06-04T21:09:21Z",
"last_comment_at": "2026-06-04T21:10:10Z",
"last_comment_author": "codecov"
},
{
"number": 674,
"created_at": "2026-06-24T14:27:21Z",
"last_comment_at": "2026-06-28T16:04:51Z",
"last_comment_author": "acolomb"
},
{
"number": 675,
"created_at": "2026-06-27T22:05:43Z",
"last_comment_at": "2026-06-27T22:13:59Z",
"last_comment_author": "acolomb"
}
],
"last_merged_pr_at": "2026-06-28T15:28:19Z",
"ci_last_conclusion": "SUCCESS",
"oldest_open_issues": [
{
"number": 47,
"created_at": "2017-10-29T12:33:07Z",
"last_comment_at": "2021-10-12T02:30:58Z",
"last_comment_author": "sveinse"
},
{
"number": 143,
"created_at": "2019-02-25T19:05:14Z",
"last_comment_at": "2020-06-04T14:19:44Z",
"last_comment_author": "arne123"
},
{
"number": 145,
"created_at": "2019-03-11T12:17:17Z",
"last_comment_at": "2019-03-14T09:10:11Z",
"last_comment_author": "grw1983"
},
{
"number": 179,
"created_at": "2020-03-05T09:54:02Z",
"last_comment_at": "2025-01-25T02:27:38Z",
"last_comment_author": "ZeroErrControl"
},
{
"number": 203,
"created_at": "2020-10-13T12:14:05Z",
"last_comment_at": "2020-10-20T07:15:45Z",
"last_comment_author": "illuusio"
},
{
"number": 261,
"created_at": "2021-08-12T12:16:12Z",
"last_comment_at": "2024-06-14T08:01:47Z",
"last_comment_author": "friederschueler"
},
{
"number": 272,
"created_at": "2021-10-12T02:29:21Z",
"last_comment_at": "2026-06-05T21:03:07Z",
"last_comment_author": "acolomb"
},
{
"number": 286,
"created_at": "2021-11-29T13:51:47Z",
"last_comment_at": "2022-02-18T20:03:56Z",
"last_comment_author": "af-silva"
},
{
"number": 321,
"created_at": "2022-06-29T14:51:48Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 325,
"created_at": "2022-08-12T06:36:59Z",
"last_comment_at": "2022-08-15T05:13:54Z",
"last_comment_author": "gardenvariety42"
},
{
"number": 329,
"created_at": "2022-09-27T11:58:17Z",
"last_comment_at": "2022-09-29T08:48:37Z",
"last_comment_author": "TudorMorosanu"
},
{
"number": 358,
"created_at": "2023-03-26T22:54:44Z",
"last_comment_at": "2025-06-22T20:08:15Z",
"last_comment_author": "acolomb"
},
{
"number": 372,
"created_at": "2023-04-12T16:32:52Z",
"last_comment_at": "2023-04-14T17:36:28Z",
"last_comment_author": "superlogan72"
},
{
"number": 374,
"created_at": "2023-04-21T09:52:42Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 377,
"created_at": "2023-05-12T09:51:33Z",
"last_comment_at": "2024-06-13T08:26:20Z",
"last_comment_author": "friederschueler"
},
{
"number": 383,
"created_at": "2023-06-23T14:14:31Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 385,
"created_at": "2023-07-07T20:56:52Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 391,
"created_at": "2023-08-19T03:57:31Z",
"last_comment_at": "2024-06-13T09:30:42Z",
"last_comment_author": "friederschueler"
},
{
"number": 392,
"created_at": "2023-08-28T16:07:51Z",
"last_comment_at": "2023-09-13T14:13:48Z",
"last_comment_author": "sam-tj"
},
{
"number": 396,
"created_at": "2023-09-05T10:53:25Z",
"last_comment_at": "2024-01-07T12:30:59Z",
"last_comment_author": "acolomb"
}
]
}
},
"config": {
"disabled_metrics": [],
"disabled_categories": [],
"disabled_components": {}
},
"source": {
"url": "https://github.com/canopen-python/canopen",
"host": "github.com",
"name": "canopen",
"owner": "canopen-python"
},
"metrics": {
"overall": {
"key": "overall",
"band": "moderate",
"name": "Overall health",
"note": null,
"notes": [],
"value": 69,
"inputs": {
"security": 64,
"vitality": 63,
"community": 72,
"governance": 66,
"engineering": 81
},
"components": []
},
"categories": [
{
"key": "vitality",
"band": "moderate",
"name": "Vitality",
"value": 63,
"weight": 0.22,
"metrics": [
{
"key": "development_activity",
"band": "moderate",
"name": "Development activity",
"note": null,
"notes": [],
"value": 64,
"inputs": {
"commits_last_year": 43,
"human_commit_share": 1,
"days_since_last_push": 28,
"active_weeks_last_year": 15
},
"components": [
{
"key": "push_recency",
"name": "Push recency",
"detail": "last push 28 days ago",
"points": 28.8,
"status": "partial",
"details": [
{
"code": "push_recency",
"params": {
"days": 28
}
}
],
"max_points": 36
},
{
"key": "commit_cadence",
"name": "Commit cadence",
"detail": "15/52 weeks with commits",
"points": 10.4,
"status": "partial",
"details": [
{
"code": "commit_cadence_weeks",
"params": {
"weeks": 15
}
}
],
"max_points": 36
},
{
"key": "commit_volume",
"name": "Commit volume",
"detail": "43 commits in the last year",
"points": 14.8,
"status": "partial",
"details": [
{
"code": "commits_last_year",
"params": {
"count": 43
}
}
],
"max_points": 18
},
{
"key": "openssf_scorecard_maintained",
"name": "OpenSSF Scorecard: Maintained",
"detail": "26 commit(s) and 10 issue activity found in the last 90 days -- score normalized to 10",
"points": 10,
"status": "met",
"details": [],
"max_points": 10
}
]
},
{
"key": "release_discipline",
"band": "moderate",
"name": "Release discipline",
"note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Signed-Releases. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"openssf_scorecard_signed_releases"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 62,
"inputs": {
"releases_count": 22,
"latest_release_tag": "v2.4.1",
"releases_from_tags": false,
"days_since_latest_release": 355,
"mean_days_between_releases": 237
},
"components": [
{
"key": "ships_releases",
"name": "Ships releases",
"detail": "22 releases published",
"points": 27,
"status": "met",
"details": [
{
"code": "releases_published",
"params": {
"count": 22
}
}
],
"max_points": 27
},
{
"key": "release_recency",
"name": "Release recency",
"detail": "latest release 355 days ago",
"points": 16.2,
"status": "partial",
"details": [
{
"code": "release_recency",
"params": {
"days": 355
}
}
],
"max_points": 36
},
{
"key": "release_cadence",
"name": "Release cadence",
"detail": "a release every ~237 days",
"points": 12.6,
"status": "partial",
"details": [
{
"code": "release_cadence",
"params": {
"gap": 237
}
}
],
"max_points": 27
},
{
"key": "openssf_scorecard_signed_releases",
"name": "OpenSSF Scorecard: Signed-Releases",
"detail": "no releases found",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 10
}
]
},
{
"key": "abandonment",
"band": "excellent",
"name": "Abandonment",
"note": null,
"notes": [],
"value": 100,
"inputs": {
"cap": null,
"state": "maintained",
"guards": [],
"signals": [],
"red_flag": false,
"multiplier_pct": 100,
"declared_reason": null,
"unverified_reason": null,
"unanswered_open_prs": null,
"unanswered_open_issues": null,
"days_since_last_merged_pr": null,
"days_since_last_human_commit": 28,
"days_since_last_human_commit_is_floor": false
},
"components": [
{
"key": "project_is_still_maintained",
"name": "Project is still maintained",
"detail": "last human commit 28 days ago",
"points": 100,
"status": "met",
"details": [
{
"code": "abandonment_maintained",
"params": {
"days": 28
}
}
],
"max_points": 100
}
]
}
],
"description": "Is the project alive — is code being written and are releases shipping?"
},
{
"key": "community",
"band": "good",
"name": "Community & Adoption",
"value": 72,
"weight": 0.18,
"metrics": [
{
"key": "popularity",
"band": "good",
"name": "Popularity & adoption",
"note": null,
"notes": [],
"value": 74,
"inputs": {
"forks": 359,
"stars": 562,
"watchers": 26,
"growth_state": "unverified",
"growth_factor_pct": 100,
"growth_unverified_reason": "no_history"
},
"components": [
{
"key": "stars",
"name": "Stars",
"detail": "562 stars",
"points": 44.6,
"status": "partial",
"details": [
{
"code": "stars",
"params": {
"count": 562
}
}
],
"max_points": 60
},
{
"key": "forks",
"name": "Forks",
"detail": "359 forks",
"points": 21.3,
"status": "partial",
"details": [
{
"code": "forks",
"params": {
"count": 359
}
}
],
"max_points": 25
},
{
"key": "watchers",
"name": "Watchers",
"detail": "26 watchers",
"points": 7.8,
"status": "partial",
"details": [
{
"code": "watchers",
"params": {
"count": 26
}
}
],
"max_points": 15
}
]
},
{
"key": "community_health",
"band": "moderate",
"name": "Community health",
"note": null,
"notes": [],
"value": 50,
"inputs": {
"has_readme": true,
"has_license": true,
"has_contributing": false,
"has_issue_template": false,
"has_code_of_conduct": false,
"has_pull_request_template": false
},
"components": [
{
"key": "readme",
"name": "README",
"detail": null,
"points": 22.5,
"status": "met",
"details": [],
"max_points": 22.5
},
{
"key": "license",
"name": "License",
"detail": "recognized license (MIT)",
"points": 22.5,
"status": "met",
"details": [
{
"code": "license_standard",
"params": {}
},
{
"code": "license_spdx",
"params": {
"spdx": "MIT"
}
}
],
"max_points": 22.5
},
{
"key": "contributing_guide",
"name": "CONTRIBUTING guide",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 18
},
{
"key": "code_of_conduct",
"name": "Code of conduct",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 13.5
},
{
"key": "issue_template",
"name": "Issue template",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 7.2
},
{
"key": "pr_template",
"name": "PR template",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 6.3
}
]
},
{
"key": "ecosystem_adoption",
"band": "excellent",
"name": "Ecosystem adoption (downloads)",
"note": "Excluded from scoring (no data or not applicable): Registry dependents. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"registry_dependents"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 99,
"inputs": {
"packages": [
"canopen"
],
"dependents": null,
"ecosystems": "pypi",
"total_downloads": null,
"monthly_downloads": 857519
},
"components": [
{
"key": "monthly_downloads",
"name": "Monthly downloads",
"detail": "857,519 downloads/month across pypi",
"points": 79.1,
"status": "partial",
"details": [
{
"code": "downloads_monthly",
"params": {
"count": 857519,
"ecosystems": "pypi"
}
}
],
"max_points": 80
},
{
"key": "registry_dependents",
"name": "Registry dependents",
"detail": "not reported by this ecosystem",
"points": 0,
"status": "excluded",
"details": [
{
"code": "not_reported_by_this_ecosystem",
"params": {}
}
],
"max_points": 20
}
]
}
],
"description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
},
{
"key": "governance",
"band": "moderate",
"name": "Sustainability & Governance",
"value": 66,
"weight": 0.24,
"metrics": [
{
"key": "maintainer_resilience",
"band": "moderate",
"name": "Maintainer resilience (bus factor)",
"note": null,
"notes": [],
"value": 60,
"inputs": {
"bus_factor": 2,
"contributors_sampled": 57,
"top_contributor_share": 0.488
},
"components": [
{
"key": "bus_factor",
"name": "Bus factor",
"detail": "2 contributor(s) cover half of all commits",
"points": 25.2,
"status": "partial",
"details": [
{
"code": "bus_factor",
"params": {
"count": 2
}
}
],
"max_points": 54
},
{
"key": "commit_distribution",
"name": "Commit distribution",
"detail": "top contributor authored 49% of commits",
"points": 11.5,
"status": "partial",
"details": [
{
"code": "top_contributor_share",
"params": {
"share": 49
}
}
],
"max_points": 22.5
},
{
"key": "contributor_breadth",
"name": "Contributor breadth",
"detail": "57 contributors",
"points": 13.5,
"status": "met",
"details": [
{
"code": "contributors_sampled",
"params": {
"count": 57
}
}
],
"max_points": 13.5
},
{
"key": "openssf_scorecard_contributors",
"name": "OpenSSF Scorecard: Contributors",
"detail": "project has 11 contributing companies or organizations",
"points": 10,
"status": "met",
"details": [],
"max_points": 10
}
]
},
{
"key": "responsiveness",
"band": "good",
"name": "Issue & PR responsiveness",
"note": null,
"notes": [],
"value": 81,
"inputs": {
"merged_prs": 230,
"open_issues": 53,
"closed_issues": 278,
"issue_closed_ratio": 0.84,
"closed_unmerged_prs": 56
},
"components": [
{
"key": "issue_resolution",
"name": "Issue resolution",
"detail": "84% of issues closed",
"points": 39.3,
"status": "partial",
"details": [
{
"code": "issues_closed_share",
"params": {
"share": 84
}
}
],
"max_points": 46.75
},
{
"key": "pr_acceptance",
"name": "PR acceptance",
"detail": "230/286 decided PRs merged",
"points": 30.8,
"status": "partial",
"details": [
{
"code": "decided_prs_merged",
"params": {
"merged": 230,
"decided": 286
}
}
],
"max_points": 38.25
},
{
"key": "openssf_scorecard_code_review",
"name": "OpenSSF Scorecard: Code-Review",
"detail": "Found 19/25 approved changesets -- score normalized to 7",
"points": 10.5,
"status": "partial",
"details": [],
"max_points": 15
}
]
},
{
"key": "stewardship",
"band": "at_risk",
"name": "Ownership & stewardship",
"note": null,
"notes": [],
"value": 38,
"inputs": {
"followers": 2,
"owner_type": "Organization",
"is_verified": null,
"owner_login": "canopen-python",
"public_repos": 1,
"account_age_days": 517
},
"components": [
{
"key": "ownership_backing",
"name": "Ownership backing",
"detail": "organization-owned",
"points": 30,
"status": "met",
"details": [
{
"code": "owner_organization",
"params": {}
}
],
"max_points": 30
},
{
"key": "verified_domain",
"name": "Verified domain",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 20
},
{
"key": "owner_reach",
"name": "Owner reach",
"detail": "2 followers of canopen-python",
"points": 3.4,
"status": "partial",
"details": [
{
"code": "owner_followers",
"params": {
"count": 2,
"login": "canopen-python"
}
}
],
"max_points": 25
},
{
"key": "track_record",
"name": "Track record",
"detail": "1 public repos, account ~1 yr old",
"points": 5,
"status": "partial",
"details": [
{
"code": "public_repos",
"params": {
"count": 1
}
},
{
"code": "account_age_years",
"params": {
"years": 1
}
}
],
"max_points": 25
}
]
},
{
"key": "package_maintenance",
"band": "excellent",
"name": "Package maintenance",
"note": null,
"notes": [],
"value": 91,
"inputs": {
"packages": [
"canopen"
],
"ecosystems": "pypi",
"any_deprecated": false,
"min_days_since_publish": 355
},
"components": [
{
"key": "published_resolvable",
"name": "Published & resolvable",
"detail": "1 package(s) on pypi",
"points": 25,
"status": "met",
"details": [
{
"code": "packages_published",
"params": {
"count": 1,
"ecosystems": "pypi"
}
}
],
"max_points": 25
},
{
"key": "publish_recency",
"name": "Publish recency",
"detail": "latest publish 355 days ago",
"points": 26,
"status": "partial",
"details": [
{
"code": "publish_recency",
"params": {
"days": 355
}
}
],
"max_points": 35
},
{
"key": "version_history",
"name": "Version history",
"detail": "42 published versions",
"points": 20,
"status": "met",
"details": [
{
"code": "published_versions",
"params": {
"count": 42
}
}
],
"max_points": 20
},
{
"key": "not_deprecated",
"name": "Not deprecated",
"detail": "active, not deprecated or yanked",
"points": 20,
"status": "met",
"details": [
{
"code": "package_not_deprecated",
"params": {}
}
],
"max_points": 20
}
]
}
],
"description": "Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?"
},
{
"key": "engineering",
"band": "good",
"name": "Engineering Quality",
"value": 81,
"weight": 0.2,
"metrics": [
{
"key": "engineering_practices",
"band": "moderate",
"name": "Engineering practices",
"note": null,
"notes": [],
"value": 68,
"inputs": {
"has_ci": true,
"has_tests": true,
"has_editorconfig": false,
"has_linter_config": false,
"has_precommit_config": false
},
"components": [
{
"key": "ci_workflows",
"name": "CI workflows",
"detail": "3 workflow(s)",
"points": 24,
"status": "met",
"details": [
{
"code": "ci_workflows",
"params": {
"count": 3
}
}
],
"max_points": 24
},
{
"key": "tests_present",
"name": "Tests present",
"detail": null,
"points": 24,
"status": "met",
"details": [],
"max_points": 24
},
{
"key": "linter_config",
"name": "Linter config",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 16
},
{
"key": "pre_commit_hooks",
"name": "Pre-commit hooks",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 9.6
},
{
"key": "editorconfig",
"name": ".editorconfig",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 6.4
},
{
"key": "openssf_scorecard_ci_tests",
"name": "OpenSSF Scorecard: CI-Tests",
"detail": "23 out of 23 merged PRs checked by a CI test -- score normalized to 10",
"points": 20,
"status": "met",
"details": [],
"max_points": 20
}
]
},
{
"key": "documentation",
"band": "excellent",
"name": "Documentation",
"note": null,
"notes": [],
"value": 100,
"inputs": {
"topics": [
"can",
"canopen",
"python"
],
"has_wiki": true,
"homepage": "http://canopen.readthedocs.io/",
"has_readme": true,
"has_docs_dir": true,
"has_description": true
},
"components": [
{
"key": "readme",
"name": "README",
"detail": null,
"points": 30,
"status": "met",
"details": [],
"max_points": 30
},
{
"key": "documentation_directory",
"name": "Documentation directory",
"detail": null,
"points": 25,
"status": "met",
"details": [],
"max_points": 25
},
{
"key": "documentation_homepage_site",
"name": "Documentation / homepage site",
"detail": "http://canopen.readthedocs.io/",
"points": 15,
"status": "met",
"details": [],
"max_points": 15
},
{
"key": "repository_description",
"name": "Repository description",
"detail": null,
"points": 10,
"status": "met",
"details": [],
"max_points": 10
},
{
"key": "topics",
"name": "Topics",
"detail": "3 topics",
"points": 10,
"status": "met",
"details": [
{
"code": "topics_count",
"params": {
"count": 3
}
}
],
"max_points": 10
},
{
"key": "wiki",
"name": "Wiki",
"detail": null,
"points": 10,
"status": "met",
"details": [],
"max_points": 10
}
]
}
],
"description": "Are baseline engineering and documentation practices in place?"
},
{
"key": "security",
"band": "moderate",
"name": "Security",
"value": 64,
"weight": 0.16,
"metrics": [
{
"key": "security_posture",
"band": "moderate",
"name": "Security posture",
"note": "Excluded from scoring (no data or not applicable): Branch-Protection, Signed-Releases. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"branch_protection",
"signed_releases"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 55,
"inputs": {
"source": "openssf_scorecard",
"checks_evaluated": 16,
"scorecard_version": "v5.5.0",
"checks_inconclusive": 2,
"scorecard_aggregate": 5.5
},
"components": [
{
"key": "binary_artifacts",
"name": "Binary-Artifacts",
"detail": "no binaries found in the repo",
"points": 7.5,
"status": "met",
"details": [],
"max_points": 7.5
},
{
"key": "branch_protection",
"name": "Branch-Protection",
"detail": "internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 7.5
},
{
"key": "ci_tests",
"name": "CI-Tests",
"detail": "23 out of 23 merged PRs checked by a CI test -- score normalized to 10",
"points": 2.5,
"status": "met",
"details": [],
"max_points": 2.5
},
{
"key": "cii_best_practices",
"name": "CII-Best-Practices",
"detail": "no effort to earn an OpenSSF best practices badge detected",
"points": 0,
"status": "missed",
"details": [],
"max_points": 2.5
},
{
"key": "code_review",
"name": "Code-Review",
"detail": "Found 19/25 approved changesets -- score normalized to 7",
"points": 5.2,
"status": "partial",
"details": [],
"max_points": 7.5
},
{
"key": "contributors",
"name": "Contributors",
"detail": "project has 11 contributing companies or organizations",
"points": 2.5,
"status": "met",
"details": [],
"max_points": 2.5
},
{
"key": "dangerous_workflow",
"name": "Dangerous-Workflow",
"detail": "no dangerous workflow patterns detected",
"points": 10,
"status": "met",
"details": [],
"max_points": 10
},
{
"key": "dependency_update_tool",
"name": "Dependency-Update-Tool",
"detail": "no update tool detected",
"points": 0,
"status": "missed",
"details": [],
"max_points": 7.5
},
{
"key": "fuzzing",
"name": "Fuzzing",
"detail": "project is not fuzzed",
"points": 0,
"status": "missed",
"details": [],
"max_points": 5
},
{
"key": "license",
"name": "License",
"detail": "license file detected",
"points": 2.5,
"status": "met",
"details": [],
"max_points": 2.5
},
{
"key": "maintained",
"name": "Maintained",
"detail": "26 commit(s) and 10 issue activity found in the last 90 days -- score normalized to 10",
"points": 7.5,
"status": "met",
"details": [],
"max_points": 7.5
},
{
"key": "packaging",
"name": "Packaging",
"detail": "packaging workflow detected",
"points": 5,
"status": "met",
"details": [],
"max_points": 5
},
{
"key": "pinned_dependencies",
"name": "Pinned-Dependencies",
"detail": "dependency not pinned by hash detected -- score normalized to 0",
"points": 0,
"status": "missed",
"details": [],
"max_points": 5
},
{
"key": "sast",
"name": "SAST",
"detail": "SAST tool is not run on all commits -- score normalized to 0",
"points": 0,
"status": "missed",
"details": [],
"max_points": 5
},
{
"key": "security_policy",
"name": "Security-Policy",
"detail": "security policy file not detected",
"points": 0,
"status": "missed",
"details": [],
"max_points": 5
},
{
"key": "signed_releases",
"name": "Signed-Releases",
"detail": "no releases found",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 7.5
},
{
"key": "token_permissions",
"name": "Token-Permissions",
"detail": "detected GitHub workflow tokens with excessive permissions",
"points": 0,
"status": "missed",
"details": [],
"max_points": 7.5
},
{
"key": "vulnerabilities",
"name": "Vulnerabilities",
"detail": "1 existing vulnerabilities detected",
"points": 6.8,
"status": "partial",
"details": [],
"max_points": 7.5
}
]
},
{
"key": "dependency_advisories",
"band": "excellent",
"name": "Dependency advisories",
"note": "Excluded from scoring (no data or not applicable): No advisories left outstanding. Remaining weights renormalized. Matched the pypi:canopen@2.4.1 runtime dependency closure — what installing the published package pulls in — 4 packages. Reachability is not analyzed.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"no_advisories_left_outstanding"
]
}
},
{
"code": "weights_renormalized",
"params": {}
},
{
"code": "advisories_scope_published",
"params": {
"package": "pypi:canopen@2.4.1",
"assessed": 4
}
},
{
"code": "advisories_reachability",
"params": {}
}
],
"value": 100,
"inputs": {
"source": "osv",
"advisories": 0,
"affected_packages": 0,
"assessed_packages": 4,
"unassessed_packages": 0,
"affected_by_severity": "none",
"direct_affected_packages": 0
},
"components": [
{
"key": "direct_dependencies_free_of_known_advisories",
"name": "Direct dependencies free of known advisories",
"detail": "no direct dependency carries a known advisory",
"points": 35,
"status": "met",
"details": [
{
"code": "no_direct_advisories",
"params": {}
}
],
"max_points": 35
},
{
"key": "indirect_dependencies_free_of_known_advisories",
"name": "Indirect dependencies free of known advisories",
"detail": "no indirect dependency carries a known advisory",
"points": 25,
"status": "met",
"details": [
{
"code": "no_indirect_advisories",
"params": {}
}
],
"max_points": 25
},
{
"key": "no_advisories_left_outstanding",
"name": "No advisories left outstanding",
"detail": "no advisory carries a publication date",
"points": 0,
"status": "excluded",
"details": [
{
"code": "advisories_no_publication_date",
"params": {}
}
],
"max_points": 40
}
]
},
{
"key": "malicious_dependencies",
"band": "excellent",
"name": "Malicious dependencies",
"note": null,
"notes": [],
"value": 100,
"inputs": {
"source": "osv",
"meaning": "reported as a malicious package by the OpenSSF corpus; the remedy is removal or moving off the compromised name, never an upgrade of the same artifact. Versions the registry has since pulled are listed but not scored",
"packages": [],
"red_flag": false,
"assessed_packages": 4,
"malicious_packages": 0,
"direct_malicious_packages": 0,
"withdrawn_malicious_packages": 0,
"installable_malicious_packages": 0
},
"components": [
{
"key": "no_dependency_reported_as_a_malicious_package",
"name": "No dependency reported as a malicious package",
"detail": "no dependency is reported as a malicious package",
"points": 100,
"status": "met",
"details": [
{
"code": "no_malicious_dependencies",
"params": {}
}
],
"max_points": 100
}
]
},
{
"key": "high_risk_jurisdiction_exposure",
"band": "excellent",
"name": "High-Risk Jurisdiction Exposure",
"note": "Only high-confidence self-published location evidence affects this multiplier. Ambiguous matches are review-only; country evidence is not proof of nationality, citizenship, legal registration, malicious intent, or sanctions status.",
"notes": [
{
"code": "jurisdiction_evidence_limits",
"params": {}
}
],
"value": 100,
"inputs": {
"meaning": "self-published location evidence; not nationality or citizenship",
"red_flag": false,
"exposures": [],
"policy_countries": [
"Russia",
"Iran",
"North Korea"
],
"review_only_matches": 0,
"assessed_self_published_locations": 10
},
"components": [
{
"key": "policy_exposure_multiplier",
"name": "Policy exposure multiplier",
"detail": "no confirmed policy-scope location match",
"points": 100,
"status": "met",
"details": [
{
"code": "jurisdiction_no_match",
"params": {}
}
],
"max_points": 100
}
]
}
],
"description": "Are visible security and supply-chain practices strong, with no malicious dependency and no unresolved high-risk jurisdiction exposure?"
},
{
"key": "ai_readiness",
"band": "moderate",
"name": "AI Readiness",
"value": 51,
"weight": 0,
"metrics": [
{
"key": "ai_agent_context",
"band": "at_risk",
"name": "Agent context & guidance",
"note": null,
"notes": [],
"value": 40,
"inputs": {
"has_llms_txt": false,
"legible_history_share": 0.91,
"agent_instruction_files": [],
"agent_instruction_max_bytes": null
},
"components": [
{
"key": "agent_instructions",
"name": "Agent instructions",
"detail": "no CLAUDE.md / AGENTS.md / editor rules",
"points": 0,
"status": "missed",
"details": [
{
"code": "no_agent_instructions",
"params": {}
}
],
"max_points": 45
},
{
"key": "machine_readable_docs_llms_txt",
"name": "Machine-readable docs (llms.txt)",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 15
},
{
"key": "legible_commit_history",
"name": "Legible commit history",
"detail": "91 of 100 human commits state their intent (structured subject or explanatory body)",
"points": 40,
"status": "met",
"details": [
{
"code": "legible_history",
"params": {
"legible": 91,
"sampled": 100
}
}
],
"max_points": 40
}
]
},
{
"key": "ai_verify_loop",
"band": "moderate",
"name": "Verify loop (build / test / typecheck)",
"note": null,
"notes": [],
"value": 51,
"inputs": {
"has_nix": false,
"has_tests": true,
"lockfiles": [],
"has_dockerfile": false,
"typed_language": false,
"bootstrap_files": [
"doc/Makefile"
],
"has_devcontainer": false,
"has_linter_config": false,
"typecheck_configs": [
"canopen/py.typed"
],
"agent_commit_share": 0,
"toolchain_manifests": [],
"dependency_bot_commit_share": 0
},
"components": [
{
"key": "one_command_bootstrap",
"name": "One-command bootstrap",
"detail": "doc/Makefile",
"points": 18,
"status": "met",
"details": [
{
"code": "file_list",
"params": {
"files": "doc/Makefile"
}
}
],
"max_points": 18
},
{
"key": "automated_tests",
"name": "Automated tests",
"detail": null,
"points": 22,
"status": "met",
"details": [],
"max_points": 22
},
{
"key": "lint_format_config",
"name": "Lint / format config",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 11
},
{
"key": "static_type_checking",
"name": "Static type checking",
"detail": "canopen/py.typed",
"points": 11,
"status": "met",
"details": [
{
"code": "file_list",
"params": {
"files": "canopen/py.typed"
}
}
],
"max_points": 11
},
{
"key": "reproducible_environment",
"name": "Reproducible environment",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 10
},
{
"key": "demonstrated_agent_practice",
"name": "Demonstrated agent practice",
"detail": "no agent-authored commits among the last 100",
"points": 0,
"status": "missed",
"details": [
{
"code": "no_agent_authored_commits",
"params": {
"sampled": 100
}
}
],
"max_points": 10
},
{
"key": "automated_maintenance",
"name": "Automated maintenance",
"detail": "no automated dependency updates observed",
"points": 0,
"status": "missed",
"details": [
{
"code": "no_dependency_automation",
"params": {}
}
],
"max_points": 8
},
{
"key": "openssf_scorecard_pinned_dependencies",
"name": "OpenSSF Scorecard: Pinned-Dependencies",
"detail": "dependency not pinned by hash detected -- score normalized to 0",
"points": 0,
"status": "missed",
"details": [],
"max_points": 10
}
]
},
{
"key": "ai_code_legibility",
"band": "good",
"name": "Code legibility for models",
"note": null,
"notes": [],
"value": 82,
"inputs": {
"primary_language": "Python",
"largest_source_bytes": 40222,
"source_files_sampled": 49,
"oversized_source_files": 0
},
"components": [
{
"key": "type_checkable_code",
"name": "Type-checkable code",
"detail": "Python with type-check config (canopen/py.typed)",
"points": 27,
"status": "partial",
"details": [
{
"code": "typecheck_config_language",
"params": {
"files": "canopen/py.typed",
"language": "Python"
}
}
],
"max_points": 45
},
{
"key": "manageable_file_sizes",
"name": "Manageable file sizes",
"detail": "0/49 source files over 60KB",
"points": 55,
"status": "met",
"details": [
{
"code": "oversized_source_files",
"params": {
"kb": 60,
"sampled": 49,
"oversized": 0
}
}
],
"max_points": 55
}
]
},
{
"key": "ai_interfaces",
"band": "at_risk",
"name": "Machine-readable interfaces",
"note": null,
"notes": [],
"value": 40,
"inputs": {
"example_dirs": [
"examples"
],
"has_mcp_signal": false,
"api_schema_files": []
},
"components": [
{
"key": "api_schema_openapi_graphql_proto",
"name": "API schema (OpenAPI/GraphQL/proto)",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 40
},
{
"key": "mcp_server",
"name": "MCP server",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 20
},
{
"key": "runnable_examples",
"name": "Runnable examples",
"detail": "examples",
"points": 40,
"status": "met",
"details": [
{
"code": "file_list",
"params": {
"files": "examples"
}
}
],
"max_points": 40
}
]
}
],
"description": "How well is the repo equipped to be developed and maintained with AI coding agents? An independent, experimental badge — weight 0.0, so it is surfaced on its own and does not affect the overall health score."
}
],
"metrics_version": "1.13.0"
},
"warnings": [
"Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token"
],
"report_type": "repository",
"generated_at": "2026-07-26T21:17:41.882771Z",
"schema_version": "0.27.0",
"badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/c/canopen-python/canopen.svg",
"full_name": "canopen-python/canopen",
"license_state": "standard",
"license_spdx": "MIT"
}