Skip to content

Conversation

@alexrudd2
Copy link
Collaborator

Make types either more explicit or clearer, to help more powerful type checkers than mypy (I'm trying pyright and zuban)

# Continuing, or use a sparse DataBlock which can have gaps
datablock = lambda : ModbusSparseDataBlock({0x00: 0, 0x05: 1}) # pylint: disable=unnecessary-lambda-assignment
elif args.store == "factory": # pragma: no cover
elif args.store == "factory" or True: # pragma: no cover # pylint: disable=condition-evals-to-constant
Copy link
Collaborator Author

@alexrudd2 alexrudd2 Dec 8, 2025

Choose a reason for hiding this comment

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

Just to make it explicit what the final/default value is

Copy link
Collaborator

Choose a reason for hiding this comment

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

You replace one pylint disable with another and make it more difficult to read.

Why does this need to be changed ? (and if changed it should be changed to something without a pylint disable.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I had done it this way to make it clear the final conditional is == factory.
The alternative here is to just use a default else.

---    elif args.store == "factory" or True:  # pragma: no cover  # pylint: disable=condition-evals-to-constant
+++    else:  # args.store == "factory"

"""Return a summary of the modbus plus statistics.
:returns: 54 16-bit words representing the status
:returns: An iterator over lists of 8-bit integers representing each statistic
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think this docstring has been wrong for a long time...

Copy link
Collaborator

@janiversen janiversen left a comment

Choose a reason for hiding this comment

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

A couple of comments.

# Continuing, or use a sparse DataBlock which can have gaps
datablock = lambda : ModbusSparseDataBlock({0x00: 0, 0x05: 1}) # pylint: disable=unnecessary-lambda-assignment
elif args.store == "factory": # pragma: no cover
elif args.store == "factory" or True: # pragma: no cover # pylint: disable=condition-evals-to-constant
Copy link
Collaborator

Choose a reason for hiding this comment

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

You replace one pylint disable with another and make it more difficult to read.

Why does this need to be changed ? (and if changed it should be changed to something without a pylint disable.

"""
self.single = single
self._devices = devices or {}
self._devices: dict = devices or {}
Copy link
Collaborator

Choose a reason for hiding this comment

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

No need for type declaration, the None is removed.

The type should be added in line 157 "dict | None"

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Agreed line 157 is better. Upon looking close, nobody ever calls ModbusDeviceContext(devices = None) so changing the default value to {} is cleaner all around.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Actually, this has revealed a tiny error in either the docstring or examples.

It's possible to pass Modbus*Context directly, as long as you pass single=True!

async def main(host="localhost", port=5020):
"""Run versions of read coil."""
store = ModbusServerContext(devices=ModbusDeviceContext(
di=ModbusSequentialDataBlock(0, [17] * 100),
co=ModbusSequentialDataBlock(0, [17] * 100),
hr=ModbusSequentialDataBlock(0, [17] * 100),
ir=ModbusSequentialDataBlock(0, [17] * 100),
),
single=True
)

return new_sslctx

def copy(self) -> CommParams:
def copy(self: CommParams) -> CommParams:
Copy link
Collaborator

Choose a reason for hiding this comment

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

This looks wrong, self already have the correct type ??

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

dataclasses.replace() on line 131 is.. imperfect. mypy is slightly too forgiving, but zuban is stricter.
See https://discuss.python.org/t/better-typing-for-copy-replace-functions-that-operate-on-dataclasses/105004/9

error: Argument 1 to "replace" has incompatible type "Self"; expected a dataclass [misc]

This seemed like a good workaround. Other options:
(1) typing.Self (but that requires 3.11)
(2) an explicit cast - ugly
(3) Using copy.copy()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants