The implementation of python I was using didn't have access to import pkgutil
I also wanted to tell the difference between:
- system / builtin modules
- modules installed with pip
- modules created by modifying the PYTHONPATH (e.g. local project imports)
This solution isn't perfect, it requires installing the regex
module, however it is pretty close.
# returns a set of strings
def list_module_names(system_only=False, installed_only=False):
# check if a file/folder is a package name (and return the package name if true)
def item_is_python_module(item_name, parent_path):
import regex
import os
if os.path.isdir(os.path.join(parent_path, item_name)):
# simple name of folder
result = regex.match(r"([a-zA-Z][a-zA-Z_0-9]*)$", item_name)
if result:
return result[1]
# dist name
result = regex.match(r"([a-zA-Z][a-zA-Z_0-9]*)-\d+(\.\d+)*\.dist-info$", item_name)
if result:
return result[1]
# if file
else:
# regular python file
result = regex.match(r"([a-zA-Z_][a-zA-Z_0-9\-]*)\.py$", item_name)
if result:
return result[1]
# cpython file
result = regex.match(r"([a-zA-Z_][a-zA-Z_0-9\-]*)\.cpython-.+\.(so|dll)$", item_name)
if result:
return result[1]
# nspkg.pth file
result = regex.match(r"([a-zA-Z_][a-zA-Z_0-9\.\-]*)-\d+(\.\d+)*-.+-nspkg.pth$", item_name)
if result:
return result[1]
# egg-link file
result = regex.match(r"([a-zA-Z_][a-zA-Z_0-9\.\-]*)\.egg-link$", item_name)
if result:
return result[1]
return False
import os
import sys
import subprocess
#
# what paths to look at
#
paths = sys.path
if system_only:
paths = eval(subprocess.run([sys.executable, '-S', '-s', '-u', '-c', 'import sys;print(list(sys.path))'], capture_output=True, env={"PYTHONPATH": "","PYTHONHOME": "",}).stdout)
else:
paths = eval(subprocess.run([sys.executable, '-u', '-c', 'import sys;print(list(sys.path))'], capture_output=True, env={"PYTHONPATH": "","PYTHONHOME": "",}).stdout)
#
# add all names
#
all_modules = set()
for each_path in paths:
if os.path.isdir(each_path):
files = os.listdir(each_path)
local_modules = [ item_is_python_module(each_file_name, each_path) for each_file_name in files ]
# filter out invalid ones
local_modules = set([ each for each in local_modules if each is not False ])
all_modules |= local_modules
# special module
all_modules.add('__main__')
return all_modules