How do I get a list of members in a Python module, or how should I create plugins? - plugins

I'm writing a sort of dashboard-esque program in Python to make tasks that I do frequently here at work a little better/easier.
What I currently have set up is an actions module. actions contains an actionlist that is a list of classes. Each of my classes has a .title, .icon (base-64 encoded string for use with tkinter.PhotoImage), and a .action function.
Using tkinter I create a new button for each of these actions, with the appropriate icon, text, and the command of the button set to the .action. So far it works quite nicely, but if I want to create a new action, I have to go into my actions module, create a new class, and then put that class in my actionlist.
What I would like to do is be able to dynamically generate this actionlist based on what files are present in my module. So I would like to have something like this:
actions/
__init__.py
someaction.py
do_another_thing.py
do_all_the_things.py
chop_down_the_tree_with_a_herring.py
And whenever I would like to create a new action, I just put a .py script in my actions folder and then it would automagically generate the list of actions for me, however I can't seem to find any good way of creating this list. I've thought of using something like os.listdir to get the list of files, but neither that or the other methods of introspection (dir, and inspect.getmembers) seemed to have quite what I wanted. Of course, it's possible that I just missed the features that I need.
In any case, pointers in the right direction would be great. Thanks!
SOLUTION:
Lennart's solution, with some slight adaptations worked for me.
Inside my __init__.py I have the following code:
plugins = []
__all__ = [
'do_all_the_things',
'do_another_thing',
'someaction',
'chop_down_the_tree_with_a_herring',
'seek_the_grail',
]
Then inside each of my plugins I have something like this:
from actions import plugins
class SeekTheGrail:
def __init__(self):
self.text = 'We Seek The Grail'
self.icon = b'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'
def action(self):
print('I am Arthur, King of the Britons!')
plugins.append(SeekTheGrail())
and now I just have to modify the __all__ collection when I want to add or remove the actions from loading.

The way to create plugins is to have a plugin registry, and register plugins there. This can be done from the amazingly simple, like just defining a global list in a module:
plugins = []
And then registering the plugins with:
class APlugin(object):
blahblah
from themodule import plugins
plugin.add(APlugin())
This can be made more flexible by having types of plugins and registering them with a name etc. But then you are on the path to complexity, and then you might want to take a look at the Zope Component Architecture, which simply speaking is a very powerful system for making your application pluggable.
It should be noted that in any case it's best to have a list of what plugins should be activated instead of auto-discovering them. This can be as simple as a list in a text file or a setting in a config file. This enables you to easily activate and disactivate the plugins.

You can use something like this:
actions = {}
for filename in os.listdir('somepath/actions'):
actions[filename] = __import__('actions.'+filename)
for name,class in actions:
createButton(name,class)
Where in the createButton you create your tk_inter button and assign it the functions from the class. The important bit i suppose is that you can use __import__ to import a class using its string name and save it as a variable.

Related

Specifying window (global) variable type hinting in VSCode from external JS file without typescript

This may be a silly question but I really don't know where to look.
I'm creating a browser testing environment for a pretty large-scale API written in typescript. This API uses esbuild to build the typescript files into a /dist/ folder with a single index.js entry-point and its appropriate d.ts file.
I've created a /tests/ folder to hold some browser files that includes an index.html file with Mocha and Chai imported. It also imports /dist/index.js which is set globally to a window.myAPI variable.
In /tests/index.html:
import * as myAPI from "./dist/index.js"
Alongside index.html in the tests folder, there are separate JS files included for different tests that run things on window.myAPI... to do assertion tests.
search.test.js
book.test.js
navigate.test.js
I then run a server to host at the root. These separate tests are then imported from /tests/index.html. The separate tests look like this inside:
const { chai, mocha } = window;
const { assert } = chai;
describe("Search", function() {
describe("Setup", function() {
it("Setting URL should work", function() {
const call = myAPI.someCall()
assert.ok(call);
});
});
});
mocha.run();
Everything works, but I have no code hinting for myAPI. I'd like to be able to see what functions are available when I type myAPI, and what parameters they take, and what they should return - along with all my comments on each function.
In typescript you can do things like ambient declarations, but I don't want to make my tests typescript because then I add an unnecessary build step to the tests. But it would be as easy as:
/// <reference path = "/dist/index.d.ts" />
How can I tell VSCode that window.myAPI is an import of /dist/index.js and should import the types as well so I can see them ?
I'm open to different solutions to this, but I feel like this should be pretty simple. I don't know if ESLint is capable of doing something like this, but I tagged it because I feel it's relevant.
Thanks!

How to exclude UnityEditor reference from asmdef?

How to exclude UnityEditor reference from asmdef?
Why I need it:
I have an asmdef file. For example, it is MyAssembly/MyAssembly.asmdef. The MyAssembly contains a lot of features and each feature staff is placed in its own folder. And some of these features has a code that is needed only in editor, and it refers to UnityEditor namespace. Such editor code is placed into an Editor folder.
But as you know, Editor folder name means nothing in terms of asmdef usage. So I add AssemblyDefenitionReference in each folder and refer it to the MyAssemblyEditor.asmdef assembly definition. So the paths looks like this:
MyAssembly/MyAssembly.asmdef
MyAssembly/Editor/MyAssemblyEditor.asmdef - this folder contains no code. It's needed just to place asmdef, because it's not allowed to place two asmdefs in a single folder.
MyAssembly/SomeFeature/Editor/*feature editor staff*
MyAssembly/SomeFeature/Editor/Editor.asmref - refers to MyAssemblyEditor.asmdef
MyAssembly/SomeFeature/*feature staff*
All this works good. But the problem is that, when some developer adds a new feature, he can forget to add a reference to the MyAssemblyEditor.asmdef in the editor folder. And there are no any errors will be shown in this case. This mistake will be revealed only when the build will be cooked. But I'd like that using of UnityEditor in MyAssembly will be instantly marked as an error.
Feel free to suggest other solution for this problem.
This thread got me thinking I can use CsprojPostprocessor to remove all references to UnityEditor from my csproj file. I wrote such class:
using System.Text.RegularExpressions;
using UnityEditor;
// ReSharper disable once CheckNamespace
public class CsprojPostprocessor : AssetPostprocessor
{
public static string OnGeneratedCSProject(string path, string content)
{
if (!path.EndsWith("Editor.csproj") && !path.EndsWith("Tests.csproj"))
{
var newContent =
Regex.Replace(content, "<Reference Include=\"UnityEditor(.|\n)*?</Reference>", "");
return newContent;
}
return content;
}
}
It also can be done with an xml parser or something.
The only thing, that confuse me is that this mechanism is badly documented and doesn't look like something simple users should use. So I use it at my own risk, but looks like there is no guarantee it will be strongly supported in future.

How to use Strings to access class members in Flutter

I want to set use Strings from an API as Fontawesome icons. I have added the font_awesome_flutter plugin.
I need to store the icon name in a variable and then create an object from it. I would imagine it to like this:
String iconfromApi = 'suitcase';
Icon(FontAwesomeIcons.iconfromApi);
As described here, you would need have access to the dart:mirrors pacakage, which is not avaible in Flutter.
A solution that would work in Flutter is creating a helper method. This means that you will have to code in cases for all icon names you want to use. If you do not want to write all of that by hand, you can take a look at a package like reflectable as mentioned in the GitHub comment or potentially source_gen or build_runner, however, I am not sure if the latter two are well suited.
Anyways, what you could also write by hand is a helper function like this:
IconData fontAwesomeIconFromString(String name) {
switch (name) {
case 'suitecase':
return FontAwesomeIcons.suitecase;
case 'gamepad':
return FontAwesomeIcons.gamepad;
// ...
}
}
In your code, you can now use it like this:
String iconfromApi = 'suitcase';
Icon(fontAwesomeIconFromString(iconFromApi));

How we can make cypress scripts easily maintainable like POM in other tools like selenium

This is just a general clarification about building framework using cypress.io.
In cypress can we write a test framework like page object model in selenium?
These model make our life easy to maintain tests.
For eg if ID or class of a particular element which is used across multiple tests /files has changed with a new version of Application-In cypress it is hard to go to multiple test files/tests and change the ID right?
Can we follow the same page object model concept like declaring all elements as variables in each page and use the variable names in tests/functions?
Also can we reuse these variables across different test .js files ?
If yes - can you please give a sample
Thanks
I have seen only a few people using POM concept while creating an automation framework using Cypress. Is that advisable to follow POM model, it depends on reading the following link from team. I would say this may depend upon automation tools/ architecture. According to Cypress team this is not recommendable, may be a debatable topic, read this: https://www.cypress.io/blog/2019/01/03/stop-using-page-objects-and-start-using-app-actions/#
We can declare the variable names in Cypress.env.json file or cypress.json file like below:
{
"weight": "85",
"height": "180",
"age": "35"
}
Then if you want to use them in a test-spec, create a new variable and receive it like below in test-spec.
const t_weight = Cypress.env('weight');
const t_height = Cypress.env('height');
Now you can use the variable in respective textbox input of pages as below:
cy.get('#someheighttextfieldID').type(t_weight);
cy.get('#someweighttextfieldID').type(t_height);
or receive it directly;
cy.get('#someweighttextfieldID').type(Cypress.env('weight'));
example:
/* declare varaibles in 'test-spec.js' file*/
const t_weight = Cypress.env('weight');
const t_height = Cypress.env('height');
//Cypress test - assume below test to test some action and receive the variable to text box
describe('Cypress test to receive variable', function(){
it('Cypress test to receive variable', function(){
cy.visit('/')
cy.get('#someweighttextfieldID').type(t_weight);
cy.get('#someheighttextfieldID').type(t_height);
//even receive the variable straight away
cy.get('#someweighttextfieldID').type(Cypress.env('weight'));
})
});

Python w/QT Creator form - Possible to grab multiple values?

I'm surprised to not find a previous question about this, but I did give an honest try before posting.
I've created a ui with Qt Creator which contains quite a few QtWidgets of type QLineEdit, QTextEdit, and QCheckbox. I've used pyuic5 to convert to a .py file for use in a small python app. I've successfully got the form connected and working, but this is my first time using python with forms.
I'm searching to see if there is a built-in function or object that would allow me to pull the ObjectNames and Values of all widgets contained within the GUI form and store them in a dictionary with associated keys:values, because I need to send off the information for post-processing.
I guess something like this would work manually:
...
dict = []
dict['checkboxName1'] = self.checkboxName1.isChecked()
dict['checkboxName2'] = self.checkboxName2.isChecked()
dict['checkboxName3'] = self.checkboxName3.isChecked()
dict['checkboxName4'] = self.checkboxName4.isChecked()
dict['lineEditName1'] = self.lineEditName1.text()
... and on and on
But is there a way to grab all the objects and loop through them, even if each different type (i.e. checkboxes, lineedits, etc) needs to be done separately?
I hope I've explained that clearly.
Thank you.
Finally got it working. Couldn't find a python specific example anywhere, so through trial and error this worked perfectly. I'm including the entire working code of a .py file that can generate a list of all QCheckBox objectNames on a properly referenced form.
I named my form main_form.ui from within Qt Creator. I then converted it into a .py file with pyuic5
pyuic5 main_form.ui -o main_form.py
This is the contents of a sandbox.py file:
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
import main_form
# the name of my Qt Creator .ui form converted to main_form.py with pyuic5
# pyuic5 original_form_name_in_creator.ui -o main_form.py
class MainApp(QtWidgets.QMainWindow, main_form.Ui_MainWindow):
def __init__(self):
super(self.__class__, self).__init__()
self.setupUi(self)
# Push button object on main_form named btn_test
self.btn_test.clicked.connect(self.runTest)
def runTest(self):
# I believe this creates a List of all QCheckBox objects on entire UI page
c = self.findChildren(QtWidgets.QCheckBox)
# This is just to show how to access objectName property as an example
for box in c:
print(box.objectName())
def main():
app = QtWidgets.QApplication(sys.argv) # A new instance of QApplication
form = MainApp() # We set the form to be our ExampleApp (design)
form.show() # Show the form
app.exec_() # and execute the app
if __name__ == '__main__': # if we're running file directly and not importing it
main() # run the main function
See QObject::findChildren()
In C++ the template argument would allow one to specify which type of widget to retrieve, e.g. to just retrieve the QLineEdit objects, but I don't know if or how that is mapped into Python.
Might need to retrieve all types and then switch handling while iterating over the resulting list.