Distractor during the experiment

I used the following code component to create a distractor every five trials in a perception experiment.

Begin experiment

import random
basestring = str
list1 = ["↑","↓"]

Begin routine

if (trial.thisN+1) % 5 != 0:
    continueRoutine = False
else:
    key = random.choice(list1)
    if key == "↑":
        press = "up"
    if key == "↓":
        press = "down"
    feedback.setText("Please press " + key + " to continue——")

And I set the allowed keys in the keyboard component as $press.
This worked perfectly in a previous experiment in 2018 (with the same PsychoPy version), but it doesn’t work in my current experiment.
The errors I got are the following, without showing the feedback text:

if not type(press) in [list, tuple, np.ndarray]:
TypeError: ‘str’ object is not callable

Even though I make isinstance(press,basestring) = True

I also try setting the allowed keys in the keyboard component as $‘press’, the feedback text will show, but any keypress will not continue the experiment. I also think this isn’t right, because press already includes the “”.

Thanks!
Nine

The line of code generating the error is not actually here. Can you post the full error message or the part of the code that has the conditional statement it’s tripping on?

Hi Jonathan,

Thanks for your reply.
This is the entire code trunk that has the conditional statement for the distractor, and that’s the full error message.
The allowed keys will call $press and throws the error.
Or maybe you are after something else that I didn’t know?

Thanks again.

The error, on closer inspection, is not quite what I initially thought. The error is saying that something is being called like a method when it’s actually a string.

Looking at the line in question, the problem is actually the word ‘type’. Rather than ‘type’ being a method (which it should be), there is a string somewhere in your experiment named ‘type’, and I think that is what’s causing the problem.

Did you name a variable in your experiment ‘type’, and if so can you name it something else?

There’s no variable named ‘type’.
The only place where type occurs is the trunk of code here:

# AllowedKeys looks like a variable named `press`
            if not type(press) in [list, tuple, np.ndarray]:
                if not isinstance(press, basestring):
                    logging.error('AllowedKeys variable `press` is not string- or list-like.')
                    core.quit()
                elif not ',' in press: press = (press,)
                else:  press = eval(press)
            # keyboard checking is just starting
            event.clearEvents(eventType='keyboard')
        if feedback2_2.status == STARTED:
            theseKeys = event.getKeys(keyList=list(press))
            

Is it possible that the problem is with ‘press’? Because when I put $‘press’ in the keyboard component, it can show the feedback dialog, though any keypress won’t make it continue. But when I put $press, it will quit and show the error msg after the 5th trial. I tried specifying press as a string or a list of string, but it wouldn’t take it.

That’s odd. The error message is that a string is being treated like a function. It’s possible ‘press’ is getting replaced by something that reads like a function (see python - How do I fix this "TypeError: 'str' object is not callable" error? - Stack Overflow), but I can’t really tell where that would spring up in your code.

Can you share your whole experiment on Pavlovia or somewhere?

Hmm, it’s odd indeed. Because I have exactly the same code in a previous experiment 3 years ago and it worked fine. I just copied and adapted the code in the current experiment, and even tried to redo the whole experiment by copy&pasting components based on the previous experiment without changing that code component. But it just won’t work…
I attach my experiment here. Would that be okay?

Thanks so much for taking a look!
pd_perception_lastrun.py (165.1 KB)

I see. The error is not located in the experiment code itself, but rather from something the experiment code relies on in the PsychoPy library. I see you have tried renaming ‘press’, did that work?

For the error message, there must be more text telling you the line number and the file on which the error occurred, can you copy that over as well?

Thanks so much for looking it over.
Sigh, renaming ‘press’ to ‘allow’ didn’t work either.

Here’s the error msg:

pyo version 0.9.0 (uses single precision)
Traceback (most recent call last):
File “C:\Users\exp\pd_perception_lastrun.py”, line 3366, in
if not type(allow) in [list, tuple, np.ndarray]:
TypeError: ‘str’ object is not callable

Ah, it just dawned on me. It’s because ‘press’/‘allow’ needs to be a list, because the allowed keys expects a list even if there’s only one entry. So, just modify the code as follows:

else:
    key = random.choice(list1)
    if key == "↑":
        allow = ["up"]
    if key == "↓":
        allow = ["down"]
    feedback.setText("Please press " + key + " to continue——")

The type-check is looking for a list, tuple, or nparray, which a string is not.

I tried the exact same thing before, but it didn’t work either… with the same error msg
I also tried to define a list first with

up = ["up"]
# and then assign that to allow
allow = up

Still won’t work.

After all these attempts, I think there should be some bug in this version of PsychoPy, but after 3 years…?

I’m attaching the code again with what’s been modified.

pd_perception_lastrun.py (165.4 KB)

This is helpful. I can’t get it to run at all myself because it’s missing the conditions file, but I can at least see where the error is arising now.

First, worth noting that I get this message when I try to run the code at all:

Alert 4052:Experiment was built in a past version of PsychoPy (2020.2.4), saving it in this version may add parameters which cannot be parsed.

That might indicate that it is something that’s changed from previous versions about how something is being parsed. It might be worth re-creating the experiment entirely from scratch in the builder in the latest version rather than trying to copy it from an old version, if you have the time.

I also have some suggestions about how to debug this. Here’s some code copied from around line 3348 and modified where it says “NEW DEBUG CODE”

        # *feedback1* updates
        if t >= 0.0 and feedback1.status == NOT_STARTED:
            # keep track of start time/frame for later
            feedback1.tStart = t
            feedback1.frameNStart = frameN  # exact frame index
            feedback1.status = STARTED
            
            # NEW DEBUG CODE
            print(type(allow))
            
            # AllowedKeys looks like a variable named `allow`
            if not type(allow) in [list, tuple, np.ndarray]:
                if not isinstance(allow, basestring):
                    logging.error('AllowedKeys variable `allow` is not string- or list-like.')
                    core.quit()
                elif not ',' in allow: allow = (allow,)
                else:  allow = eval(allow)

This will do one of two things: 1) make the error appear when the print line is called, which tells us the problem is in type(allow), or 2) verify what type ‘allow’ is before the error occurs. That will help narrow down the causes and the potential solutions.

1 Like

Thanks, Jonathan.
I added the debug code, and it verifies the first scenario that the error comes from type(allow) or “allow”.

Traceback (most recent call last):
  File "C:\Users\exp\pd_perception_lastrun.py", line 3354, in <module>
    print(type(allow))
TypeError: 'str' object is not callable

I previously did download the latest version to run the file in an old version, and the debugging there is also a pain, so I went back with the old version. The oddest thing is that this trunk of code still works in an old experiment. I also attached the code from that experiment here. (The relevant code is around line 1443.) creaky_perception - key_font_630_lastrun.py (81.6 KB)
If you could help me take a look between the two experiments, maybe that would narrow down the causes?

In the meantime, I’ll try re-creating the entire experiment from scratch using the latest version to see if that works.

All the best.

The two experiments are nearly identical, in all the ways that should matter. There’s a small chance this has to do with the fact that newer versions of PsychoPy use Python 3.6, which did change how the type() function works, slightly. Just to clarify: Can you run the old version in the most recent versions of PsychoPy? Or did it only work in an older version of PsychoPy?

I have a potential solution but it involves changing code that is generated by the builder, so it will get overwritten if you ever change the experiment from the builder. So this would mean that you would either have to make this change every time you updated the experiment in the builder, or you will only be able to change the experiment in the coder directly.

The change would look like this:

# *feedback1* updates
        if t >= 0.0 and feedback1.status == NOT_STARTED:
            # keep track of start time/frame for later
            feedback1.tStart = t
            feedback1.frameNStart = frameN  # exact frame index
            feedback1.status = STARTED
            
            # NEW DEBUG CODE
            print(allow.__class__)
            
            # AllowedKeys looks like a variable named `allow`
            # CHANGE HERE TOO
            if not allow.__class__ in [list, tuple, np.ndarray]:
                if not isinstance(allow, basestring):
                    logging.error('AllowedKeys variable `allow` is not string- or list-like.')
                    core.quit()
                elif not ',' in allow: allow = (allow,)
                else:  allow = eval(allow)

In theory, allow.__class__ and type(allow) should do basically the same thing, but if the problem is with either ‘type’ being in conflict with something else called type or the change to what the type() function can do, then this will avoid the problem. So the fix is to replace every instance of type(allow) in your code with allow.__class__

Again, this is changing the code that is compiled by the builder, so if you ever re-compile from the builder these changes will be overwritten.

Sorry I can’t come up with a more elegant solution!

1 Like

Thanks, Jonathan!! I think it worked fine now!
Oh, and that debug code prints out <class ‘str’>

I can run the old version using the old version of PsychoPy, I didn’t try it on the most recent versions.

Now I’ll make sure to make the coder changes last.

Thanks again and have a wonderful day!
Nine

1 Like