How to use Instance Method with Lamda, Threads and Named Arugments - python-3.x

The current code below produces an error. I keep tweaking the syntax but keep getting errors. I know I am close. I am tring to use a lambda inside an instance method, to start a thread and the target is an instance method with named arguments. I am seeing various forms of wrong positional arguments Can anyone help?
arguments = {'resource_group_name': resource_group_name,
'vm_name': vm_name,
'script': script,
'parameters': parameters,
'command_id': command_id}
thread = Thread(name=vm_name,
target=lambda self, q, **kwargs: q.put(self.run_command(kwargs)),
args=(que, arguments))
thread.start()

Was able to get it working with this:
thread = Thread(name=vm_name,
target=lambda self, q, kwargs: q.put(self.run_command(**kwargs)),
args=(self, que, arguments))

Related

finding the caller object given its name only

I want to find the caller callable from within the called object, without explcitely forwarding the caller to the called as an object.
My current code looks something like this:
class Boo:
#classmethod
def foo(cls, aa, b2=2):
_ret = aa + b2
autolog(fn=Boo.foo, values={"input": locals(), "output": _ret}, message="This is what it should look like")
autolog_nameless(values={"input": locals(), "output": _ret}, message="This would be convenient")
return _ret
and yields
DEBUG | Boo.foo with aa=3.14159, b2=2 yields 5.14159. Message: This is what it should look like
DEBUG | cls=<class '__main__.Boo'>, aa=3.14159, b2=2, _ret=5.14159 yields 5.14159. Message: This would be convenient
The method autolog gets the locals() and the caller method fn, and parses them using the signature of the caller. This works nice and provides the desired output, but requires passing the caller as an object - something I'd like to avoid as I'm refractoring to include this feature and have about 1000 places to modify.
What I'd like to achieve is: pass locals() only; get the name of the caller within autolog_nameless, using inspect.stack()[1][3] or rather inspect.currentframe().f_back.f_code.co_name (latter has much less overhead), and using this - an possibly the information in locals() - find the caller object to inspect it for its signature.
The method autolog_nameless gets cls, actually the class as part of locals() (or would get self if the caller was a simple method), but I can't really do anything with it.
I'd think all the information required is given, but I just can't find a solution. Any help is greatly appreciated.
As it turns out it's quite simple: listing the methods of the class object found in locals() and searching by name should do the trick.
Code, without error checking:
# getting all methods of the class
methods = inspect.getmembers(locals()['cls'], predicate=inspect.ismethod)
# finding the callers name; won't work within the list comprehension for scope issues
_name = inspect.currentframe().f_back.f_code.co_name
# methods is a list of tuples, each tuple holds the name and the method object
fn = [x for x in methods if x[0] == _name][0][1]
and fn is the caller object to check the signature.
Note, locals()['cls'] works here as in the example we have a classmethod, but this is just the object that the called method belongs to.

Too many positional arguments are given

Hi there smart people,
I have a small program where a combobox should be updated with a new list, depending on a User entry, when a Button is clicked.
Infact I would assume that no arguments need to be given since the called functions "gets" the user entries and then updates the combobox. No additional external Info needed.
Unfortunatley I get the Error:
TypeError:Func_Update_MA() takes 1positional argument but 2 were given.
How can I solve this issue?
To be honest I dont really get the whole "self" thing but I tried pretty much every combination of using self, not using it and combining it with something like args* or kwargs** (another mystery to me)
If you need more code I will provide it off course.
Thanks alot in advance!
class Class_MA_Win():
def __init__(self, Win_MA_Sel, Cockpit_Win):
Btt_Update_MA = Button(self.Mitarbeiter_Selection_Win, text="Liste Updaten")
Btt_Update_MA.bind("<Button-1>",self.Func_Update_MA)
Btt_Update_MA.grid(column=2, row=3, padx=10, pady=10)
def Func_Update_MA(self):
Entry_name = self.Ent_first_name_MA.get()
Entry_lastname = self.Ent_last_name_MA.get()
Entry_ID = self.Ent_ID_MA.get()
Whenever you use widget.bind(...), it will return an event object with a number of attributes describing the event. This is then passed to your func Func_Update_MA which accepts no argument, thus the error.
To solve this, simply accept the event as an arg:
def Func_Update_MA(self,event=None):
...
Also you mentioned about args and kwargs but you seem to wrongly position the asterisks. The correct is *args and **kwargs, like so:
def Func_Update_MA(self, *args, **kwargs):
...

Binding method with multiple arguments to <<ListboxSelect>>

Trying to make a method execute on the selection of an item in a tkinter listbox. I can bind some function to is using .bind("<<ListboxSelect>>", method), but if i try to give any arguments to it, like method(argumentone, argumenttwo), the method no longer executes on the selection of an item from the listbox. It is however executed at startup when Mainloop starts. Furthermore, giving arguments to a method is problematic, as i cant pass the event argument already passed to any bound method, as it is not assigned to any variable beforehand. I'd like to be able to pass the event and one other variable to some method ideally. Any help on achieving this would be very welcome. Some example code of what I'm trying to achieve
def onSelection(event, foo):
widget = event.widget
print(widget.curselection())
print(foo)
sam = "sam"
chars = Listbox(main)
chars.insert(someList)
chars.bind("<<ListboxSelect>>", onSelection(evt, sam))
Note that this code doesn't work. evt isn't defined.
When you specify a function in bind() that ends in parenthesis it just runs the function without bind generating an event. Instead you have to give bind a name. You can accomplish this with lambda or functools.partial. I'll use lambda in my example.
Bind is generating an event when triggered, so lambda will have to take it as input. Then call onSelection() with desired arguments.
from tkinter import *
main = Tk()
def onSelection(event, foo):
widget = event.widget
print(widget.curselection())
print(foo)
someList = ['Stilton', 'Brie', 'Edam', 'Cheddar', 'Ilchester']
chars = Listbox(main)
chars.pack(padx=10, pady=10)
for item in someList:
chars.insert("end", item)
sam = "sam"
chars.bind("<<ListboxSelect>>", lambda event: onSelection(event, sam))
# Take care of event created by bind ----^ ^
# Pass the event as well as argument to callback function ----|
main.mainloop()

Python 3.5 asyncio execute coroutine on event loop from synchronous code in different thread

I am hoping someone can help me here.
I have an object that has the ability to have attributes that return coroutine objects. This works beautifully, however I have a situation where I need to get the results of the coroutine object from synchronous code in a separate thread, while the event loop is currently running. The code I came up with is:
def get_sync(self, key: str, default: typing.Any=None) -> typing.Any:
"""
Get an attribute synchronously and safely.
Note:
This does nothing special if an attribute is synchronous. It only
really has a use for asynchronous attributes. It processes
asynchronous attributes synchronously, blocking everything until
the attribute is processed. This helps when running SQL code that
cannot run asynchronously in coroutines.
Args:
key (str): The Config object's attribute name, as a string.
default (Any): The value to use if the Config object does not have
the given attribute. Defaults to None.
Returns:
Any: The vale of the Config object's attribute, or the default
value if the Config object does not have the given attribute.
"""
ret = self.get(key, default)
if asyncio.iscoroutine(ret):
if loop.is_running():
loop2 = asyncio.new_event_loop()
try:
ret = loop2.run_until_complete(ret)
finally:
loop2.close()
else:
ret = loop.run_until_complete(ret)
return ret
What I am looking for is a safe way to synchronously get the results of a coroutine object in a multithreaded environment. self.get() can return a coroutine object, for attributes I have set to provide them. The issues I have found are: If the event loop is running or not. After searching for a few hours on stack overflow and a few other sites, my (broken) solution is above. If the loop is running, I make a new event loop and run my coroutine in the new event loop. This works, except that the code hangs forever on the ret = loop2.run_until_complete(ret) line.
Right now, I have the following scenarios with results:
results of self.get() is not a coroutine
Returns results. [Good]
results of self.get() is a coroutine & event loop is not running (basically in same thread as the event loop)
Returns results. [Good]
results of self.get() is a coroutine & event loop is running (basically in a different thread than the event loop)
Hangs forever waiting for results. [Bad]
Does anyone know how I can go about fixing the bad result so I can get the value I need? Thanks.
I hope I made some sense here.
I do have a good, and valid reason to be using threads; specifically I am using SQLAlchemy which is not async and I punt the SQLAlchemy code to a ThreadPoolExecutor to handle it safely. However, I need to be able to query these asynchronous attributes from within these threads for the SQLAlchemy code to get certain configuration values safely. And no, I won't switch away from SQLAlchemy to another system just in order to accomplish what I need, so please do not offer alternatives to it. The project is too far along to switch something so fundamental to it.
I tried using asyncio.run_coroutine_threadsafe() and loop.call_soon_threadsafe() and both failed. So far, this has gotten the farthest on making it work, I feel like I am just missing something obvious.
When I get a chance, I will write some code that provides an example of the problem.
Ok, I implemented an example case, and it worked the way I would expect. So it is likely my problem is elsewhere in the code. Leaving this open and will change the question to fit my real problem if I need.
Does anyone have any possible ideas as to why a concurrent.futures.Future from asyncio.run_coroutine_threadsafe() would hang forever rather than return a result?
My example code that does not duplicate my error, unfortunately, is below:
import asyncio
import typing
loop = asyncio.get_event_loop()
class ConfigSimpleAttr:
__slots__ = ('value', '_is_async')
def __init__(
self,
value: typing.Any,
is_async: bool=False
):
self.value = value
self._is_async = is_async
async def _get_async(self):
return self.value
def __get__(self, inst, cls):
if self._is_async and loop.is_running():
return self._get_async()
else:
return self.value
class BaseConfig:
__slots__ = ()
attr1 = ConfigSimpleAttr(10, True)
attr2 = ConfigSimpleAttr(20, True)
def get(self, key: str, default: typing.Any=None) -> typing.Any:
return getattr(self, key, default)
def get_sync(self, key: str, default: typing.Any=None) -> typing.Any:
ret = self.get(key, default)
if asyncio.iscoroutine(ret):
if loop.is_running():
fut = asyncio.run_coroutine_threadsafe(ret, loop)
print(fut, fut.running())
ret = fut.result()
else:
ret = loop.run_until_complete(ret)
return ret
config = BaseConfig()
def example_func():
return config.get_sync('attr1')
async def main():
a1 = await loop.run_in_executor(None, example_func)
a2 = await config.attr2
val = a1 + a2
print('{a1} + {a2} = {val}'.format(a1=a1, a2=a2, val=val))
return val
loop.run_until_complete(main())
This is the stripped down version of exactly what my code is doing, and the example works, even if my actual application doesn't. I am stuck as far as where to look for answers. Suggestions are welcome as to where to try to track down my "stuck forever" problem, even if my code above doesn't actually duplicate the problem.
It is very unlikely that you need to run several event loops at the same time, so this part looks quite wrong:
if loop.is_running():
loop2 = asyncio.new_event_loop()
try:
ret = loop2.run_until_complete(ret)
finally:
loop2.close()
else:
ret = loop.run_until_complete(ret)
Even testing whether the loop is running or not doesn't seem to be the right approach. It's probably better to give explicitly the (only) running loop to get_sync and schedule the coroutine using run_coroutine_threadsafe:
def get_sync(self, key, loop):
ret = self.get(key, default)
if not asyncio.iscoroutine(ret):
return ret
future = asyncio.run_coroutine_threadsafe(ret, loop)
return future.result()
EDIT: Hanging problems can be related to tasks being scheduled in the wrong loop (e.g. forgetting about the optional loop argument when calling a coroutine). This kind of problem should be easier to debug with the PR 303 (now merged): a RuntimeError is raised instead when the loop and the future don't match. So you might want to run your tests with the latest version of asyncio.
Ok, I got my code working, by taking a different approach to it. The problem was tied with using something that had file IO, which I was converting into a coroutine using loop.run_in_executor() on the file IO components. Then, I was trying to use this in a sync function being called from another thread, processed using another loop.run_in_executor() on that function. This is a very important routine in my code (called probably a million times or more during the execution of my short-running code), and I made a decision that my logic was just getting too complicated. So... I uncomplicated it. Now, if I want to use the file IO components asynchronously, I explicitly use my "get_async()" method, otherwise, I use my attribute through normal attribute access.
By removing the complexity of my logic, it made the code cleaner, easier to understand, and even more importantly, it actually works. While I am not 100% certain that I know the root cause of the issue (I believe it has something to do with a thread processing an attribute, which then in turn starts another thread that tries to read the attribute before it is processed, which caused something like a race condition and halting my code, but I could never duplicate the error outside of my application unfortunately to completely prove it out), I was able to get past it and continue with my development efforts.

Creating custom slots and signals old style

I'm trying to create my own custom signal but for whatever reason it's not going into the function. Any ideas where I went wrong?
def __init__(self):
...
self.connect( self, QtCore.SIGNAL( 'dagEvent(type, *args)' ), self.OnDagEvent)
def someFunc(self, ...):
...
self.emit(QtCore.SIGNAL('dagEvent()'), type, *args)
def OnDagEvent( self, eventType, eventData ):
print 'Test'
The problem is with the way you are creating the signature for you custom signal. There are a few ways you are allow to do it, but the way you have it now isn't one of them.
When you define your connection, the way you are doing it should be causing an error to be raised:
# not a valid type of signature
QtCore.SIGNAL( 'dagEvent(type, *args)' )
And even if that were allowed to be created, when you emit later, you are not referencing the same signature anyways:
# if it were even allowed, would have to be: dagEvent(type, *args)
self.emit(QtCore.SIGNAL('dagEvent()'), type, *args)
Old-style Signal and Slot Support
The easiest way to create a custom signal from PyQt is to simply use the callable name only:
self.connect(self, QtCore.SIGNAL('dagEvent'), self.OnDagEvent)
...
# "type" is a builtin. I renamed it to type_
self.emit(QtCore.SIGNAL('dagEvent'), type_, *args)
This approach does not care what args you decide to pass it. You can pass anything you want.
If you want to specifically control the signature, you can define builtin types:
self.connect(self, QtCore.SIGNAL('dagEvent(int,str,int)'), self.OnDagEvent)
...
self.emit(QtCore.SIGNAL('dagEvent(int,str,int)'), i1, s2, i3)
If you fail to use the right signature in the emit, it will not be called, and passing the wrong types when emitting will raise an error.
Now if you want to somewhat define a signature, but not limit it to any basic type, and allow any python object, you can do this:
self.connect(self, QtCore.SIGNAL('dagEvent(PyQt_PyObject)'), self.OnDagEvent)
...
self.emit(QtCore.SIGNAL('dagEvent(PyQt_PyObject)'), foo)
This will allow any single python object to be passed, but specifically says it expects 1 argument.

Resources