python-trio / trio

Trio – a friendly Python library for async concurrency and I/O
https://trio.readthedocs.io
Other
6.2k stars 341 forks source link

Make it easier to see if a `Task` is created/running/exited #3079

Open jakkdl opened 2 months ago

jakkdl commented 2 months ago

It is currently possible to check the status of a task with https://docs.python.org/3/library/inspect.html#inspect.getcoroutinestate, but this is fairly obscure and not very readable. We should either document this, or add properties to Task that are easy to interface with. For implementing it, I think has_started() -> bool and has_exited -> bool would be a clean way of doing it.

I personally encountered this in https://github.com/python-trio/trio/issues/3035#issuecomment-2312328838 which would only be for internal use, but I'm guessing it might be useful enough to surface in the public API.

See discussion in Gitter: https://matrix.to/#/!OqDVTrmPstKzivLwZW:gitter.im/$umF5oOw9d2Z20gb95GF_C48HyBi2l6eD3ZW4r_4ark0?via=gitter.im&via=matrix.org&via=zoltan.site

mikenerone commented 2 months ago

I don't know the performance characteristics of getcoroutinestate(), but it might be faster to instead just set boolean attributes on the task object when the transition events actually happen.

A5rocks commented 2 months ago

This doesn't need to be the fastest and getcoroutinestate() seems to check booleans stored on the coroutine.

I would prefer an enum over two methods because otherwise we can represent states that are impossible (a task that exited but hasn't started?).


In addition, defined correctly, an enum allows for simple comparison:

>>> import enum
>>> class LifecycleEnum(enum.Enum):
...   A = enum.auto()
...   B = enum.auto()
...   C = enum.auto()
...
>>> LifecycleEnum.A.value < LifecycleEnum.B.value
True

(We can use specific numbers rather than enum.auto() to make sure we can add states if we want, later. But probably a bad idea.)

jakkdl commented 2 months ago

In general I find enums to be much more cumbersome to work with, mytask.has_exited vs mytask.run_status == trio.TaskRunStatus.HAS_EXITED, and if a user decides to write not mytask.has_started and mytask.exited then it's a pretty egregious logic error. And if you really do want complete enumeration you can directly use inspect.getcoroutinestate (which we'll definitely mention in the docstring). This argument is much weaker if we'd end up implementing this as more than just a thin wrapper around inspect.getcoroutinestate though.

TeamSpen210 commented 2 months ago

In some of my own code, I've used enums with propertys defined on them to encapsulate comparisons, nicely compact. The main concern is if we need to add another state later, people doing direct comparisons wouldn't handle that new state, while a bunch of properties would keep the code working still.