I'm developping a PyQT application. This application is able to load some data from a database, and then do various analysis on these data. All of this works. But as the analyses can be quite complicated, and as a will not be the only user, I had to develop a system with users defined script.
Basically, there's a text editor where the user can program his own small python script (with functions). Then the user can save the script or execute it, by loading the file as a module (within the application).
Here, there a simplified version of my application.
The core of the application is in My_apps.py
and the plugins are here in the same folder i.e. Plugin_A.py
this is the code of My_apps.py:
import sys,os
class Analysis(object):
def __init__(self):
print "I'm the core of the application, I do some analysis etc..."
def Analyze_Stuff(self):
self.Amplitudes_1=[1,2,3,1,2,3]
class Plugins(object):
def __init__(self):
newpath = "C:\Users\Antoine.Valera.NEUROSECRETION\Desktop\Model" #where the file is
sys.path.append(newpath)
Plugin_List=[]
for module in os.listdir(newpath):
if os.path.splitext(module)[1] == ".py":
module=module.replace(".py","")
Plugin_List.append(module)
for plugin in Plugin_List:
a=__import__(plugin)
setattr(self,plugin,a)
def Execute_a_Plugin(self):
Plugins.Plugin_A.External_Function(self)
if __name__ == "__main__":
Analysis=Analysis()
Plugins=Plugins()
Plugins.Execute_a_Plugin()
and here is an example of the code of Plugin_A.py
def External_Function(self):
Analysis.Analyze_Stuff()
print Analysis.Amplitudes_1
why do I get :
Traceback (most recent call last):
File "C:\Users\Antoine.Valera.NEUROSECRETION\Desktop\Model\My_Apps.py", line 46, in <module>
Plugins.Execute_a_Plugin()
File "C:\Users\Antoine.Valera.NEUROSECRETION\Desktop\Model\My_Apps.py", line 37, in Execute_a_Plugin
Plugins.Plugin_A.External_Function(self)
File "C:\Users\Antoine.Valera.NEUROSECRETION\Desktop\Model\Plugin_A.py", line 8, in External_Function
Analysis.Analyze_Stuff()
NameError: global name 'Analysis' is not defined
but if I add the 2 lines following lines instead of Plugins.Execute_a_Plugin()
Analysis.Analyze_Stuff()
print Analysis.Amplitudes_1
then, it works.
How could I indicate to every dynamically loaded plugins that he has to use the variables/objects already existing in Analysis? Why can't I print Analysis.Amplitudes_1 from within the plugin?
Thank you!!
The error message seems perfectly clear: the name "Analysis" doesn't exist in the namespace of the Plugin_A module you imported, and so External_Function cannot access it.
When you import the Plugin_A module, it doesn't get access to the names in the namespace of the importing module, My_apps. So it cannot "see" the instance of the Analysis class that you created there.
A simple solution to this is to change the signature of External_Function (and other related functions), so that it can take an instance of the Analysis class:
Plugin_A.py:
def External_Function(self, analysis):
analysis.Analyze_Stuff()
print analysis.Amplitudes_1
My_apps.py:
...
def Execute_a_Plugin(self):
plugins.Plugin_A.External_Function(self, analysis)
if __name__ == "__main__":
analysis = Analysis()
plugins = Plugins()
plugins.Execute_a_Plugin()
Note that I have altered the naming so that the instance names don't shadow the class names.
You have to import your module. Add the following on top of Plugin_A.py
from My_apps import Analysis
A = Analysis()
A.Analyze_Stuff()
print A.Amplitudes_1
Related
I'm trying to understand how the importlib.reload method actually behaves.
I'll give a boiled down example
import importlib
import sys
from pathlib import Path
import gc
def write_dummy_class(return_value):
target = Path(__file__).parent / 'test_reload_import.py'
target.write_text(
"class Dummy:\n"
f" var = {return_value}\n"
" def run(self):\n"
" print(f'Dummy.run(self) >> self.var = {id(self.var):x}')\n"
f" return self.var\n"
)
write_dummy_class(1)
from test_reload_import import Dummy
print(f'id Dummy: {id(Dummy):x}')
print(Dummy.run)
assert Dummy().run() == 1, "Initial one failed??"
write_dummy_class(2)
old_module = sys.modules["test_reload_import"]
old_dummy = old_module.Dummy # Keep a reference alive
print(f'Reloading, old module: {id(old_module):x}')
new_module = importlib.reload(old_module)
print(f'Reloaded, new module: {id(new_module):x}')
print(f'id new Dummy: {id(new_module.Dummy):x}')
print(f'id old Dummy: {id(old_dummy):x}')
print(f'id Dummy: {id(new_module.Dummy):x}')
print(new_module.Dummy.run)
new_run = new_module.Dummy().run()
assert new_run == 2, f'Dummy.run() returned {new_run} instead of 2.'
This is the output:
id Dummy: 1dd320c0fa0
<function Dummy.run at 0x000001DD325CC700>
Dummy.run(self) >> self.var = 1dd31d06930
Reloading, old module: 1dd325c7950
Reloaded, new module: 1dd325c7950
id new Dummy: 1dd320c30d0
id old Dummy: 1dd320c0fa0
<function Dummy.run at 0x000001DD325CC790>
Dummy.run(self) >> self.var = 1dd31d06930
Traceback (most recent call last):
File "test_reload.py", line 240, in <module>
assert new_run == 2, f'Dummy.run() returned {new_run} instead of 2.'
AssertionError: Dummy.run() returned 1 instead of 2.
Observations:
reloading a module returns the same memory address as the previous one for the module.
objects do get reloaded inside the module (Dummy class has another id).
But what is baffling to me is that the memory address of the class variable 'Dummy.var' still points to the OLD one.
Could someone explains this last bit to me? How is it that the class is re-loaded, but the class variables are not? Isn't the code re-interpreted? And as such, the var should be re-interpreted as well, no? So basically getting another memory address?
Which leads me to my next question: what is also not reloaded?
BTW, I know that small integers are mapped to the same memory addresses in Python. That is not what is at play here. As I'm changing a class variable from a 1 to a 2, it should be another memory address. Or if it's would be the same address, it should have a different value.
But after reloading the class, the memory address of the class variable isn't updated somehow, which baffles me. And which leads me to wonder what other objects are exhibiting the same behavior.
(Python version: 3.9.9)
Oh, and one very strange thing is that this script works perfectly fine when running under "Debug" in PyCharm. But when "Run"... It breaks at the 2nd assert.
Thanks a lot!
This is an import system bug, Python issue 31772. If a source file is updated quickly without changing the file length, the import system won't realize that it's changed.
Your importlib.reload call is reexecuting stale bytecode instead of rereading the file. That's why var isn't updated to the new value - the import system is using the old bytecode for the var = 1 version of the file.
I have a text file I am using as a calibration file for a rudder indicator I am making. (It stores the port and stbd limits and the center position)
I would like to call this file when the program is booted so it had the same calibration settings from previously.
I can store the 3 numbers as a str in a .txt file and know how to recall them as a list.
My thought is to run a function when the app starts defining each part of the list as a variable eg.
calibrationfile1 = open('calfile.txt','r')
lines = calibrationfile1.readlines()
calvalue1 = lines[0].replace(",","").replace("[","").replace("]","")
calvalue = calvalue1.split()
rudderlimits = calvalue
port_rudder_limit = rudderlimits[0]
stbd_rudder_limit = rudderlimits[1]
center_position = rudderlimits[2]
how do I do call this in a function at startup and make the variables available in another function I dont want to use 'global'?
I have already made a funciton that is a calibration that creates this calfile.txt and it works.
thanks for your help :)
you can load the file on the app on_start method
class YourApp(App):
def on_start(self):
self.calibration_data = your_file_loading_function() # returns a dict?
... # other places in your code
class Popcorn(Widget):
def on_callback(self):
port_rudder_limit = App.get_running_app().calibration_data['port_rudder_limit']
... # do something....
or from kv file
<MyWidget>:
port_rudder_limit: app.calibration_data['port_rudder_limit']
How do I access execution context in my program to capture screenshot ?
The following program will fail since contain text does not exist.
from ExtendedSelenium2Library import ExtendedSelenium2Library
import logging
class firsttest():
def googleit(self):
self.use_url = 'https://google.ca'
self.use_browser = 'chrome'
s2l = ExtendedSelenium2Library()
s2l.open_browser(self.use_url, self.use_browser)
s2l.maximize_browser_window()
try:
# Should fail
s2l.page_should_contain('this text does not exist on page')
except:
logger.debug('failed')
runit = firsttest()
runit.googleit()
When I run this program get warning
WARNING - Keyword 'Capture Page Screenshot' could not be run on failure: Cannot access execution context
You have to use robot to execute the test, you can't just instantiate classes and expect them to work. They are designed to work only when run by robot.
If you need to write tests in python, there's no need to use ExtendedSeleniumLilbrary, you can just call the selenium API directly from python.
The problem likely stems from the fact that you did not write your python Library in the correct format for Robot Framework.
Here is the correct format for writing Python code in Robot Framework:
from robot.libraries.BuiltIn import BuiltIn
class ClickAnElement(object):
def __init__(self):
self.selenium_lib = BuiltIn().get_library_instance('ExtendedSelenium2Library')
def click_an_element(self, locator):
BuiltIn().click_element(locator)
How this works (I believe) is in Robot Framework you call this library in your *** Settings *** section with Library ClickAnElement.py. That activates the __init__ function. Then you can call the keywords like you would a keyword from Selenium2Library. So, if I were to re-write your posted code in the correct format, it would look as follows:
from robot.libraries.BuiltIn import BuiltIn
import logging
class FirstTest():
def __init__(self):
self.selenium_lib = BuiltIn().get_library_instance('ExtendedSelenium2Library')
def google_it(self):
self.use_url = 'https://google.ca'
self.use_browser = 'chrome'
s2l = ExtendedSelenium2Library()
s2l.open_browser(self.use_url, self.use_browser)
s2l.maximize_browser_window()
try:
# Should fail
s2l.page_should_contain('this text does not exist on page')
except:
logger.debug('failed')
Then, my .robot file would look as such:
*** Settings ***
Library FirstTest
*** Test Cases ***
Test Google It
Google It
You were writing a Python file to work outside of Robot Framework. If you want it to work inside of Robot Framework, you need to use the correct Library format.
Mind you, I'm only formatting your code, not testing it. I can't, since I don't have your application to test it on.
Can anyone provide a minimal working example using the Yapsy plugin framework?
Here's a very simple example. It has three files:
plugins\plugin1.py - the plugin. This has to contain a class inherited from IPlugin.
plugins\plugin1.yapsy-plugin - information about the plugin.
yapsy-example.py - the main script. This just loads all the plugins it can find in the "plugins" directory, and calls a method on them to prove that they work.
You could add more plugins to the plugins directory, and this script would loop around them all.
There's another more complicated example at http://lateral.netmanagers.com.ar/weblog/posts/BB923.html (archived).
yapsy-example.py
from yapsy.PluginManager import PluginManager
def main():
# Load the plugins from the plugin directory.
manager = PluginManager()
manager.setPluginPlaces(["plugins"])
manager.collectPlugins()
# Loop round the plugins and print their names.
for plugin in manager.getAllPlugins():
plugin.plugin_object.print_name()
if __name__ == "__main__":
main()
plugins\plugin1.py
from yapsy.IPlugin import IPlugin
class PluginOne(IPlugin):
def print_name(self):
print "This is plugin 1"
plugins\plugin1.yapsy-plugin
[Core]
Name = Plugin 1
Module = plugin1
[Documentation]
Author = John Smith
Version = 0.1
Website = http://lotsofplugins.com
Description = My first plugin
Im playing around with web.py as a lightweight web framework. Im having problems when i attempt to move the actual implementation of my page into a separate file instead of the root file. As a demonstration, My core.py file looks like this:
import web, sys, os
sys.path.append(os.path.abspath(os.path.dirname(__file__)))
urls = (
'/', 'index'
)
app = web.application(urls, globals())
render = web.template.render('templates/')
if __name__ == "__main__":
app.run()
ive moved my implementation into a file called index.py at the same level as core.py. My implementation looks like this:
class index:
def GET(self):
return "Hello world"
however, whenever i run my application, i get an error:
<type 'exceptions.KeyError'> at /
can anybody tell me what is going on?
According to http://webpy.org/tutorial3.en#urlhandling, web.py does a lookup for the classes you specified in your urls in the global namespace.
In your core.py there is no class named index (after you moved it), that's what causes this keyerror. In my test I could fix that by importing the index class in core.py.
from index import index
(I haven't used web.py before, so please correct me if I'm wrong)
You can add dots to crawl into modules. So say you have a folder controllers with a file named file.py and you wanted to access the controller named index:
from controllers import *
urls = (
'/', 'controllers.file.index'
)
I'm guessing the bug is in your template. I hit this error when if forgot a ':' on an if statement in my template.