Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 20 additions & 10 deletions plugins/python/parser/pyparser/pybuiltin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,44 @@
import os
import importlib.util
import traceback
import sysconfig
from jedi.api.classes import Name
from parserlog import log, bcolors
from parserconfig import ParserConfig

class PYBuiltin:
builtin = {}

@staticmethod
def __searchDirectory(directory: str):
for root, dirs, files in os.walk(directory):
for file in files:
p = os.path.join(root, file)
ext = os.path.splitext(p)[1]

if ext and ext.lower() == '.py':
PYBuiltin.builtin[p] = True

@staticmethod
def findBuiltins(config: ParserConfig):
try:
# Consider all Python modules in the stdlib directory builtin
sysconfig_paths = sysconfig.get_paths()
if "stdlib" in sysconfig_paths:
PYBuiltin.__searchDirectory(sysconfig_paths["stdlib"])

# Locate module paths via ModuleSpec
# However, this can return "frozen" and "built-in" as module origin and not the actual file
# Note: Python 3.10+ required
stdlib_modules = sys.stdlib_module_names

for e in stdlib_modules:
spec = importlib.util.find_spec(e)
if spec and spec.origin:
if spec and spec.origin and spec.origin != "frozen" and spec.origin != "built-in":
PYBuiltin.builtin[spec.origin] = True

if spec and spec.submodule_search_locations:
for submodule_dir in spec.submodule_search_locations:
for root, dirs, files in os.walk(submodule_dir):
for file in files:
p = os.path.join(root, file)
ext = os.path.splitext(p)[1]

if ext and ext.lower() == '.py':
PYBuiltin.builtin[p] = True

PYBuiltin.__searchDirectory(submodule_dir)
except:
log(f"{bcolors.FAIL}Failed to find Python builtins!")
if config.stack_trace:
Expand Down
41 changes: 41 additions & 0 deletions plugins/python/test/src/pythonparsertest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,47 @@ TEST_F(PythonParserTest, ImportModule)
EXPECT_EQ(pyname.is_import, true);
}

TEST_F(PythonParserTest, BuiltinVariable)
{
model::PYName pyname;

pyname = queryFile("imports.py",
(odb::query<model::PYName>::line_start == 2 &&
odb::query<model::PYName>::value == "import os"));

EXPECT_EQ(pyname.is_builtin, true);

pyname = queryFile("imports.py",
(odb::query<model::PYName>::line_start == 6 &&
odb::query<model::PYName>::value == "print"));

EXPECT_EQ(pyname.is_builtin, true);

pyname = queryFile("imports.py",
(odb::query<model::PYName>::line_start == 12 &&
odb::query<model::PYName>::value == "getpid"));

EXPECT_EQ(pyname.is_builtin, true);

pyname = queryFile("functions.py",
(odb::query<model::PYName>::line_start == 85 &&
odb::query<model::PYName>::value == "str"));

EXPECT_EQ(pyname.is_builtin, true);

pyname = queryFile("functions.py",
(odb::query<model::PYName>::line_start == 85 &&
odb::query<model::PYName>::value == "List"));

EXPECT_EQ(pyname.is_builtin, true);

pyname = queryFile("functions.py",
(odb::query<model::PYName>::line_start == 98 &&
odb::query<model::PYName>::value == "range"));

EXPECT_EQ(pyname.is_builtin, true);
}

TEST_F(PythonParserTest, ReferenceID)
{
model::PYName pyname;
Expand Down
Loading