Since Python 3.4 there's a new standard library which makes dealing with paths a lot easier and more platform agnostic.
A little introduction is here, docs are here
For example it would be neat to create an ImageStim this way:
instruction = ImageStim(win=window, image=Path(INSTR_PATH) / some / subdirectory / 'instruction.png')
I do realize this might remove feature parity between the Python 2 and 3 versions and is by far not a pressing issue.
For now we resort to wrapping it into a str() which is fine but somehow pointless.
What do you think?
At first glance I'm not personally a fan. I usually want this:
instruction = ImageStim(win=window, image='images/instructions.png')
and it does work on all platforms
Under the new scheme I would need
from pathlib import Path
imagesPath = Path('images')
instruction = ImageStim(win=window, image= imagesPath/'instructions.png')
or
from pathlib import Path
instruction = ImageStim(win=window, image= Path('.')/'images'/'instructions.png')
I'm imagining leading a workshop and trying to explain to attendees:
/ not as a stringI guess I don't care that the feature is there for people that like it, but I wouldn't want it to be prominent any tutorial materials etc, and I definitely wouldn't personally stop using 'images/image.png'
@peircej I am also not a big fan of pathlib, but I don't think @EruIluvatar asks to _replace_ the current functionality of PsychoPy, but rather would like to be able to simply pass a Path object and see it handled correctly, while retaining current support for path strings.
So I believe the only change that would be required would be to check whether the passed filename is an instance of os.PathLike, and if it is, convert it to a string and proceed as usual. Which should be straightforward to implement?
Yes, hence the final comment, "I guess I don't care that the feature is there for people that like it, but I wouldn't want it to be prominent any tutorial materials etc"
Happy to take a PR that does what you say
Thanks for your input.
Sounds reasonable, this might be a chance for me to submit a PR at some point
Edit:
I will not be able to contribute due to changing job situation.
I think the way to go about this is to implement a small helper function in psychopy.tools.filetools that sanitizes all path names. This not only applies to "input" files (as for use with (Simple)ImageStim), but should also work with the file output methods in data.py.
The issue with Pathlib has been rapidly spreading as more users try to adopt it without a number of projects that yet support it. Looking towards the future, I'd say adopting this is a must. Unfortunately its not a straightforward issue to fix because pathlib exists in python 3.2 and above, but standard library approaches to being able to properly access their underlying path in a pathlib object doesn't appear until 3.6 (e.g. the __fspath__ under and the os library support of os.fspath() I've actually had a brief discussion of how to implement this in another project and we settled with pandas having the "best" approach to this to resolving pathlib objects to their underlying representation that we are planning on adding. pandas. _stringify_path
Essentially, pandas._stringify_path checks to see if the object has the __fspath__ dunder, and calls that if it does (see pep 519 __fspath__). This is the 3.6+ fix because pathlib path objects will have __fspath__. Then for the gap of 3.2-3.5x where pathlib is common but doesn't have __fspath__, they resort to calling text_type(pathlib_path) where text_type is str for python3 and unicode for python2.
I like the pandas approach, think we could borrow it (with an appropriate copyright message -- should work since their license is more permissive than PsychoPy's)?
This is the 3.6+ fix because pathlib path objects will have __fspath__. Then for the gap of 3.2-3.5x where pathlib is common but doesn't have __fspath__, they resort to calling text_type(pathlib_path) where text_type is str for python3 and unicode for python2.
I don't think we'll want to support Python 3 < 3.6 anyway
(and I' actually very much looking forward to finally dropping Python 2 support (maybe next year? :)) so we can start using type hints and keyword-only args etc.)
I'm more conservative about dropping support in a way that breaks existing code but, for new features, I'm happy to support only 3.6 where there's good reason. In this instance, the only people likely to be using pathlib are enthusiasts/purists and they will have installed 3.6 long ago!
I'm in agreement here, about python 3 < 3.6; especially if we can do what @hoechenberger wants and support python 3.6+ only since 3.6 added a large number of helpful features. Typehints, f-strings, and pathlib (yes, I'm one of those pathlib supporter people) have made me sad because I can't really share projects that use them because of the people stuck on python2.
Back on topic, if we only want to support python3.6, we can easily do this to fix the pathlib issue: (pseudocode)
from psychopy.constants import PY3
import os
def fix_path(filepath_or_buffer):
#only python3 has the os.fspath function, so have to do a version check before we call it
#could also use a try/except but I think this is more explicit
if PY3:
return os.fspath(filepath_or_buffer)
return filepath_or_buffer
@CRiddler fancy doing a PR on this one? :)
Of course! Reading through the previous comments, are we still in agreement that we will NOT be supporting 3 < 3.6?
Yes, no need to support 3.4, 3.5
We do support 2.7 but I'm fine with the change as long as it doesn't prevent 2.7 users (or any, for that matter) having standard string-based paths. :-)
I'm actually growing to like pathlib. Going to switch all new code I write to pathlib. It's much more readable than os.path:
current_dir = pathlib.Path(__file__).parent
base_dir = current_dir.parents[1] # ../../ --> base directory of project.
data_dir = base_dir / 'Data'
rawdata_dir = data_dir / 'Raw'
fig_dir = base_dir / 'Figures'
input_files = rawdata_dir.glob('*Hummel*.xlsx')
So, +1 on supporting this in PsychoPy as well :-)
Most helpful comment
I don't think we'll want to support Python 3 < 3.6 anyway
(and I' actually very much looking forward to finally dropping Python 2 support (maybe next year? :)) so we can start using type hints and keyword-only args etc.)