0

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");
Codejoy
  • 3,722
  • 13
  • 59
  • 99
  • 1
    `self.runFunc(self)` looks wrong in general since `self` is passed twice. How is `_runConfig` defined? – 0x5453 Sep 01 '22 at 18:22
  • Based on the output, I think this is the issue: `if not hasattr(res, "next"):`. Python3 generators do not have a `next` attribute. Maybe use [`inspect.isgenerator`](https://docs.python.org/3/library/inspect.html#inspect.isgenerator) to check if the object is a generator. – Dunes Sep 01 '22 at 18:24
  • Python 3 uses `__next__`, but having type `generator` is strictly more specific than having a `__next__` attribute; other types implement the iterator protocol by defining `__next__` as well. – chepner Sep 01 '22 at 18:48
  • If I remove the (self) from runFunc then I see the error: Traceback (most recent call last): File "/Users/dev/TUI3/RO/ScriptRunner.py", line 793, in _continue res = self.runFunc() TypeError: _runConfig() missing 1 required positional argument: 'sr' – Codejoy Sep 01 '22 at 19:18
  • also added the runConfig definition that instruments use that doesn't execute. The other runConfigs are a bit different and defined for each instrument that works under their own overriding of the StatusConfigWdg class – Codejoy Sep 01 '22 at 19:20
  • @Dunes no, it should be `if not hasattr(res, "__next__")` instead if you want a direct conversion. – juanpa.arrivillaga Sep 01 '22 at 20:12
  • You guys are gods! it was indeed the __next__ issue. I need to test more but so far it looks like that was it. – Codejoy Sep 01 '22 at 20:23

0 Answers0