4

I am making a job class by inheriting from asyncio.Future with some custom attributes, and expect the job instance functions like the original Future.

When I call job.set_result inside a coroutine, it raises a Future object is not initialized error, then I tried to get future initialized by call asyncio.ensure_future and the same error appears.

I tried more and find that the future is usually created by loop.create_future(), however, there is no options to create my custom future.

Below is an example, How can I get my custom future initialized?

import asyncio
from dataclasses import dataclass

@dataclass
class Job(asyncio.Future):
    job_task: Callable
    real_future: asyncio.Future = None
    something: str = None

    def schedule(self):
        async def run():
            res = await self.job_task()
            self.set_result(res) # raise error, future not initialized
            return res
        self.real_future = asyncio.ensure_future(run())

async def main():
    async def task():
        await asyncio.sleep(1)
        return 1
    job = Job(task)
    job.schedule()
    await job

asyncio.run(main())

1
  • @user4815162342 wow, it worked. thanks, can you make an answer so I could accept this. Commented Mar 27, 2021 at 8:28

2 Answers 2

6

You need to call the superclass constructor yourself, something that dataclass's __init__ can't do because of reasons. But you also shouldn't try to re-implement __init__ yourself, since it is not exactly intuitive and you might mess up.

The Right Way To Do It (given that you want to keep using the @dataclass decorator) is leveraging the __post_init__ hook that dataclasses provide:

@dataclass
class Job(asyncio.Future):
    job_task: Callable
    real_future: asyncio.Future = None
    something: str = None

    def __post_init__(self)
        super().__init__()

    def schedule(self):
        async def run():
            res = await self.job_task()
            self.set_result(res)  # works now
            return res
        self.real_future = asyncio.ensure_future(run())
Sign up to request clarification or add additional context in comments.

Comments

2

The problem is that Future is not a dataclass, but your Job class inherits from it and uses the @dataclass decorator. This results in failure to invoke Future.__init__ and consequently failure to initialize the future object.

To fix the issue, don't use the @dataclass decorator in the first place. Instead, write an explicit __init__ that sets the necessary attributes, and invokes super().__init__() to initialize the Future correctly.

4 Comments

@downvoter How is this answer "not usefu" - it was posted as a comment, and the OP said it fixed their issue. There can be other ways to fix it, but if this answer is actually incorrect in some way, then please write a comment describing it, so that I can improve it.
It was me, I thought about starting a discussion but decided to just post my issues as an answer. I think it's not useful because it solves the problem by introducing something unnecessarily dangerous.
@Arne My answer was meant to imply to not use the @dataclass decorator in the first place, replacing its use with a manual __init__. I now see that it wasn't explicit and could be misunderstood as suggesting to write an explicit __init__ and keep using @dataclass, which was not my intention. I've now amended the answer to be explicit about it.
ah, fair enough.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.