Bug 1865924 - Import reads files from current working directory, instead of system library
Summary: Import reads files from current working directory, instead of system library
Keywords:
Status: CLOSED NOTABUG
Alias: None
Product: Fedora
Classification: Fedora
Component: python3.9
Version: rawhide
Hardware: Unspecified
OS: Unspecified
unspecified
unspecified
Target Milestone: ---
Assignee: Miro Hrončok
QA Contact: Fedora Extras Quality Assurance
URL:
Whiteboard:
Depends On:
Blocks: 1863666
TreeView+ depends on / blocked
 
Reported: 2020-08-04 14:21 UTC by Milan Crha
Modified: 2020-08-08 16:11 UTC (History)
8 users (show)

Fixed In Version:
Doc Type: If docs needed, set a value
Doc Text:
Clone Of:
Environment:
Last Closed: 2020-08-05 12:25:37 UTC
Type: Bug
Embargoed:


Attachments (Terms of Use)

Description Milan Crha 2020-08-04 14:21:05 UTC
The libical rebuild fails with

  AttributeError: 'gi.repository.GLib' object has no attribute 'Idle'

as can be seen here:
https://koji.fedoraproject.org/koji/taskinfo?taskID=48606102

(I copied one of the tests error log below.)

It took me some time to realize what that lengthy backtrace means, to find out that a system library socket.py includes:

   import array

and the python3.9 decides to load array.py from the current working directory, instead of the system library. Apart of the backtrace (it didn't make much sense to see the internal attach.py test in the backtrace) when I rename the array.py to something else the test passes with no problem.

Someone may call it a feature. Someone else may call it a security issue. What I know is that the previous version of python didn't have this problem.

--------------------------------------------------------------------------

The full error log for one of the tests:

57: Test command: /usr/bin/python3 "/builddir/build/BUILD/libical-3.0.8/src/test/libical-glib/value.py"
57: Environment variables: 
57:  GI_TYPELIB_PATH=/builddir/build/BUILD/libical-3.0.8/x86_64-redhat-linux-gnu/src/libical-glib
57:  LD_LIBRARY_PATH=/builddir/build/BUILD/libical-3.0.8/x86_64-redhat-linux-gnu/lib
57:  ZONEINFO_DIRECTORY=/builddir/build/BUILD/libical-3.0.8/zoneinfo
57: Test timeout computed to be: 10000000
57: Traceback (most recent call last):
57:   File "/builddir/build/BUILD/libical-3.0.8/src/test/libical-glib/value.py", line 25, in <module>
57:     from gi.repository import ICalGLib
57:   File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
57:   File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
57:   File "<frozen importlib._bootstrap>", line 664, in _load_unlocked
57:   File "<frozen importlib._bootstrap>", line 627, in _load_backward_compatible
57:   File "/usr/lib64/python3.9/site-packages/gi/importer.py", line 145, in load_module
57:     importlib.import_module('gi.repository.' + dep.split("-")[0])
57:   File "/usr/lib64/python3.9/importlib/__init__.py", line 127, in import_module
57:     return _bootstrap._gcd_import(name[level:], package, level)
57:   File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
57:   File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
57:   File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
57:   File "<frozen importlib._bootstrap>", line 664, in _load_unlocked
57:   File "<frozen importlib._bootstrap>", line 627, in _load_backward_compatible
57:   File "/usr/lib64/python3.9/site-packages/gi/importer.py", line 145, in load_module
57:     importlib.import_module('gi.repository.' + dep.split("-")[0])
57:   File "/usr/lib64/python3.9/importlib/__init__.py", line 127, in import_module
57:     return _bootstrap._gcd_import(name[level:], package, level)
57:   File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
57:   File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
57:   File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
57:   File "<frozen importlib._bootstrap>", line 664, in _load_unlocked
57:   File "<frozen importlib._bootstrap>", line 627, in _load_backward_compatible
57:   File "/usr/lib64/python3.9/site-packages/gi/importer.py", line 146, in load_module
57:     dynamic_module = load_overrides(introspection_module)
57:   File "/usr/lib64/python3.9/site-packages/gi/overrides/__init__.py", line 118, in load_overrides
57:     override_mod = importlib.import_module(override_package_name)
57:   File "/usr/lib64/python3.9/importlib/__init__.py", line 127, in import_module
57:     return _bootstrap._gcd_import(name[level:], package, level)
57:   File "/usr/lib64/python3.9/site-packages/gi/overrides/GLib.py", line 24, in <module>
57:     import socket
57:   File "/usr/lib64/python3.9/socket.py", line 548, in <module>
57:     import array
57:   File "/builddir/build/BUILD/libical-3.0.8/src/test/libical-glib/array.py", line 25, in <module>
57:     from gi.repository import ICalGLib
57:   File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
57:   File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
57:   File "<frozen importlib._bootstrap>", line 664, in _load_unlocked
57:   File "<frozen importlib._bootstrap>", line 627, in _load_backward_compatible
57:   File "/usr/lib64/python3.9/site-packages/gi/importer.py", line 145, in load_module
57:     importlib.import_module('gi.repository.' + dep.split("-")[0])
57:   File "/usr/lib64/python3.9/importlib/__init__.py", line 127, in import_module
57:     return _bootstrap._gcd_import(name[level:], package, level)
57:   File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
57:   File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
57:   File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
57:   File "<frozen importlib._bootstrap>", line 664, in _load_unlocked
57:   File "<frozen importlib._bootstrap>", line 627, in _load_backward_compatible
57:   File "/usr/lib64/python3.9/site-packages/gi/importer.py", line 146, in load_module
57:     dynamic_module = load_overrides(introspection_module)
57:   File "/usr/lib64/python3.9/site-packages/gi/overrides/__init__.py", line 118, in load_overrides
57:     override_mod = importlib.import_module(override_package_name)
57:   File "/usr/lib64/python3.9/importlib/__init__.py", line 127, in import_module
57:     return _bootstrap._gcd_import(name[level:], package, level)
57:   File "/usr/lib64/python3.9/site-packages/gi/overrides/GObject.py", line 59, in <module>
57:     globals()[name] = getattr(GLib, name)
57:   File "/usr/lib64/python3.9/site-packages/gi/overrides/__init__.py", line 32, in __getattr__
57:     return getattr(self._introspection_module, name)
57:   File "/usr/lib64/python3.9/site-packages/gi/module.py", line 131, in __getattr__
57:     raise AttributeError("%r object has no attribute %r" % (
57: AttributeError: 'gi.repository.GLib' object has no attribute 'Idle'
57/57 Test #57: libical-glib-value ...............***Failed    0.09 sec

Comment 1 Miro Hrončok 2020-08-04 14:41:11 UTC
> and the python3.9 decides to load array.py from the current working directory, instead of the system library.

Yes, this is a documented and expected behavior. Invoke python with -I to avoid that.

$ pwd
/home/churchyard/tmp

$ python3
Python 3.8.5 (default, Jul 20 2020, 00:00:00) 
[GCC 10.1.1 20200507 (Red Hat 10.1.1-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
['', '/usr/lib64/python38.zip', '/usr/lib64/python3.8', '/usr/lib64/python3.8/lib-dynload', '/home/churchyard/.local/lib/python3.8/site-packages', '/usr/lib64/python3.8/site-packages', '/usr/lib/python3.8/site-packages']
>>> import array
>>> array.__file__
'/home/churchyard/tmp/array.py'

$ python3 -I
Python 3.8.5 (default, Jul 20 2020, 00:00:00) 
[GCC 10.1.1 20200507 (Red Hat 10.1.1-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys  
>>> sys.path
['/usr/lib64/python38.zip', '/usr/lib64/python3.8', '/usr/lib64/python3.8/lib-dynload', '/usr/lib64/python3.8/site-packages', '/usr/lib/python3.8/site-packages']
>>> import array
>>> array.__file__
'/usr/lib64/python3.8/lib-dynload/array.cpython-38-x86_64-linux-gnu.so'
>>>

Comment 2 Miro Hrončok 2020-08-04 14:45:17 UTC
Note that this was always the case, but -I only works on Python 3:

$ python3.9 -c 'import array; print(array.__file__)'
/home/churchyard/tmp/array.py

$ python3.9 -I -c 'import array; print(array.__file__)'
/usr/lib64/python3.9/lib-dynload/array.cpython-39-x86_64-linux-gnu.so

...

$ python3.5 -c 'import array; print(array.__file__)'
/home/churchyard/tmp/array.py

$ python3.5 -I -c 'import array; print(array.__file__)'
/usr/lib64/python3.5/lib-dynload/array.cpython-35m-x86_64-linux-gnu.so

$ python3.4 -c 'import array; print(array.__file__)'
/home/churchyard/tmp/array.py

$ python3.4 -I -c 'import array; print(array.__file__)'
/usr/lib64/python3.4/lib-dynload/array.cpython-34m.so

$ python2.7 -c 'import array; print(array.__file__)'
array.py

$ python2.7 -I -c 'import array; print(array.__file__)'
Unknown option: -I
usage: python2.7 [option] ... [-c cmd | -m mod | file | -] [arg] ...
Try `python -h' for more information.

Comment 3 Milan Crha 2020-08-04 14:54:33 UTC
Hmm, that might mean the change was on the build side, where the most probably change is the CMake build change.

Comment 4 Milan Crha 2020-08-04 15:16:08 UTC
Nope, it's not it. I do not understand how it could work with python3-3.8.3~rc1-1.fc33.x86_64 [1], but fail with python3-3.9.0~b5-4.fc33.x86_64 (the link to the build is in the description).

Both builds use:

45: Test command: /usr/bin/python3 "/builddir/build/BUILD/libical-3.0.8/src/test/libical-glib/array.py"
45: Environment variables: 
45:  GI_TYPELIB_PATH=/builddir/build/BUILD/libical-3.0.8/x86_64-redhat-linux-gnu/src/libical-glib
45:  LD_LIBRARY_PATH=/builddir/build/BUILD/libical-3.0.8/x86_64-redhat-linux-gnu/lib
45:  ZONEINFO_DIRECTORY=/builddir/build/BUILD/libical-3.0.8/zoneinfo


45: Test command: /usr/bin/python3 "/builddir/build/BUILD/libical-3.0.8/src/test/libical-glib/array.py"
45: Environment variables: 
45:  GI_TYPELIB_PATH=/builddir/build/BUILD/libical-3.0.8/x86_64-redhat-linux-gnu/src/libical-glib
45:  LD_LIBRARY_PATH=/builddir/build/BUILD/libical-3.0.8/x86_64-redhat-linux-gnu/lib
45:  ZONEINFO_DIRECTORY=/builddir/build/BUILD/libical-3.0.8/zoneinfo

and as far as I can tell, the current working directory (not shown here) is CMAKE_CURRENT_BINARY_DIR, which is, in my `fedpkg local` build: 
'/home/zyx/devel/fedora/libical/master/libical-3.0.8/x86_64-redhat-linux-gnu/src/test/libical-glib', where is no .py file at all.

[1] https://koji.fedoraproject.org/koji/buildinfo?buildID=1507521

Comment 5 Milan Crha 2020-08-04 15:45:09 UTC
Just to confirm, adding the -I into the python3 command helps.

What lefts here is to understand why it was not needed before, but is needed now. When I added the sys.path print into the array.py it shows this when being invoked as part of the `make test`:

45: ['/home/zyx/devel/fedora/libical/master/libical-3.0.8/src/test/libical-glib', '/usr/lib64/python39.zip', '/usr/lib64/python3.9', '/usr/lib64/python3.9/lib-dynload', '/usr/lib64/python3.9/site-packages', '/usr/lib/python3.9/site-packages']

The difference is the first argument, not being the empty string, but the current source directory (not the current build directory). I tried a scratch build under Fedora 32 with the same print [1] and it says:

43: ['/builddir/build/BUILD/libical-3.0.8/src/test/libical-glib', '/usr/lib/python38.zip', '/usr/lib/python3.8', '/usr/lib/python3.8/lib-dynload', '/usr/lib/python3.8/site-packages']

Could any defaults in the behaviour change on the python side, like no -I needed before, only now?

I tried a scratch build on rawhide ( https://koji.fedoraproject.org/koji/taskinfo?taskID=48640211 ) and it says there:

43: ['/builddir/build/BUILD/libical-3.0.8/src/test/libical-glib', '/usr/lib/python39.zip', '/usr/lib/python3.9', '/usr/lib/python3.9/lib-dynload', '/usr/lib/python3.9/site-packages']

Comment 6 Milan Crha 2020-08-04 15:46:45 UTC
[1] https://koji.fedoraproject.org/koji/taskinfo?taskID=48639808 (Fedora 32 scratch build link)

Comment 7 Milan Crha 2020-08-04 16:30:12 UTC
I made a change on the libical side to have invoked the tests with the -I.

It would be nice to verify what changed on the python/rpmbuild side here, whether it was intentional or not, just in case. Otherwise you can close this bug report. Thank you for your help on this.

Comment 8 Miro Hrončok 2020-08-04 18:19:51 UTC
There was no Python change of the sys.path behavior. The only difference is that the socket module now imports array and it did not do that before:


[~]$ python3.8
Python 3.8.5 (default, Jul 20 2020, 00:00:00) 
[GCC 10.1.1 20200507 (Red Hat 10.1.1-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import socket
>>> socket.array
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'socket' has no attribute 'array'


[~]$ python3.9
Python 3.9.0b5 (default, Jul 20 2020, 00:00:00) 
[GCC 10.1.1 20200507 (Red Hat 10.1.1-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import socket
>>> socket.array
<module 'array' from '/usr/lib64/python3.9/lib-dynload/array.cpython-39-x86_64-linux-gnu.so'>


As a reproducer of the problem:

$ cat tmp/value.py 
import sys, socket
print("value.py", sys.path[0], socket.array.__file__)

$ cat tmp/array.py
print("array.py")

$ python3.9 tmp/value.py 
array.py
value.py /home/churchyard/tmp /home/churchyard/tmp/array.py

$ python3.8 tmp/value.py 
Traceback (most recent call last):
  File "tmp/value.py", line 2, in <module>
    print("value.py", sys.path[0], socket.array.__file__)
AttributeError: module 'socket' has no attribute 'array'



See https://github.com/fedora-python/cpython/commit/8d120f75fb8c8731464b5f7531d74cdbb897d924

Comment 9 Milan Crha 2020-08-05 05:39:59 UTC
(In reply to Miro Hrončok from comment #8)
> The only difference is that the socket module now imports array and it did not do that before

I see, that was also an option (that some module changed and imports something new, in this case the clashing 'array'). Might there help if the socket module doesn't use `import array`, but specify precisely from where the array should be imported? (like being done here: `from gi.repository import ICalGLib`)

Unless it would be desired to avoid surprises like this (and eventual security problem by replacing system libraries/modules with local versions) you can close this as Not a Bug or any such resolution. Thanks again for you help here.

Comment 10 Miro Hrončok 2020-08-05 09:02:13 UTC
There is no "from" solution. `from gi.repository import ICalGLib` is affected in the same way if you put gi.py in the script's directory.

The mitigation is to not name top level modules the same way as standard library ones.

Comment 11 Allen Winter 2020-08-08 16:11:01 UTC
For the record, I just upstreamed the patch to libical official repository for the 3.0 and master branches
commit 07d1edb3393e86c821282beb6f7aa607beaeedc4


Note You need to log in before you can comment on or make changes to this bug.