Pyclbr readmodule fails when run in script for another directory

Viewed 196

I want to run the function readmodule from pyclbr. The Python code runs fine in the terminal but fails in the script, when called from the terminal. It has to do something with the directory change.

I tried reading the source code but could not deduce any differentiating points.

This is the code in the script:

import os
import pyclbr
import sys

print(os.getcwd())
os.chdir('rto')
print(os.getcwd())
source_code = pyclbr.readmodule('car')
print(source_code)
source_code = pyclbr.readmodule('transport')
print(source_code)
source_code = pyclbr.readmodule('vehicles')
print(source_code)

I run the above script with this command: /usr/local/bin/python3 test_readmodule.py and face the following error:

/Users/aviralsrivastava/dev/generate_uml/inheritance_and_dependencies
/Users/aviralsrivastava/dev/generate_uml/inheritance_and_dependencies/rto
Inside _readmodule, module=car, path=[]
Traceback (most recent call last):
  File "test_readmodule.py", line 8, in <module>
    source_code = pyclbr.readmodule('car')
  File "/usr/local/Cellar/python/3.7.4_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/pyclbr.py", line 123, in readmodule
    for key, value in _readmodule(module, path or []).items():
  File "/usr/local/Cellar/python/3.7.4_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/pyclbr.py", line 190, in _readmodule
    if spec.submodule_search_locations is not None:
AttributeError: 'NoneType' object has no attribute 'submodule_search_locations'

However, when I run the same code in Python3 shell, in the same directory from where I was running the python3 command for the script:

āžœ  inheritance_and_dependencies git:(master) āœ— /usr/local/bin/python3
Python 3.7.4 (default, Sep  7 2019, 18:27:02)
[Clang 10.0.1 (clang-1001.0.46.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> import pyclbr
>>> import sys
>>>
>>> print(os.getcwd())
/Users/aviralsrivastava/dev/generate_uml/inheritance_and_dependencies
>>> os.chdir('rto')
>>> print(os.getcwd())
/Users/aviralsrivastava/dev/generate_uml/inheritance_and_dependencies/rto
>>> source_code = pyclbr.readmodule('car')
Inside _readmodule, module=car, path=[]
Inside _readmodule, module=vehicles, path=[]
Inside _readmodule, module=transport, path=[]
Inside _readmodule, module=vehicles, path=[]
returning from 157
>>> print(source_code)
{'Vehicle': <pyclbr.Class object at 0x105369510>, 'Farzi': <pyclbr.Class object at 0x1053c5e50>, 'CarPollutionPermit': <pyclbr.Class object at 0x1053c58d0>, 'BikePollutionPermit': <pyclbr.Class object at 0x1053c5e10>, 'Car': <pyclbr.Class object at 0x1052ebd50>, 'Bike': <pyclbr.Class object at 0x1053df210>}
>>> source_code = pyclbr.readmodule('transport')
Inside _readmodule, module=transport, path=[]
returning from 157
>>> print(source_code)
{'Vehicle': <pyclbr.Class object at 0x105369510>, 'Farzi': <pyclbr.Class object at 0x1053c5e50>, 'CarPollutionPermit': <pyclbr.Class object at 0x1053c58d0>, 'BikePollutionPermit': <pyclbr.Class object at 0x1053c5e10>}
>>> source_code = pyclbr.readmodule('vehicles')
Inside _readmodule, module=vehicles, path=[]
returning from 157
>>> print(source_code)
{'Vehicle': <pyclbr.Class object at 0x105369510>, 'Farzi': <pyclbr.Class object at 0x1053c5e50>}

I am fine with the error, but not fine with the different behavior in shell and script.

1 Answers

pyclbr.readmodule looks for modules in the ordinary module search path sys.path, optionally augmented by additional directories given by a path argument.

When you run python to launch an interactive session, one of the elements of sys.path is '.', a relative path referring to the current directory, whatever the current directory happens to be at the time. Changing the current directory with os.chdir thus affects module lookup.

When you run a script, depending on how you run it, a different path may take the place of the '.' entry, or it may be gone with no replacement. In this case, changing the current directory will not affect module lookup, or will affect it in stranger ways.


Instead of changing the working directory, you should either pass a path argument to pyclbr.readmodule:

pyclbr.readmodule('car',  path=['rto'])

or, if rto is supposed to be a package and car a submodule of the package, pass 'rto.car' as the module name instead of car:

pyclbr.readmodule('rto.car')
Related