0

I am trying to find an efficient and secure way to call different functions based on the transaction name the user enters. There are a 100+ different transactions. A 100 "IF" would do the job, however, I want to find a more efficent way to call the transaction. The "eval" would do it, but I read that this should not be used, as the user can enter any transaction name.

from operator import methodcaller
import  sys
from    PyQt5.QtWidgets     import (QMainWindow,QToolBar,QLineEdit,
                                    QLabel, QApplication)
def one():
        print ("1")

def two():
        print ("2")

def three():
        print("3")

class main_menu(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        menuBar = self.menuBar()
        self.ToolBar = QToolBar()
        self.ToolBar.setMovable(False)
        self.addToolBar(self.ToolBar)
        self.tcode = QLineEdit(maxLength=5)
        self.tcode.returnPressed.connect(self.tcode_action) 
        self.ToolBar.addWidget(QLabel("  Transaction : "))
        self.ToolBar.addWidget(self.tcode)

    def tcode_action(self):
##        if self.tcode.text() == "one":
##                one()
##        if self.tcode.text() == "two":
##                two()
##        if self.tcode.text() == "three":
##                three()

##        eval(self.tcode.text()+"()")        

def main(args):
    app                 = QApplication(sys.argv)
    mm = main_menu()
    mm.show()
    sys.exit(app.exec_())
if __name__=="__main__":
    main(sys.argv)


Andy
  • 5
  • 1
  • side note, as the question has already been answered by Anurag Regmi: Always use `if --> elif --> else` with a big list of if's. If you don't it will execute the rest of the ifs even after an if has already been matched... – Edo Akse May 28 '21 at 10:13

3 Answers3

1

Global variables can be accessed via globals() in python. You can use:

def tcode_action(self):
    fn = globals().get(self.tcode.text())
    if fn:
        fn()
    else:
        print("invalid input")
Anurag Regmi
  • 629
  • 5
  • 12
  • 1
    note, there is some danger in using this, as it could alow arbitrary code execution (eg. user inputs `main`). Safer way would be to use a dict as shown in [this example](https://stackoverflow.com/a/9168387/9267296), but that coud be a pain in the behind if there's 100+ different functions that can be called... – Edo Akse May 28 '21 at 10:15
0

One option could be to use a QComboBox to restrict the function set. You can also use an Enum to enumerate valid functions.

from enum import Enum
from functools import partial


# function definitions
def fcn_1( x ):
  print( 'F1' )


def fcn_2( x ):
  print( 'F2' )


# valid functions Enum
# Must wrap functions in partial so they are not defined as methods.
# See below post for more details.
class ValidFunctions( Enum ):
  Echo = partial( fcn_1 )
  Increment = partial( fcn_2 )


# function selection ComboBox
cb = QComboBox()
cb.addItem( 'Echo' )
cb.AddItem( 'Increment' )


# connecting the signal
def call_function():
  fcn = ValidFunctions[ cb.currentText() ]
  fcn()


cb.currentIndexChanged.connect( call_function )

Note: I haven't debugged this code.

How to define enum values that are functions

bicarlsen
  • 1,241
  • 10
  • 27
0

I will do this with this code now:

def tcode_action(self):
    try:
            func = getattr(self,self.tcode.text())
            func()
    except:
            pass

Any comments to this?

Andy
  • 5
  • 1
  • In your original code the functions are not members of the class, so calling `getattr` will not work, and you should use @Anurag Regmi's code using globals, or move your functions to be members of your class. – bicarlsen May 29 '21 at 12:44
  • Right, I moved the functions to the class. – Andy May 29 '21 at 22:37