Async function in class - Call it from another file? - python

I have a project that includes me using some SQL, I am wondering how can I call a function in the DB file?
class testing_db(commands.Cog):
def __init__(self, bot):
self.bot=bot
async def create_db_pool():
conn = await asyncpg.create_pool(database="db", user="user", host="nunyabusiness", password=DB_PW)
async def get_account_id(other_id):
mhm = "Do whatever I do here (I know how to do it but not call it from another file)"
loop.run_until_complete(create_db_pool())
def setup(bot):
bot.add_cog(testing_db(bot))

I'm not sure if this is the best/right way, but this is the first result on google so I wanted to share my workaround.
Add a function called run_main or whatever you want to call it. The function just needs to return your async run command:
def run_main():
return asyncio.run(main())
You can then call the run_main() function as normal from outside this file. Of course, substitute main() as necessary.
In your case, I would assume it's along the lines of adding a method like this:
def run_get_account_id(other_id):
return get_account_id(other_id)
Then import that method like normal. Note that this is probably a blocking call. I'm feeding a list to my main() so all the async stuff is happening inside main(). I would assume if you call run_get_account_id() individually, you will not get async behavior.

Related

Embed referenced before assignment

when I try running the code blow, I keep getting an error code that embed is already referenced even if I don't send the message ".help". Could any of you guys help me thanks.
import discord
import asyncio
from discord.ext import commands
client=discord.Client()
cliet=commands.Bot(command_prefix='.')
#client.event
async def on_ready():
print('help.py sucessfully loaded')
activity = discord.Activity(name='command watching for', type=discord.ActivityType.watching)
client = discord.Client(activity=activity)
#client.command()
async def help(ctx):
embed=discord.Embed(title="Title Here", description="Random Text Here", color=discord.Color.orange()), embed.set_footer(text="Inseart footer here"), embed.set_author(name=ctx.author.display_name, icon_url=ctx.author.avatar_url),
await ctx.channel.send(embed=embed)```
Not knowing a language is quite bad if you want to make something with the language.
Here is a minimal example of your problem:
class MyClass:
def __init__(self, arg):
self.arg = arg
def hello(self):
return "owo"
def my_func():
my_class = MyClass(1, my_class.hello()) # here, you we are trying to use `my_class` while assigning it at the same time which leads to the error.
my_func()
a more normal way such error is raised is
def my_func(x):
if x > 3:
y = 1 # if x is less than or equal to 3, y will not be assigned
return y # so what to return now? this will raise UnboundLocalVariable error
my_func(5) # works fine
my_func(100) # works fine
my_func(1) # oops, error
set_footer, set_author are bound methods of Embed object. You need an instance of it to use that. You would do these in separate lines
class MyClass:
def __init__(self, arg):
self.arg = arg
def hello(self):
return "owo"
def my_func():
my_class = MyClass(1)
my_class.hello() # now works!
my_func()
I won't be giving you the correct code for your specific problem, understand your problem, and fix it yourself.
See this line. (I added a few spaces to improve the readability)
embed = discord.Embed(title="Title Here", description="Random Text Here", color=discord.Color.orange()), embed.set_footer(text="Inseart footer here"), embed.set_author(name=ctx.author.display_name, icon_url=ctx.author.avatar_url),
What this is doing is creating a tuple, with a discord.Embed, followed by the return result of embed.set_footer, followed by the return result of embed.set_author. If we strip out the arguments, it basically comes down to this:
embed = discord.Embed(...), embed.set_footer(...), embed.set_author(...)
In basically every language, the right side of the assignment is evaluated first, and then set to the left side. This causes the method invocations to fail, because embed does not exist yet. You probably don't want embed to be a tuple anyway.
What you really want is this:
embed = discord.Embed(title="Title Here", description="Random Text Here", color=discord.Color.orange())
embed.set_footer(text="Inseart footer here")
embed.set_author(name=ctx.author.display_name, icon_url=ctx.author.avatar_url)
This creates the embed object first, and actually sets it to an Embed, and then runs the methods that set the other things.
I think you might use the command line
client= commands.Bot(command_prefix='.', help_command=None)
So there is not a default command message and you can use your own command.

Async Python - How to make a class __init__ run async functions within the class __init__

Let's say I have this
class Test():
def __init__(self, number):
self.number = number
await self.TestPrint()
async def TestPrint(self):
print(self.number)
As you can see this won't work since __init__ is not async and I cannot call await for the function
I want to be able to run TestPrint within __init__ assuming I want to maintain this function async
I also want this to have nothing to do with anything else other than the class (other function, other classes, main, etc.)
Thank you for your time.
Like chepner mentioned in the comments:
An asynchronous class method that creates the object and then calls the TestPrint method before returning it sounds more appropriate.
This is the preferred way above all else and why a lot of there are a lot of functions that initialize internal asyncio classes rather than you directly instantiating them.
That said, if you wish to have this close to the class you can use a #classmethod which can be async. Your code would look like this:
class Test():
def __init__(self, number):
self.number = number
async def TestPrint(self):
print(self.number)
#classmethod
async def with_print(cls, number):
self = cls(number)
await self.TestPrint()
return self
async def main():
t = await Test.with_print(123)
# 't' is now your Test instance.
...

Python: How do I refactor and structure code in this scenario?

I'm quite stuck on structuring the code in this scenario. Can anyone help me with this?
| module.py
import asyncio
class Server:
def __init__(self):
self.d = {}
#classmethod
async def create(cls):
self = cls()
await self.func()
return self
async def func(self):
await asyncio.sleep(5) # Some other async code here
self.a = 12
def reg(self, ev):
def decorator(func):
self.d[ev] = func()
retun func
return decorator
def reg2(self, ev, func):
self.d[ev] = func
| main.py
import asyncio
from module import Server
async def main():
ser = await Server.create()
# This would be another way... but i find the other way one neater
serv.reg2('msg', some_handler)
# I want to decorate and register this using
# reg func; but since object is not created yet
# how do i acomplish this?
# #ser.reg('msg')
async def some_handler():
...
if __name__ == "__main__":
asyncio.run(main())
Some key points of my aim:
The function 'some_handler' is never used other than the time for register. That is, the function soley exists to be registered and is not used anywhere else.
Since Server class needs an asynchronous initialisation, it cannot be done globally.
(I dont know whether this point is helpful) Generally only one Server instance is created for a single program. There wont be any other instance even in other modules.
How do I model my code to satisfy this senario? I have mentioned an alternate way to register the function, but I feel I am missing something, as some_handler isn't used anywhere else. I have thought about making Server class into a metaclass to do registering and converting the main() and some_handler() as parts of the metclass's class but I'm seeking for different views and opinions.

How to properly encapsulate an asyncio Process

I have a program executed in a subprocess. This program runs forever, reads a line from its stdin, processes it, and outputs a result on stdout. I have encapsulated it as follows:
class BrainProcess:
def __init__(self, filepath):
# starting the program in a subprocess
self._process = asyncio.run(self.create_process(filepath))
# check if the program could not be executed
if self._process.returncode is not None:
raise BrainException(f"Could not start process {filepath}")
#staticmethod
async def create_process(filepath):
process = await sp.create_subprocess_exec(
filepath, stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE)
return process
# destructor function
def __del__(self):
self._process.kill() # kill the program, since it never stops
# waiting for the program to terminate
# self._process.wait() is asynchronous so I use async.run() to execute it
asyncio.run(self._process.wait())
async def _send(self, msg):
b = bytes(msg + '\n', "utf-8")
self._process.stdin.write(b)
await self._process.stdin.drain()
async def _readline(self):
return await self._process.stdout.readline()
def send_start_cmd(self, size):
asyncio.run(self._send(f"START {size}"))
line = asyncio.run(self._readline())
print(line)
return line
From my understanding asyncio.run() is used to run asynchronous code in a synchronous context. That is why I use it at the following lines:
# in __init__
self._process = asyncio.run(self.create_process(filepath))
# in send_start_cmd
asyncio.run(self._send(f"START {size}"))
# ...
line = asyncio.run(self._readline())
# in __del__
asyncio.run(self._process.wait())
The first line seems to work properly (the process is created correctly), but the other throw exceptions that look like got Future <Future pending> attached to a different loop.
Code:
brain = BrainProcess("./test")
res = brain.send_start_cmd(20)
print(res)
So my questions are:
What do these errors mean ?
How do I fix them ?
Did I use asyncio.run() correctly ?
Is there a better way to encapsulate the process to send and retrieve data to/from it without making my whole application use async / await ?
asyncio.run is meant to be used for running a body of async code, and producing a well-defined result. The most typical example is running the whole program:
async def main():
# your application here
if __name__ == '__main__':
asyncio.run(main())
Of couurse, asyncio.run is not limited to that usage, it is perfectly possible to call it multiple times - but it will create a fresh event loop each time. This means you won't be able to share async-specific objects (such as futures or objects that refer to them) between invocations - which is precisely what you tried to do. If you want to completely hide the fact that you're using async, why use asyncio.subprocess in the first place, wouldn't the regular subprocess do just as well?
The simplest fix is to avoid asyncio.run and just stick to the same event loop. For example:
_loop = asyncio.get_event_loop()
class BrainProcess:
def __init__(self, filepath):
# starting the program in a subprocess
self._process = _loop.run_until_complete(self.create_process(filepath))
...
...
Is there a better way to encapsulate the process to send and retrieve data to/from it without making my whole application use async / await ?
The idea is precisely for the whole application to use async/await, otherwise you won't be able to take advantage of asyncio - e.g. you won't be able to parallelize your async code.

Callback inside inlineCallbacks function

Let's say I have a function like this:
def display(this, that):
print this, that
and a class:
class Runner(object):
def __init__(self, callback):
self.callback = callback
self.loop = twisted.internet.task.LoopingCall(repeat)
self.loop.start(0)
#defer.inlineCallbacks
def repeat(self):
this = yield do_this()
that = yield do_that()
if this and that:
# now I want to call the callback function
yield self.callback(this, that) # makes sense?
runner = Runner(display)
reactor.run()
Basically what I want to do is I want to create a Runner class which will do some specific tasks and every time it gets a result, it will call the given callback function. Instead of creating a new function which does a specific thing, I want to create a generic class which does only one thing. E.g:
class TwitterReader(object):
def __init__(self, callback):
...
...
#defer.inlineCallbacks
def get_messages(self):
...
...
yield callback(messages)
class MessageFilter(object):
def __init__(self):
self.bad_messages = open('bad_messages.txt', 'w')
self.twitter = TwitterReader(self.message_received)
def message_received(messages):
for message in messages:
for bad_word in BAD_WORDS:
if bad_word in message:
self.bad_messages.write(message)
break
I'm new to twisted. So, I'm not sure if this is the right way to do it. Is it?
Thanks
Your problem is that callback inside repeat should instead be self.callback.
Other than that your example should work exactly as written.
You'd only need to yield self.callback if it returned a deferred and you wanted to wait for the result before exiting the repeat function. In your example, your callback is a normal function (which effectively returns None), so there is no advantage to yielding - however it is allowed to yield non-deferred values so no harm is done. From the inlineCallbacks docs:
Things that are not Deferreds may also be yielded, and your generator
will be resumed with the same object sent back. This means yield
performs an operation roughly equivalent to maybeDeferred.
If your callback did return a deferred (eg, if it was also an inlineCallbacks decorated function) then yielding would pause execution of repeat until the deferred completed. This may or may not be desirable in your application.

Categories

Resources