Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request/Response: change execute to be async method #2142

Merged

Conversation

ilkka-ollakka
Copy link
Contributor

@ilkka-ollakka ilkka-ollakka commented Apr 2, 2024

contains only async/await edits, no functional changes otherwise. This combined with #2144 allows to utilize async datastore calls in modbus server

Split from #2127 and depends on #2139

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.

This is wrong and will not work for the sync client.

I thought you were working on the changes in asyncio in another PR, where you demonstrated that execute do not need to be async.

@ilkka-ollakka
Copy link
Contributor Author

In theory execute don't need to be async, but some of them are checking results of getValues/setValues, so those should be reworked to be out from execute in that case. I had the impression that only server-code called execute and this was ok approach. But I'll take second look of the comments and check what I missed.

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.

And btw it seems you have not analyzed the code. The execute is both sync and async (as in it returns a future). Execute splits at a lower level to async_execute when called in an async environment

@janiversen
Copy link
Collaborator

Did you look at how we define the T which is the return from the mixin methods, that secures you can use the modhid as sync/async and satisfy mypy.

@janiversen
Copy link
Collaborator

you might be right that the execute in the different messages is only called from asyncio (and a couple of special examples),
however I am afraid of the side effects, especially since you gain absolutely nothing by just declaring the execute async, and the very least you would need to call an async method in the datastore.

@janiversen
Copy link
Collaborator

I will take a closer look, once you have a green CI.

@ilkka-ollakka
Copy link
Contributor Author

you might be right that the execute in the different messages is only called from asyncio (and a couple of special examples), however I am afraid of the side effects, especially since you gain absolutely nothing by just declaring the execute async, and the very least you would need to call an async method in the datastore.

Yes, My initial idea was to do that change in separate PR, that I haven't yet opened. Just to make this one only async/await changes and in that point of view, simpler to review. Referring to #2127 (comment)

So plan is to open separate PR that will add async_get/setValues to datastore and changes execute to use those with await, then fourth to change remote-datastore to use client as async flow.

@ilkka-ollakka ilkka-ollakka force-pushed the feat/messages_execute_to_async branch from 2162e3d to 459913b Compare April 2, 2024 07:59
@ilkka-ollakka
Copy link
Contributor Author

you might be right that the execute in the different messages is only called from asyncio (and a couple of special examples), however I am afraid of the side effects, especially since you gain absolutely nothing by just declaring the execute async, and the very least you would need to call an async method in the datastore.

I'm also fine to leave this PR open until I have opened the remaining PR's, so you can check those aswell to see end goal better?

@ilkka-ollakka
Copy link
Contributor Author

And btw it seems you have not analyzed the code. The execute is both sync and async (as in it returns a future). Execute splits at a lower level to async_execute when called in an async environment

Isn't that only in ModbusClient.execute codepath and not in server-codepath used ModbusRequest.execute ?

@janiversen
Copy link
Collaborator

Sorry you are right.

@janiversen
Copy link
Collaborator

I might not had said it correctly, but you need to attach this bottom-up, meaning e.g. this PR does not make sense until you have async datastore in place.

I review each PR as a closed part (which of course can form part of bigger picture), and each PR must be understandable and have a reason (it is not a reason, to state this is needed for a future change somewhere else). This PR adds "async" to a method that have no awaits, so it does not make sense.

I hope I have explained myself better....I am all in favor of having an async datastore, I just need to ensure that the code and the architecture do not break.

@ilkka-ollakka
Copy link
Contributor Author

I might not had said it correctly, but you need to attach this bottom-up, meaning e.g. this PR does not make sense until you have async datastore in place.

That makes sense

I review each PR as a closed part (which of course can form part of bigger picture), and each PR must be understandable and have a reason (it is not a reason, to state this is needed for a future change somewhere else). This PR adds "async" to a method that have no awaits, so it does not make sense.

Understandable

I hope I have explained myself better....I am all in favor of having an async datastore, I just need to ensure that the code and the architecture do not break.

Yes, I'll try to get the remaining PR's open so things can be justified and revied easier.

@janiversen
Copy link
Collaborator

btw just for information, you might have noticed that asyncio.py calls self.framer.processIncomingPacket which is not async (since it is shared with the sync client) and thus giving you problems. We are currently working a lot to refactor framer, and once complete it will be totally async.

In the meantime, you can replace the "lambda" with functols.partial calling the async executor, with async_execute as argument. This is not a needed change, but just to show you how to get around having 2 execute methods.

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.

You change all server execute to be async, but execute in asyncio is still sync ?

@@ -392,7 +392,7 @@ def __init__(self, base, req, retries=0):
async def delayed_resp(self):
"""Send a response to a received packet."""
await asyncio.sleep(0.05)
resp = self.req.execute(self.ctx)
resp = await self.req.execute(self.ctx)
Copy link
Collaborator

Choose a reason for hiding this comment

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

This seems wrong ! the client should be affected by a server change.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

that is RequestExecute, so for example ReadCoilsRequest, and as that mocks server-responses, it needs to do that async execute call.

Copy link
Collaborator

Choose a reason for hiding this comment

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

You are correct, thanks for pointing it out.

@ilkka-ollakka
Copy link
Contributor Author

You change all server execute to be async, but execute in asyncio is still sync ?

Yes, as execute is passed to callback for processIncomingPacket, and I wanted to scope down the changes as much as possible, I could have used partial there and remove the lambda, but I saw it as not required change at the moment in this scope.

@ilkka-ollakka ilkka-ollakka force-pushed the feat/messages_execute_to_async branch from 459913b to 58cda02 Compare April 5, 2024 15:31
@janiversen janiversen marked this pull request as draft April 9, 2024 15:55
@ilkka-ollakka ilkka-ollakka force-pushed the feat/messages_execute_to_async branch from 58cda02 to e6f6efa Compare April 9, 2024 19:08
@janiversen
Copy link
Collaborator

Closing this since it changed to several small PR

@janiversen janiversen closed this Apr 15, 2024
@ilkka-ollakka
Copy link
Contributor Author

Closing this since it changed to several small PR

Hi, I think you are mixing #2127 and this PR, as this PR was split out from the bigger PR, which is 2127 and if you look the change of this PR, it is one of the small changes

@janiversen
Copy link
Collaborator

Sounds correct, sorry for the mistake.

@janiversen janiversen reopened this Apr 16, 2024
@janiversen
Copy link
Collaborator

It's still draft though.

contains only async/await edits, no functional changes otherwise
@ilkka-ollakka ilkka-ollakka marked this pull request as ready for review April 16, 2024 07:32
@ilkka-ollakka
Copy link
Contributor Author

It's still draft though.

I rebased and re-opened the PR

@ilkka-ollakka
Copy link
Contributor Author

windows CI seems to fail on not having python

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.

The changes as such are OK, however I miss an await in asyncio.py where execute is called.... it seems to me that this PR guarantees that execute delivers a future.

@ilkka-ollakka
Copy link
Contributor Author

The changes as such are OK, however I miss an await in asyncio.py where execute is called.... it seems to me that this PR guarantees that execute delivers a future.

That is already in, and was merged in #2139

@janiversen
Copy link
Collaborator

As far as I can see it is not changed.

_async_execute() still contains the check iscoroutine(), which is no longer needed.

@ilkka-ollakka
Copy link
Contributor Author

As far as I can see it is not changed.

_async_execute() still contains the check iscoroutine(), which is no longer needed.

I'll add commit to remove it

all serverside executes are async calls now
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.

LGTM, thanks.

@janiversen janiversen merged commit 9c4466b into pymodbus-dev:dev Apr 16, 2024
1 check passed
janiversen added a commit that referenced this pull request Jun 27, 2024
* add _legacy_decoder to message rtu (#2119)

* Add generate_ssl() to TLS client as helper. (#2120)

* ASCII framer using message decode() (#2128)

* SOCKET/TLS framer using message decode(). (#2129)

* Fix decode for wrong mdap len.

* Streamline message class. (#2133)

* modbus_server: call execute in a way that those can be either coroutines or normal methods (#2139)

* Clean datastore setValues. (#2145)

* fixed kwargs not being expanded for actions on bit registers, adjusted tests to catch this issue (#2161)

* datastore: add async_setValues/getValues methods (#2165)

Co-authored-by: Ilkka Ollakka <ilkka.ollakka@cloudersolutions.com>

* Request/Response: change execute to be async method (#2142)

* Bump actions CI. (#2166)

* Fix usage of AsyncModbusTcpClient in client docs page (#2169)

* Sphinx: do not turn warnings into errors.

* Add minimal devcontainer. (#2172)

* Transaction id overrun.

* call async datastore from modbus server (#2144)

* Datastore will not return ExceptionResponse. (#2175)

* Describe zero_mode in ModbusSlaveContext.__init__ (#2187)

* Solve pylint error.

* Show error if example is run without support files. (#2189)

* Fix usage file names (#2194)

* Update client.rst (#2199)

* Transaction_id for serial == 0. (#2208)

* Remember to remove serial writer. (#2209)

* Fix writing to serial (rs485) on windows os. (#2191)

Co-authored-by: jan iversen <jancasacondor@gmail.com>

* test convert registers with 1234.... (#2217)

* Solve serial unrequested frame. (#2219)

* Log comm retries. (#2220)

* prepare v3.6.9.

* pylint.

* Remove python 3.8 from CI.

---------

Co-authored-by: Ilkka Ollakka <14361597+ilkka-ollakka@users.noreply.github.com>
Co-authored-by: sumguytho <48988983+sumguytho@users.noreply.github.com>
Co-authored-by: Ilkka Ollakka <ilkka.ollakka@cloudersolutions.com>
Co-authored-by: Yohrog <69543220+Yohrog@users.noreply.github.com>
Co-authored-by: James Cameron <90580716+jcameron-sso@users.noreply.github.com>
Co-authored-by: Qi Li <160646648+qili-eaton@users.noreply.github.com>
Co-authored-by: andrew-harness <75149647+andrew-harness@users.noreply.github.com>
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.

2 participants