Data file delimiter set to semicolon, but automatic/crash-saved CSV uses commas (PsychoPy 2026.1.3)

Hi,

I think I have found a possible inconsistency in how the Builder Data file delimiter setting is handled during normal saving versus PsychoPy’s automatic/fallback saving.

Environment

  • PsychoPy Builder 2026.1.3

  • Windows 10

  • Data file delimiter in Experiment Settings explicitly set to semicolon

Problem

I noticed that some .csv files from my experiment were comma-delimited even though the experiment’s Data file delimiter was set to semicolon.

The normal Builder-generated save function correctly contains:

thisExp.saveAsWideText(filename + '.csv', delim='semicolon')
thisExp.saveAsPickle(filename)

So the Builder setting is being propagated correctly.

However, ExperimentHandler.save() appears to use:

self.saveAsWideText(self.dataFileName + '.csv')

without passing the experiment’s selected delimiter.

Since saveAsWideText() defaults to:

delim='auto'

and the filename ends in .csv, this results in comma-separated output.

ExperimentHandler.close() calls self.save(), and close() is registered with atexit, so this appears to affect the automatic/fallback save that PsychoPy uses when the experiment does not reach the normal Builder saveData() path.

As a temporary Builder workaround I am considering changing only the automatic delim='auto' behavior:

if not hasattr(data.ExperimentHandler, '_originalSaveAsWideText_semicolonFix'):
    data.ExperimentHandler._originalSaveAsWideText_semicolonFix = (
        data.ExperimentHandler.saveAsWideText
    )

def _saveAsWideText_semicolonFix(
    self, fileName, delim='auto', *args, **kwargs
):
    if delim == 'auto':
        delim = 'semicolon'

    return data.ExperimentHandler._originalSaveAsWideText_semicolonFix(
        self, fileName, delim, *args, **kwargs
    )

data.ExperimentHandler.saveAsWideText = _saveAsWideText_semicolonFix

Maybe this will be helpful to someone else who runs into the same issue. Cheers.

Yes, ExperimentHandler.save exists largely as a last resort to be used when the Experiment Handler is deleted if something like saveData from a Builder experiment can’t be called, but it does rely solely on the filename to infer delimiter.

I wonder whether we should just have delim be an attribute of the Experiment Handler, so that it can call save in its __del__ function with no arguments and still know what delimiter to use…