Pass Python object as argument to function in "parfeval" - python

I am trying to pass one Python object as an argument to a function that I am evaluating in the background with parfeval. The Python object is an instance of a Python class, and I detail it below. However, to reproduce the error, I will exemplify with a Python dictionary... However, simply using struct(pydict) would not work because I would lose all the attributes and methods in the Python class.
Assume the Python dictionary is
o = py.dict(pyargs('soup',3.57,'bread',2.29,'bacon',3.91,'salad',5.00));
and the function is
function t = testFunc(x)
t = x{'soup'};
end
If I evaluate the function, I get the correct answer:
>> testFunc(o)
ans =
3.5700
However, if I use parfeval, I get the following error:
>> f = parfeval(#testFunc,1,o);
>> fetchOutputs(f)
Error using parallel.Future/fetchOutputs
One or more futures resulted in an error.
Caused by:
Error using testFunc (line 2)
Invalid or deleted object.
Is there a workaround to this error that doesn't mean I have to recode my whole Python class?
Here is the preview of the object I want to pass as a function to parfeval:
clt =
Python Client with properties:
enforce_enums: 1
api_key: [1×45 py.str]
request_number: [1×1 py.int]
logger: [1×1 py.logging.Logger]
session: [1×1 py.authlib.integrations.httpx_client.oauth2_client.OAuth2Client]
token_metadata: [1×1 py.tda.auth.TokenMetadata]
<tda.client.synchronous.Client object at 0x000001ECA08EAE50>
I didn't find any restrictions in the documentation that says that parfeval function inputs cannot be anything...
https://www.mathworks.com/help/matlab/ref/parfeval.html
"X1,...,Xm — Input arguments
comma-separated list of variables or expressions... Input arguments, specified as a comma-separated list of variables or expressions"

One of the limitations of the MATLAB->Python support is that Python objects cannot be serialized. parfeval (and other parallel constructs) require serialization to transfer data from one MATLAB process to another.
You might be able to work around this by having each worker build the data structure directly and storing it / accessing it via parallel.pool.Constant, like this:
oC = parallel.pool.Constant(#() py.dict(pyargs('soup',3.57,'bread',2.29,'bacon',3.91,'salad',5.00)));
fetchOutputs(parfeval(#(c) c.Value{'salad'}, 1, oC))

Related

Type Mismatch using win32com in Python

I am trying to use Python to run an external program using its COM interface. In general, everything works as expected. The only problem I’m having is that any time I try to retrieve a value from the program using their GetValue method, I get the following error (code included).
This method is supposed to overwrite the value of my Value variable. The function takes 3 arguments, and I have verified that the error is related to the first argument (Value) by using separate functions successfully that only take the last 2 arguments. The documentation for the COM interface mentions this issue for C#, and their recommended solution was to define Value as an Object type variable and set the value to null. I have it set to None below, but I’ve also tried setting it to different int/float/string with no success.
I receive this error whether or not I use the EnsureModule command before dispatch.
See the code below
Object = win32.Dispatch(“COMObject”)
Object.OpenFile(“Filename”)
Value = None
Object.GetValue(Value,0,1)
Exception has occurred:com_error
(-2147352571,’type mismatch’,None,1)

Mypy rejects type objects created with `type(name, (bases,), {})`

I'm having some trouble getting mypy to accept type objects. I am
convinced I'm just doing it wrong but my google searches have not led me to any answers so far.
class Good(object):
a = 1
def works(thing: Good):
print(thing.a)
o = Good()
works(o)
Bad = type('Bad', (object, ), dict(a=1))
def fails_mypy(thing: Bad):
print(thing.a)
s = Bad()
fails_mypy(s)
Things constructed like 'Good' are ok, while things constructed like 'Bad' fail mypy checks with:
error: Invalid type "test.Bad"
error: Bad? has no attribute "a"
Based on the Unsupported Python Features section of mypys wiki, runtime creation of classes like this isn't currently supported. It cannot understand what Bad is in your function definition. Using reveal_type(Good) and reveal_type(Bad) when executing mypy should make this clear.
An approach to silence these is by using Any. Either using Python 3.6 variable annotation syntax:
Bad: Any = type('Bad', (), {'a':1})
or, with Python < 3.6:
Bad = type('Bad', (), {'a':1}) # type: Any
(in both cases Any should first be imported from typing)
of course, this basically means that your function now accepts anything. A price to pay, but that's what you get with dynamic languages; since Bar is defined at runtime, it can theoretically be anything :-)

python get members of COM ole object

In python 3.6 I'm using an COM interface to communicate with Excel and Word, in this case Word for automated reporting as the data processing is done in python.
I don't know how python can get the members of such a COM object similar to the use of the dir() function.
(Previously using Matlab, i would use the .get or .invoke methods to get this)
So the code would be:
def wordOpen(wordfile):
pythoncom.CoInitializeEx(pythoncom.COINIT_APARTMENTTHREADED)
wApp = win32com.client.DispatchEx('Word.Application')
wDoc = wApp.Documents.Open(wordfile)
wApp.Visible = 1
wApp.Activate
wRange = wDoc.Content
return wApp, wDoc, wRange
wApp, wDoc, wRange = wordOpen(wordfile)
dir(wDoc)
.. which does not provide me the list of methods and properties of the Word document object (similar for wApp and wRange).
Similarly I've tried inspect.getmembers(wDoc) but this also does not provide the list of methods/properties that I'm looking for.
For the same thing when communicating with Excel, I used to go into the VBA editor and get a list there, but is there any method to do this from the IPython
console directly?
Did some more searching and figured it out myself. The above example uses 'dynamic dispatching' and we want to use 'static dispatching' instead. That does not change the code, it only entails a one-off action.
Dynamic is a quick-and-dirty way to create these objects, and then python knows nothing about the type of object.
Static means that you have to run makepy.py from the command line to create a type library (in this case for Word objects), and after that has been done once, python knows all the info about the object every next time it launches the COM object.
The procedure is very simple and comparable to the VBA action of adding References to your project.

Python multiprocessing shared variable list can't append

I'm trying to do multiprocessing with Python and there is a variable that needs to be shared across all instances.
The variable to be shared is a list that stores variables of types by appending some user-defined class
<class 'output_handlers.email_output_handler.email_output_handler'>
<class 'output_handlers.dw_output_handler.dw_output_handler'>
as seen from print(type(var)).
I attempted to use a Manager to append the same variables to it's generated list before passing it into the threads:
from multiprocessing import Manager
...
man = Manager()
output_handlers = man.list()
output_handlers.append(variable)
The above yields error TypeError: cannot serialize '_io.TextIOWrapper' object even though it can append simple types like integers and chars.
Attempting to do
tmp = []
tmp.append(variables)
output_handlers = man.list(tmp)
also yields the same error.
I also wanted to use multiprocessing.Value() to explicitly make the list sharable, but I never found the ctype code for the list
Can anyone help out with this problem?

Namespacing issue in Python

I am using a program where Python is the native scripting language. Unfortunately, they have a native function that uses the name bytes. This causes a problem when I am trying to use the actual bytes built-in function, and it thinks I am referencing that built-in variable. I will show you what I mean, one object as the following built-in code:
def receive(row, table, message, bytes):
#This is defined in the GUI
So, row, table, message, and bytes are all passed in as arguments, effectively overwriting the name bytes. So if I were to say bytes(something).decode() I get a TypeError: 'bytes' object is not callable
Is there any way to get out of this jam?
Use a different name for the fourth parameter (if you can change the signature of the function)
def receive(row, table, message, bytes_):
#This is defined in the GUI
Your problem is similar to this one. Just from builtins import bytes as _bytes; this will let you do _bytes(something).decode().
Although renaming the fourth argument is a better solution.

Categories