1. 引子
大家还记得python如何操作CANoe吗?如果不懂,请再看一遍我的文章《如何用python操作CANoe》。或者参考CANoe Sample Configurations里的相关示例
示例中提供了一个python文件RunAllTests.py,用于演示python代码如何操作canoe功能
import time, os, msvcrt
from win32com.client import *
from win32com.client.connect import *
def DoEvents():
pythoncom.PumpWaitingMessages()
time.sleep(.1)
def DoEventsUntil(cond):
while not cond():
DoEvents()
class CanoeSync(object):
"""Wrapper class for CANoe Application object"""
Started = False
Stopped = False
ConfigPath = ""
def __init__(self):
app = DispatchEx('CANoe.Application')
app.Configuration.Modified = False
ver = app.Version
print('Loaded CANoe version ',
ver.major, '.',
ver.minor, '.',
ver.Build, '...', sep='')
self.App = app
self.Measurement = app.Measurement
self.Running = lambda : self.Measurement.Running
self.WaitForStart = lambda: DoEventsUntil(lambda: CanoeSync.Started)
self.WaitForStop = lambda: DoEventsUntil(lambda: CanoeSync.Stopped)
WithEvents(self.App.Measurement, CanoeMeasurementEvents)
def Load(self, cfgPath):
# current dir must point to the script file
cfg = os.path.join(os.curdir, cfgPath)
cfg = os.path.abspath(cfg)
print('Opening: ', cfg)
self.ConfigPath = os.path.dirname(cfg)
self.Configuration = self.App.Configuration
self.App.Open(cfg)
def LoadTestSetup(self, testsetup):
self.TestSetup = self.App.Configuration.TestSetup
path = os.path.join(self.ConfigPath, testsetup)
testenv = self.TestSetup.TestEnvironments.Add(path)
testenv = CastTo(testenv, "ITestEnvironment2")
# TestModules property to access the test modules
self.TestModules = []
self.TraverseTestItem(testenv, lambda tm: self.TestModules.append(CanoeTestModule(tm)))
def LoadTestConfiguration(self, testcfgname, testunits):
""" Adds a test configuration and initialize it with a list of existing test units """
tc = self.App.Configuration.TestConfigurations.Add()
tc.Name = testcfgname
tus = CastTo(tc.TestUnits, "ITestUnits2")
for tu in testunits:
tus.Add(tu)
# TestConfigs property to access the test configuration
self.TestConfigs = [CanoeTestConfiguration(tc)]
def Start(self):
if not self.Running():
self.Measurement.Start()
self.WaitForStart()
def Stop(self):
if self.Running():
self.Measurement.Stop()
self.WaitForStop()
def RunTestModules(self):
""" starts all test modules and waits for all of them to finish"""
# start all test modules
for tm in self.TestModules:
tm.Start()
# wait for test modules to stop
while not all([not tm.Enabled or tm.IsDone() for tm in app.TestModules]):
DoEvents()
def RunTestConfigs(self):
""" starts all test configurations and waits for all of them to finish"""
# start all test configurations
for tc in self.TestConfigs:
tc.Start()
# wait for test modules to stop
while not all([not tc.Enabled or tc.IsDone() for tc in app.TestConfigs]):
DoEvents()
def TraverseTestItem(self, parent, testf):
for test in parent.TestModules:
testf(test)
for folder in parent.Folders:
found = self.TraverseTestItem(folder, testf)
class CanoeMeasurementEvents(object):
"""Handler for CANoe measurement events"""
def OnStart(self):
CanoeSync.Started = True
CanoeSync.Stopped = False
print("< measurement started >")
def OnStop(self) :
CanoeSync.Started = False
CanoeSync.Stopped = True
print("< measurement stopped >")
class CanoeTestModule:
"""Wrapper class for CANoe TestModule object"""
def __init__(self, tm):
self.tm = tm
self.Events = DispatchWithEvents(tm, CanoeTestEvents)
self.Name = tm.Name
self.IsDone = lambda: self.Events.stopped
self.Enabled = tm.Enabled
def Start(self):
if self.tm.Enabled:
self.tm.Start()
self.Events.WaitForStart()
class CanoeTestConfiguration:
"""Wrapper class for a CANoe Test Configuration object"""
def __init__(self, tc):
self.tc = tc
self.Name = tc.Name
self.Events = DispatchWithEvents(tc, CanoeTestEvents)
self.IsDone = lambda: self.Events.stopped
self.Enabled = tc.Enabled
def Start(self):
if self.tc.Enabled:
self.tc.Start()
self.Events.WaitForStart()
class CanoeTestEvents:
"""Utility class to handle the test events"""
def __init__(self):
self.started = False
self.stopped = False
self.WaitForStart = lambda: DoEventsUntil(lambda: self.started)
self.WaitForStop = lambda: DoEventsUntil(lambda: self.stopped)
def OnStart(self):
self.started = True
self.stopped = False
print("<", self.Name, " started >")
def OnStop(self, reason):
self.started = False
self.stopped = True
print("<", self.Name, " stopped >")
# -----------------------------------------------------------------------------
# main
# -----------------------------------------------------------------------------
app = CanoeSync()
# loads the sample configuration
app.Load('CANoeConfig\PythonBasicEmpty.cfg')
# add test modules to the configuration
app.LoadTestSetup('TestEnvironments\Test Environment.tse')
# add a test configuration and a list of test units
app.LoadTestConfiguration('TestConfiguration', ['TestConfiguration\EasyTest\EasyTest.vtuexe'])
# start the measurement
app.Start()
# runs the test modules
app.RunTestModules()
# runs the test configurations
app.RunTestConfigs()
# wait for a keypress to end the program
print("Press any key to exit ...")
while not msvcrt.kbhit():
DoEvents()
# stops the measurement
app.Stop()
有人想用这个python文件RunAllTests.py作为第三方模块,导入到新的python文件中,通过调用RunAllTests.py中封装好的类和函数接口,来操作canoe
思路有问题吗?没问题!那问题出在哪了呢?
问题出在他在python执行文件中仅仅只是导入RunAllTests的代码,还没有实现调用里面的启动canoe应用的函数时,运行这个python文件,就已经把canoe应用启动了
import RunAllTest
他觉得很奇怪,他认为import模块应该只会把模块中的函数导入进来后以作调用使用
但实际上import RunAllTests会导致RunAllTests.py文件的执行,也就是会执行RunAllTests.py文件里下半部分的代码
# -----------------------------------------------------------------------------
# main
# -----------------------------------------------------------------------------
app = CanoeSync()
# loads the sample configuration
app.Load('CANoeConfig\PythonBasicEmpty.cfg')
# add test modules to the configuration
app.LoadTestSetup('TestEnvironments\Test Environment.tse')
# add a test configuration and a list of test units
app.LoadTestConfiguration('TestConfiguration', ['TestConfiguration\EasyTest\EasyTest.vtuexe'])
# start the measurement
app.Start()
# runs the test modules
app.RunTestModules()
# runs the test configurations
app.RunTestConfigs()
# wait for a keypress to end the program
print("Press any key to exit ...")
while not msvcrt.kbhit():
DoEvents()
# stops the measurement
app.Stop()
如此就造成了只是导入RunAllTests文件,却执行了上面的代码,上面的代码就会启动canoe应用
上面的python文件代码可能有点繁琐,现在我们准备一个python文件lib.py作为第三方模块,里面的代码为
b = 5
print("b = %d" %b)
然后再准备一个新的python文件test.py,里面只有一行代码
import lib
最后执行test.py,你会发现打印结果为
以上就证明了导入模块会导致模块中的代码的执行
2. 导入模块时做了什么
python代码文件按照功能可以分为两种类型:
- 用于执行的可执行程序文件
- 不用于执行,仅用于被其它python源码文件导入的模块文件
python的import是在程序运行期间执行的,并非像其它很多语言一样是在编译期间执行。也就是说,import可以出现在任何地方,只有执行到这个import行时,才会执行导入操作。且在import某个模块之前,无法访问这个模块的属性
python在import导入模块时,首先搜索模块的路径,然后编译并执行这个模块文件
- 首先在内存中为每个待导入的模块构建module类的实例:模块对象。这个模块对象目前是空对象,这个对象的名称为全局变量
- 构造空模块实例后,将编译、执行模块文件,并按照一定的规则将一些结果放进这个模块对象中
执行模块文件(已完成编译)的时候,按照一般的执行流程执行:一行一行地、以代码块为单元执行。一般地,模块文件中只用来声明变量、函数等属性,以便提供给导入它的模块使用,而不应该有其他任何操作性的行为,比如print()操作不应该出现在模块文件中,但这并非强制
总之,执行完模块文件后,这个模块文件将有一个自己的全局名称空间,在此模块文件中定义的变量、函数等属性,都会记录在此名称空间中
最后,模块的这些属性都会保存到模块对象中。由于这个模块对象赋值给了模块变量,所以通过变量可以访问到这个对象中的属性(比如变量、函数等),也就是模块文件内定义的全局属性
所以上面test.py导入lib.py文件作为模块时,lib.py文件里的变量b就变成了test.py的全局变量,test.py就可以访问这个变量了
print(lib.b)
3. 如何解决问题
最后,如何解决最开始的问题?
- 把后半部分执行的代码注释掉
- 把后半部分执行的代码放入if __name__ == "__main__"判断语句中
if __name__ == "__main__"的作用是:
如果此模块被直接运行时,python会把特殊变量__name__置为__main__,此时if语句判断为True,执行里面的代码。如果此模块不是被直接运行,而是被其他模块导入调用,那么__name__取值为该模块的名字,那么if语句判断为False,不会执行里面的代码,避免在导入此模块时还顺带运行了里面的代码并输出了此模块的结果