Python DispatchWithEvents results in attribute error: - python

Using dispatch as win32com.client.Dispatch works fine, but produces attribute error: from DispatchWithEvents call win32com.client.WithEvents, problem exists until Temp\gen_py folder is removed
I can remove the Temp\gen_py folder at the very beginning before import win32com.client
path=r"C:\Users\omc\AppData\Local\Temp\gen_py"
rmtree(path, ignore_errors=True)
while os.path.exists(path):
pass
works fine for the 1st iteration in my test, but for the 2nd iteration for the same code produces attribute error:
from shutil import rmtree
path=r"C:\Users\omc\AppData\Local\Temp\gen_py"
rmtree(path, ignore_errors=True)
while os.path.exists(path):
pass
import win32com.client
class CanoeTestModuleEvents(object):
"""Handler for CANoe TestModule events"""
def OnStart(self):
print("< Test Module started >")
bTestModuleRunning = True
def OnStop(self,Reason) :
print("< Test Module stopped >")
bTestModuleRunning = False
if Reason == 0:
print("Test module was executed completely")
else:
if Reason== 1:
print("Test module was stopped by the user")
else:
print("Test module was stopped by measurement stop")
APP = win32com.client.Dispatch("CANoe.Application")
App.load("CANoeApplication.cfg")
# ---------------------------------------------------------------
# TestEnvironment Item(2)
# ---------------------------------------------------------------
TestEnvironment = self.App.Configuration.TestSetup.TestEnvironments.Item(1)
TestModule = self.TestEnvironment.Items.Item(2)
TestModule.TestVariant = TestVariant
App.Measurement.Start()
WaitForMeasurementStart()
win32com.client.WithEvents(TestModule, CanoeTestModuleEvents)
if MeasurementRunning():
TestModule.Start()
WaitForTestModuleStart()
while app.bTestModuleRunning == True:
pythoncom.PumpWaitingMessages()
time.sleep(.1)
App.Measurement.Stop()
# ---------------------------------------------------------------
# TestEnvironment Item(3)
# ---------------------------------------------------------------
TestEnvironment = self.App.Configuration.TestSetup.TestEnvironments.Item(1)
TestModule = self.TestEnvironment.Items.Item(3)
TestModule.TestVariant = TestVariant
App.Measurement.Start()
WaitForMeasurementStart()
win32com.client.WithEvents(TestModule, CanoeTestModuleEvents)
if MeasurementRunning():
TestModule.Start()
WaitForTestModuleStart()
while app.bTestModuleRunning == True:
pythoncom.PumpWaitingMessages()
time.sleep(.1)
App.Measurement.Stop()
AttributeError: object has no attribute
AttributeError: '<win32com.gen_py.CANoe 8.5 Type Library.ITestSetupItem instance at 0x49756368>' object has no attribute 'TestVariant'

The main reason for this attribute error is because your COM-server has shifted from late-binding (dynamic) to early binding (static).
In Late Binding, whenever a method is called, the object is queried for the method and if it succeeds, then the call can be made.
In Early Binding, the information of the object model is determined in advance from type information supplied by the object call. Early binding makes use of MakePy. Also, early binding is case sensitive.
There are two ways to fix this issue:
Use the dynamic module to force your code to work in a late-bound oriented way. Example use:
"win32com.client.dynamic.Dispatch()" instead of "win32com.client.Dispatch()"
Use camelcase sensitive keywords for the early bound oriented way. Example use:
"excel.Visible()" instead of "excel.VISIBLE()" or "excel.visible()"
Also, I think using early binding dependent methods by default create the gen_py folder every time.

Actually the problem here is that for some reason you need to cast the objects
csbTstMod = win32com.client.CastTo(csbTstMod, "ITSTestModule3")
print(csbTstMod.TestVariant)
This will now work.

Related

python 2 to 3 conversion, type instancemethod now a class method? Method no longer gets called

I am converting a large project I didn't write from python2 to python3. Most the conversion is working but some parts of our UI that applies configs does nothing. I think it is the way the method is being called and defined. An example is this:
There is a file StatusConfigWdg.py that sets a runFunc to a method defined in the same py file:
self.scriptRunner = RO.ScriptRunner.ScriptRunner(
master = master,
name = "%s Config" % (self.instName,),
dispatcher = tuiModel.dispatcher,
runFunc = self._runConfig,
statusBar = self.statusBar,
stateFunc = self.enableButtons,
)
Later on in the ScriptRunner class there is a method _continue that has these lines:
if not self._iterStack:
# just started; call run function,
# and if it's an iterator, put it on the stack
print("about to call run func: " + str(self.runFunc) + " type: " + str(type(self.runFunc)))
res = self.runFunc(self) #does not work in python3
print("called run func with result res " + str(res))
if not hasattr(res, "next"):
# function was a function, not a generator; all done
print("zomg all done")
self._setState(Done)
return
self._iterStack = [res]
In python2 this works all day, several different classes would use this method of getting _runConfig which was either defined in a base class or in another class for another science instrument to use.
Now in python3, only the _runConfig defined in other classes for the science instruments to use works, if it is a science instrument using it in the base StatusConfigWdg.py class, like the one above self._runConfig, it never gets called.
The output of those print statements is what I found interesting, between python2.7 and python3 they are different.
ECHELLE instrument under Python2 (works!)
script state running
set state 0
script runner continue [0]
about to call run func: <bound method StatusConfigWdg._runConfig of <TUI.Inst.Echelle.EchelleWindow.StatusConfigWdg instance at 0x116684f50>> type: <type 'instancemethod'>
called run func with result res <generator object _runConfig at 0x11aac8870>
RUNNING CONFIG!!! YAY
command list: ['calfilter Blue']
it is a wait command
waitCMD after
ECHELLE instrument under Python3 does not work. (a few more superfluous printouts was added to the code)
script state running
set state 0reason: None
really setting state to: 0
Do call backs
script runner continue [0]
in continue val: None
about to call run func: <bound method StatusConfigWdg._runConfig of <TUI.Inst.Echelle.EchelleWindow.StatusConfigWdg object .!toplevel24.!statusconfigwdg>> type: <class 'method'>
called run func with result res <generator object StatusConfigWdg._runConfig at 0x12956a190>
zomg all done
set state -1reason: None
really setting state to: -1
Do call backs
ending in the start here
So my big question is python3 not properly executing the runFunc that is assigned to _runConfig because of the difference in types that are stated in the printouts where the functioning python2 version is:
<type 'instancemethod'>
and the non functioning python3 version is:
type: <class 'method'>
And is it because of the way it is called?
res = self.runFunc(self)
the defined _runConfig in StatusConfigWdg.py:
def _runConfig(self, sr):
"""Script runner run function to modify the configuration.
Execute each command string in self.inputWdg.getStringList(). Wait for each command
to finish before starting the next and halt if any command fails.
"""
print("RUNNING CONFIG")
cmdList = self.inputWdg.getStringList()
print("command list: " + str(cmdList))
for cmdStr in cmdList:
print("waitCMD");
yield sr.waitCmd(
actor = self.getActorForCommand(cmdStr),
cmdStr = cmdStr,
)
print("waitCMD after");

How to get the current test name in cocotb

I am using cocotb version 1.5.2, and I would like to write an utility function to create reports/plots per test.
MWE: Implementing the get_test_name function so that the following test will print my_wonderful_test.
import cocotb
#cocotb.test()
async def my_wonderful_test(dut):
print(get_test_name(dut));
def get_test_name(dut):
pass # how do I get the current test from here?
You can use "name" attribute :
import cocotb
#cocotb.test()
async def my_wonderful_test(dut):
print(my_wonderful_test.name);
But not sure that exactly you want.
Thank you for the commenters, and thank you #FabienM for trying to give an answer. Thank you for #Tzane for trying to find an answer. You were close.
If you want to know a one liner answer
import cocotb;
def get_test_name():
return cocotb.regression_manager._test.name
but the underline prefix in _test maybe it will break in the future, but for since I was only concerned about version 1.5.2 this is OK for me.
Any way I implemented another method that scans the stack one level at a time and check if the frame is in a cocotb.test decorated function. This is also the method that cocotb uses to _discover_tests
It won't work if the test is in a closure, but I never use that, and I don't know if it is even supported.
import cocotb
import inspect;
import sys
#cocotb.test()
async def test_get_testname(dut):
print('Runnign from test ', get_test_name())
def get_test_name():
try:
return cocotb.regression_manager._test.name
except:
pass
cocotbdir = '/'.join(cocotb.__file__.split('/')[:-1])
frame = sys._getframe();
prev_frame = None
while frame is not None:
try:
# the [documentation](https://docs.python.org/2/library/inspect.html#inspect.getmodule)
# says
# Try to guess which module an object was defined in.
# Implying it may fail, wrapp in a try block and everything is fine
module = inspect.getmodule(frame.f_code)
func_name = inspect.getframeinfo(frame).function
if hasattr(module, func_name):
ff = getattr(module, func_name)
if isinstance(ff, cocotb.test):
return func_name
except:
pass
prev_frame = frame;
frame = frame.f_back;
# return None if fail to determine the test name
I don't know why my questions are so badly received
It was something simple that I preferred to engage more people

python modules, call main class in modules

this is my main module, I can call other module's function with it:
#main.py
from module_1 import module_1
class APP:
def option(self):
print("[1]:.....\n[2]:.......")
option = int(input(":"))
if (option == 1):
module_1().do_something_1() #I called module_1
APP().option()
Let's say the user chose the first option, the program will call the function in the module_1
#module_1.py
class module_1:
def do_something_1(self):
#.........................
again = input("Do you wanna start again?")
if (again == "Y"):
#what I'm trying to do is here, I want to return the option function again in main.py
else:
#................
And user wanted to restart program and Asked question with Y, How can the program return the main.py(APP().option())?
[UPTADE_1]
I actually thought a circular import would work for me here but got an error
main.py:
#main.py
from module_1 import module_1
class APP:
def option(self):
print("[1]:.....\n[2]:.......")
option = int(input(":"))
if (option == 1):
module_1().do_something_1() #I called module_1
APP().option()
module_1.py:
from main import APP
class module_1:
def do_something_1(self):
print(".........")
again = input("Do you wanna start again?")
if again=="Y":
return APP().option()
else:
#.......
I get this error:
.............................
from main import APP
ImportError: cannot import name 'APP' from partially initialized module 'main' (most likely due to a circular import)
.............................
[UPTADE_2](after Grismar's answer)
main.py:
from File import*
class Options:
def __init__(self):
#...................
def ask(self):
try:
option=int(input("==>"))
except Exception as error:
#......................................
return Options().ask()
if option==1:
File().delete_all_files_extension()
elif option==2:
#.........
elif option==3:
#.........
elif option==4:
#..........
elif option==5:
#.........
elif option==6:
sys.exit()
Options().ask()
Let's say the user chose the first option, the program will call the function in the File
import sys
import os
class File:
def __init__(self):
self.deleted_files_number = 0
self.deleted_files_extension_number = 0
def delete_all_files(self):
try:
path = input("[+]Path:")
except Exception as error:
print("ERROR:%s"%error)
#.....................
#I want to return the option function again in main.py
try:
file = open("log[delete_all_files].txt","w")
if (os.path.exists(path)==True):
for r,d,f in os.walk(path):
for file in f:
time.sleep(0.001)
print("[*]%s" %os.path.join(r,file))
os.remove(os.path.join(r,file))
open("log[delete_all_files].txt","a+").write(str(os.path.join(r,file)+"\n"))
self.deleted_files_number +=1
print("[*]Deleted %s file" %self.deleted_files_number)
again=input("[+]Do you wanna start again?[Y/N]").upper()
if (again=="Y"):
#here it is, the program has to go back to main.py so it can show what can be selected again.
else:
exit()
I mean, the program will return if an error or something different occurs within the module, not after the module has finished its work.
In your example, in main.py, you call APP.option(). APP.option() calls module_1().do_something_1(), where module_1 is a class imported from module_1.
You want module_1().do_something_1 to then call APP.option() again (under certain conditions).
A few remarks:
you would do well to follow Python naming conventions and name APP App.
there appears to be little or no utility in wrapping the module module_1's functionality in a class called module_1; all this gets you is two completely different entities sharing the same name. If you want to capture the functionality in a class, you should probably name it something that reminds developers using it of its function.
you're calling APP().option(), which means you're instantiating a new object every time, creating a new APP() with a new menu every time
most importantly, since APP.option() can call module_1().do_something_1 indefinitely and module_1().do_something_1 can call APP.option(), you run the risk of creating an application that ends up exhausting the recursion depth.
Object-oriented software design is all about picking the correct scope for your classes, giving them clear jobs that encapsulate behaviour and data that clearly goes together; to create software that's easier to maintain and extend.
The main function of your App class appears to be to present the user with options and execute matching functionality. The main function of module_1 appears to be to 'do something' and then return to the main loop. You want to present the user with the options again or allow them to exit the app, after 'doing something'.
From a design perspective, it seems to make more sense to have the App present the user with the 'start again' question - after all, it has nothing to do with whatever was done in module_1. If it does depend on what happens there, it makes sense to have the do_something_1() method return a result and base the decision to continue on the returned value.
You could even base that return value on the question you ask the user, although I don't like that design decision:
if again=="Y":
return True
else:
# ... do something else ...
return False
And in main.py:
def option(self):
again = True
while again:
print("[1]:.....\n[2]:.......")
option = int(input(":"))
if (option == 1):
again = module_1().do_something_1()
Another solution could be to create the module_1, passing the App it belongs to as an owner and keeping a reference to it in the object. Something like:
def __init__(self, owner):
self.owner = owner
# ...
if again=="Y":
return self.owner.option()
That solves the object creation issue, but is still circular of course.
You can have modules importing parts of each other and the correct way to do that would be by adding them to a package. However, you would still have to find a way to get around circular creation of the classes - if Class1 creates an instance of Class2, but
Class2 creates an instance of Class1, you can see where that's going.
What's wrong with something like this:
main.py
from module_1 import Class_1
class App:
def menu(self):
run_menu = True
while run_menu :
print("[1]:.....\n[2]:.......")
option = int(input(":"))
if (option == 1):
Class_1().do_something_1()
print(".........")
run_menu = input("Do you wanna start again?") == "Y"
App().menu()
module_1.py
class Class_1:
def do_something_1():
print('doing something')
return
The problem you're having is more a problem of design, not so much of the language. If you have an example of functionality where you feel the circular import is a requirement, you should probably post a question with that example specifically - but you're likely to be marked a duplicate of one of many questions asking about circular imports.
The short answer is: there's almost always a better solution.

ERROR: https://www (unittest.loader._FailedTest) while Unit Testing

Currently trying to write a unittest for a particular function. The error is shown below:
E
======================================================================
ERROR: https://www (unittest.loader._FailedTest)
----------------------------------------------------------------------
AttributeError: module '__main__' has no attribute 'https://www'
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (errors=1)
While the funtion itself has not been called within the Test function, I am trying to initialize a class Hasher inside the test. Commenting out the initialization line leads to the program running.
class Test(unittest.TestCase):
def test_YT(self):
self.H = Hasher()
self.assertTrue(True)
The code for the class is shown below:
class Hasher:
import hashlib
def __init__(self, hash_algo='md5'):
print('we getting here')
# TODO: support for more hash algos
self.hash_algo = hash_algo
def hash_file(self, filename):
return hashlib.md5(open(filename, 'rb').read()).hexdigest()
def compare_file_txt(self, filename, hash_txt_file):
# Useful for when there is an MD5 txt in the folder
hash1 = self.hash_file(filename)
if hash1 == open(hash_txt_file).readline():
return True
return False
def YT_create_hash(self, link, output_loc='test_hash.txt'):
DL = Downloader()
file_name = DL.YT_extract(link)
hash_txt = self.hash_file(os.getcwd() + '/' + file_name)
o_file = open(output_loc, 'w')
o_file.write(hash_txt)
o_file.close()
There's nothing in the class initialization which indicates that it is using 'https://www', so not really sure where this error is coming from.
My imports are in the form of:
from Hasher import *
from Downloader import *
And my file structure right now is:
It is almost never a good idea to use from my module import *. This can cause clashes with names imported from other modules, bugs due to the wrong function or class used, and unwanted side effects.
Try to always import only needed objects. Use tools like pylint or flake8, or the built-in hints in your IDE to be notified of similar issues.
In this concrete case, the statement from downloader import * most probably caused the problem.

python: class instance can't see self attribute

In my project i use module from known bt_manager to decode sbc audio stream. This module is python wrap for C-functions from rtpsbc library.
class SBCCodec:
def __init__(self, config):
import sys
try:
self.codec = ffi.verify(b'#include "rtpsbc.h"',
libraries=[b'rtpsbc'],
ext_package=b'rtpsbc')
except:
print 'Exception:', sys.exc_info()[0]
self.config = ffi.new('sbc_t *')
self.ts = ffi.new('unsigned int *', 0)
self.seq_num = ffi.new('unsigned int *', 0)
self._init_sbc_config(config)
self.codec.sbc_init(self.config, 0)
When i try to create SBCCodec class instance it gives me:
AttributeError: SBCCodec instance has no attribute 'codec'
You can see this attribute in the piece of code i posted above. It works with ffi-methods (ffi.verify, ffi.new). When i input those commands in ipython everything works correct without errors.
What have i missed?
As #Torxed has already mentioned the only way this would happen is if ffi.verify inside your try block throws an exception. If that happens self.codec will not be initialised. If that happens your code does not rethrow the exception and continues as normal after simply printing (which is not clean behaviour). The final statement then tries to call self.codec.config.sbc_init, that is it assumes that self.codec is already intialised, which is incorrect in this particular case and that is why you get the AttibuteError.
If you want to create the instance anyway regardless of the failure for ffi.verify at the start of init define self.codec = None and in your final statement insert a check such as:
if (self.codec != None ):
self.codec.sbc_init(self.config, 0)
Hope that helps.

Categories