This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: add support for path-like objects in sys.path
Type: enhancement Stage: patch review
Components: Library (Lib) Versions: Python 3.11
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: brett.cannon, chris.jerdonek, eric.araujo, eric.snow, eryksun, jaraco, jayyin11043, lemburg, ncoghlan, ncohen, python-dev, serhiy.storchaka
Priority: normal Keywords: patch

Created on 2018-01-24 05:27 by chris.jerdonek, last changed 2022-04-11 14:58 by admin.

Pull Requests
URL Status Linked Edit
PR 5691 closed jayyin11043, 2018-02-15 05:42
PR 22000 open python-dev, 2020-08-29 15:36
PR 32118 open ncohen, 2022-03-25 19:51
Messages (28)
msg310560 - (view) Author: Chris Jerdonek (chris.jerdonek) * (Python committer) Date: 2018-01-24 05:27
This issue is to suggest enhancing sys.path to recognize path-like objects, per PEP 519.

I recently ran into an issue where a path was getting added to sys.path, but the corresponding imports weren't working as they should, even though sys.path showed the path as being present. Eventually I tracked this down to the path being a pathlib.Path object rather than a string. This wasn't obvious because Path objects appear as strings in normal debug output, etc.

The sys.path docs currently say this:

> Only strings and bytes should be added to sys.path; all other data types are ignored during import.
msg311693 - (view) Author: Jay Yin (jayyin11043) * Date: 2018-02-05 21:23
This looks a lot like https://bugs.python.org/issue32446, I'd like to tackle this, if we are going through with it.
msg311864 - (view) Author: Jay Yin (jayyin11043) * Date: 2018-02-09 03:49
what file(s) is/are the sys.path code located in?
msg311895 - (view) Author: (yan12125) * Date: 2018-02-09 16:11
> what file(s) is/are the sys.path code located in?

If I understand it correctly, sys.path is handled in importlib._bootstrap_external.PathFinder.find_spec(). I can patch PathFinder for handling path-like objects: https://github.com/yan12125/cpython/commit/e3fd473b54cbb368533e651fd896bbc813a43924

Here's an example usage:

# t.py
import pathlib
import sys

sys.path.append(pathlib.Path('foo'))

import s

# foo/s.py
print(123)
msg311986 - (view) Author: Jay Yin (jayyin11043) * Date: 2018-02-11 08:49
Thanks for the reply
msg312070 - (view) Author: Jay Yin (jayyin11043) * Date: 2018-02-12 19:44
ok, so I found the PathFinder class you referenced, just making sure, this issue pertains to changing "self.path"'s usage and declaration to be a path-like object instead of the hard coded 'sys', 'path' returns? or is that part of it already?, also since this uses a tuple to keep track of the parent path, is there functionality in the path-like objects to find parent paths?
msg312072 - (view) Author: Jay Yin (jayyin11043) * Date: 2018-02-12 19:51
https://github.com/python/cpython/blob/3c34aad4e7a95913ec7db8e5e948a8fc69047bf7/Lib/importlib/_bootstrap_external.py#L1069-L1090

those are the particular class and lines I'm referring to
msg313283 - (view) Author: Jay Yin (jayyin11043) * Date: 2018-03-05 20:43
I'm unsure how to regenerate the files that interact with the code for sys.path as travisCI states
"
Generated files not up to date
 M Python/importlib_external.h
"
since my code changes some of how the importing is handled
msg313337 - (view) Author: Brett Cannon (brett.cannon) * (Python committer) Date: 2018-03-06 15:53
'make regen-all' should do it (but so will just running 'make -s -j' like
any old build of CPython).

On Mon, Mar 5, 2018, 12:43 Jay Yin, <report@bugs.python.org> wrote:

>
> Jay Yin <jayyin11043@gmail.com> added the comment:
>
> I'm unsure how to regenerate the files that interact with the code for
> sys.path as travisCI states
> "
> Generated files not up to date
>  M Python/importlib_external.h
> "
> since my code changes some of how the importing is handled
>
> ----------
>
> _______________________________________
> Python tracker <report@bugs.python.org>
> <https://bugs.python.org/issue32642>
> _______________________________________
>
msg313388 - (view) Author: Jay Yin (jayyin11043) * Date: 2018-03-07 15:17
I've been stuck on "test_poplib" for over 149661 sec now, that's about 41 hours... I don't think this is working correctly, although I'm unsure  what test_poplib is doing that has been affected by what I've changed here.
msg313460 - (view) Author: Jay Yin (jayyin11043) * Date: 2018-03-08 22:00
The issue was resolved by updating my version of the rest of the package apparently and remaking the whole thing, must have been some outdated stuff on my end causing the issue
msg313883 - (view) Author: Jay Yin (jayyin11043) * Date: 2018-03-15 14:13
I'm having issues with my local changes for my PR, I'm unsure why my local machine takes a seemingly infinite amount of time on test_poplib, so again I think something to do with my local environment is causing issues again, any help would be appreciated...
msg313885 - (view) Author: Jay Yin (jayyin11043) * Date: 2018-03-15 14:29
https://pastebin.com/q4FKnPZH

the trace for the test_poplib
msg313887 - (view) Author: Jay Yin (jayyin11043) * Date: 2018-03-15 15:32
it seems to me like the issue in my tests is that some SSL thing is failing?, anyone have any experience with this?
msg313923 - (view) Author: Brett Cannon (brett.cannon) * (Python committer) Date: 2018-03-15 23:51
Please open a separate issue for the test_poplib issue.
msg314065 - (view) Author: Jay Yin (jayyin11043) * Date: 2018-03-18 22:18
srry I opened another issue bpo-33099
msg340950 - (view) Author: Brett Cannon (brett.cannon) * (Python committer) Date: 2019-04-26 22:27
This seems fine to me and I can't think of any negatives. Can anyone think of why this might be a bad idea? (Messing with how sys.path is used is just so fundamental I'm being paranoid. :)
msg409732 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2022-01-05 03:06
I’m not an import expert but would have misgivings about having fancy types in sys.path too!  It seems so fundamental, and used from C and Python, with a simple interface of a direct list (plus importers and finders etc), that I would understand it pure strings were required and not Paths (and not all path-likes).
msg415012 - (view) Author: Jason R. Coombs (jaraco) * (Python committer) Date: 2022-03-12 20:54
I'm in support of adding Path support for sys.path, but I also agree with Eric, there are innumerable consumers of sys.path beyond importlib. and since pathlib.Path isn't a str, it would likely introduce incompatibility. On the other hand, users introducing Path objects to sys.path could be warned that although importlib supports Path objects, other consumers may not, and that support for it in importlib isn't endorsement of the use of those types and the consequences aren't necessarily supported.

As an aside, it's too bad a Path object couldn't have been a str subclass (as it is for [path](https://pypi.org/project/path), which would have made problems like this one much safer to solve.
msg416037 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2022-03-26 05:34
Are there any problems with converting a Path to string before adding it to sys.path? You do this one time, and any users of sys.path will not need to bother about conversion. It is better even for performance.

Otherwise we will need to revise and update every code that uses sys.path, and many bugs will left in third-party code for years. For example, in unittest the code

        if not top_level_dir in sys.path:
            sys.path.insert(0, top_level_dir)

should be replaced with more cumbersome

        for path in sys.path:
            if os.fsdecode(path) == top_level_dir:
                break
        else:
            sys.path.insert(0, top_level_dir)

In multiprocessing the code

    sys_path=sys.path.copy()
    try:
        i = sys_path.index('')
    except ValueError:
        pass
    else:
        sys_path[i] = process.ORIGINAL_DIR

should be replaced with

    sys_path=sys.path.copy()
    for i, path in enumerate(sys_path):
        if os.fsdecode(path) == '':
            sys_path[i] = process.ORIGINAL_DIR
            break

It is just two examples. I did not review the whole stdlib, and there should be more third-party code.
msg416042 - (view) Author: Noam Cohen (ncohen) * Date: 2022-03-26 06:36
You've got a point.
But in my opinion path-like object usage should be intuitive and users should not worry about converting it into string in some of the places.

What do you feel about sub-classing list for sys.path and converting path-like objects upon setting items / reading?
msg416048 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2022-03-26 07:59
The import system is already complex, so I think the hesitation about allowing arbitrary Path-like objects is warranted. (For example: are importlib's caching semantics really valid for *arbitrary* path-like objects? An object can be path-like without being immutable)

Coercion on input (as Noam suggests) would have a much lower risk of unwanted side effects, as any dynamic behaviour would be resolved at insertion time.
msg416050 - (view) Author: Noam Cohen (ncohen) * Date: 2022-03-26 10:06
So I've got in mind a PyListObject subclass with calls to PyOS_FSPath before insert, append, extend and __getitem__.

But I feel like this is an overkill; also, extend function might be trickier to implement.
Do any of you have better idea?
msg416052 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2022-03-26 10:58
I think you are trying to solve a wrong problem.

> This wasn't obvious because Path objects appear as strings in normal debug output, etc.

How is it?

>>> pathlib.Path('/usr/lib')
PosixPath('/usr/lib')
>>> [pathlib.Path('/usr/lib')]
[PosixPath('/usr/lib')]

I think the problem is with the debug output. Always use repr() for it, otherwise you will trick yourself.

> but the corresponding imports weren't working as they should

It would be easier to catch if it was never working with non-string. I think we should deprecate any non-string elements in sys.path.

As I shown above some code only works correctly with strings anyway.
msg416053 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2022-03-26 11:01
pkgutil just skips non-string elements in sys.path.

    for dir in search_path:
        if not isinstance(dir, str):
            continue
msg416057 - (view) Author: Noam Cohen (ncohen) * Date: 2022-03-26 12:08
that's why pre-insert conversion was suggested.
path-like objects could still be added to sys.path, while converting to str upon insert, preserving sys.path's bytes and strings only policy.
msg416065 - (view) Author: Marc-Andre Lemburg (lemburg) * (Python committer) Date: 2022-03-26 14:34
On 26.03.2022 08:59, Nick Coghlan wrote:
> 
> The import system is already complex, so I think the hesitation about allowing arbitrary Path-like objects is warranted. (For example: are importlib's caching semantics really valid for *arbitrary* path-like objects? An object can be path-like without being immutable)
> 
> Coercion on input (as Noam suggests) would have a much lower risk of unwanted side effects, as any dynamic behaviour would be resolved at insertion time.

This is not only about the import system. Lots of Python code out there
manipulates sys.path or reads sys.path for various reasons and does not
expect Path objects as list members, since only strings and bytes
are allowed:

https://docs.python.org/3/library/sys.html#sys.path

Conversion to strings sounds like a good way to get the best out of
both worlds.

I'm just curious on how this would work. You'd like have to create a
list subclass for use with sys.path which applies the conversion
whenever a non-string member gets added. Or perhaps add helper methods
to Path objects to safely add their value to sys.path.
msg416078 - (view) Author: Eryk Sun (eryksun) * (Python triager) Date: 2022-03-26 18:25
> I've got in mind a PyListObject subclass with calls to PyOS_FSPath 
> before insert, append, extend and __getitem__.

The sys module doesn't prevent rebinding sys.path. Most code I think is careful to update sys.path. But surely some code replaces it with a new list instead of updating via insert(), append(), extend(), or updating a slice such as `sys.path[:] = new_path`. For example, ModifiedInterpreter.transfer_path() in Lib/idlelib/pyshell.py rebinds sys.path. It doesn't have to, but it does.
History
Date User Action Args
2022-04-11 14:58:57adminsetgithub: 76823
2022-03-26 18:25:50eryksunsetnosy: + eryksun
messages: + msg416078
2022-03-26 14:34:00lemburgsetnosy: + lemburg
messages: + msg416065
2022-03-26 12:08:41ncohensetmessages: + msg416057
2022-03-26 11:01:58serhiy.storchakasetmessages: + msg416053
2022-03-26 10:58:08serhiy.storchakasetmessages: + msg416052
2022-03-26 10:06:24ncohensetmessages: + msg416050
2022-03-26 07:59:39ncoghlansetmessages: + msg416048
2022-03-26 06:36:45ncohensetmessages: + msg416042
2022-03-26 05:34:01serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg416037
2022-03-25 19:51:33ncohensetnosy: + ncohen
pull_requests: + pull_request30196
2022-03-13 07:51:00yan12125setnosy: - yan12125
2022-03-12 20:54:08jaracosetnosy: + jaraco
messages: + msg415012
2022-01-05 03:06:14eric.araujosetnosy: + eric.araujo

messages: + msg409732
versions: + Python 3.11, - Python 3.7
2020-08-29 15:36:58python-devsetnosy: + python-dev
pull_requests: + pull_request21106
2019-04-26 22:27:15brett.cannonsetnosy: + ncoghlan, eric.snow
messages: + msg340950
2018-11-13 20:59:37ammar2linkissue35237 superseder
2018-03-18 22:18:44jayyin11043setmessages: + msg314065
2018-03-15 23:51:48brett.cannonsetmessages: + msg313923
2018-03-15 15:32:45jayyin11043setmessages: + msg313887
2018-03-15 14:29:54jayyin11043setmessages: + msg313885
2018-03-15 14:13:03jayyin11043setmessages: + msg313883
2018-03-08 22:00:59jayyin11043setmessages: + msg313460
2018-03-07 15:17:49jayyin11043setmessages: + msg313388
2018-03-06 15:53:30brett.cannonsetmessages: + msg313337
2018-03-05 20:43:42jayyin11043setmessages: + msg313283
2018-02-15 05:42:58jayyin11043setkeywords: + patch
stage: patch review
pull_requests: + pull_request5484
2018-02-12 19:51:22jayyin11043setmessages: + msg312072
2018-02-12 19:44:51jayyin11043setmessages: + msg312070
2018-02-11 08:49:54jayyin11043setmessages: + msg311986
2018-02-09 16:11:11yan12125setnosy: + yan12125
messages: + msg311895
2018-02-09 03:49:23jayyin11043setmessages: + msg311864
2018-02-05 21:23:01jayyin11043setnosy: + jayyin11043
messages: + msg311693
2018-01-24 05:27:12chris.jerdonekcreate