pytransitions / transitions

A lightweight, object-oriented finite state machine implementation in Python with many extensions
MIT License
5.49k stars 524 forks source link

help needed. Correct way of using AsyncMachine and multiple objects #482

Closed jekel closed 3 years ago

jekel commented 3 years ago

Hello! i'm trying to implement client-server application via websockets and i have several doubts how to do it correctly to maintain state of every connected client. one global machine and many objects for each connection? or machine + object for each connection?

so, i have begin with several tests, to check how it works concurrently

base machine

class AsyncModel:
    def __init__(self, id_):
        self.req_id = id_

    async def prepare_model(self, _):
        print("prepare_model", self.req_id)

    async def before_change(self, _):
        print("before_change", self.req_id)

    async def after_change(self, _):
        print("After change", self.req_id)

transition = dict(trigger="start", source="Start", dest="Done",
                  prepare="prepare_model",
                  before=["before_change"],
                  after="after_change")

and several runnning types

i want to all models change their state at the same time


async def main():
    tasks = []
    machine = AsyncMachine(model=None,
                           states=["Start", "Done"],
                           transitions=[transition],
                           initial='Start',
                           send_event=True,
                           queued=True)
    for i in range(3):
        model = AsyncModel(id_=i)
        machine.add_model(model)

        tasks.append(model.start())

    await asyncio.gather(*tasks)

    for m in machine.models:
        machine.remove_model(m)

asyncio.run(main())

but the output is:


prepare_model 0
before_change 0
After change 0
prepare_model 1
before_change 1
After change 1
prepare_model 2
before_change 2
After change 2

if i create machine + model:


async def main():
    tasks = []

    for i in range(3):
        model = AsyncModel(id_=i)
        machine = AsyncMachine(model=model,
                               states=["Start", "Done"],
                               transitions=[transition],
                               initial='Start',
                               send_event=True,
                               queued=True)

        tasks.append(model.start())

    await asyncio.gather(*tasks)

the output is:

prepare_model 0
prepare_model 1
prepare_model 2
before_change 0
before_change 1
before_change 2
After change 0
After change 1
After change 2

whats the correct way ?

aleneum commented 3 years ago

Hello @jekel,

this looks like a question about how to use transitions. For usage questions, post on Stack Overflow, making sure to tag your question with the [transitions] and [python] tags. Posting there has several advantages:

Your question gains higher visibility since most developers look for help there. The targeted community is larger; Some people will even help you to formulate a good question. People get 'rewarded' with 'reputation' to help you. You also gain reputation in case this questions pops up more frequently. It's a win-win situation.

I will close this issue for now. Feel free to comment nevertheless. If an issue/bug/feature request arises I will reopen the issue. You can also post the link to your SO question here and come back should your SO question stay unanswered.

jekel commented 3 years ago

Hello @aleneum , it is the first time ever seen by me in github, that package author dont want to answer the question about his code and sends you to stackoverflow... its very pity. So, i'm forced to post the same question there, to be helped.

btw, i had another question -> how it is possible to make asyncmachine to run each transition inside context(use of contextvars) of each model? not shared between models/machine. (like LockedMachine maybe?)