Skip to content
Open
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
4 changes: 2 additions & 2 deletions plugins/python/parser/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
jedi==0.19.2
parso==0.8.6
jedi==0.20.0
parso==0.8.7
6 changes: 4 additions & 2 deletions plugins/python/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ add_test(NAME pythonparser COMMAND pythonparsertest
--name pythonparsertest \
--input ${CMAKE_CURRENT_SOURCE_DIR}/sources/ \
--workspace ${CMAKE_CURRENT_BINARY_DIR}/workdir/ \
--force"
--force \
--debug"
"${TEST_DB}")

add_test(NAME pythonservice COMMAND pythonservicetest
Expand All @@ -64,5 +65,6 @@ add_test(NAME pythonservice COMMAND pythonservicetest
--name pythonservicetest \
--input ${CMAKE_CURRENT_SOURCE_DIR}/sources/ \
--workspace ${CMAKE_CURRENT_BINARY_DIR}/workdir/ \
--force"
--force \
--debug"
"${TEST_DB}")
56 changes: 50 additions & 6 deletions plugins/python/test/src/pythonparsertest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,14 @@ class PythonParserTest : public ::testing::Test

model::PYName queryFile(const std::string& filename, const odb::query<model::PYName>& odb_query)
{
model::PYName pyname;
if (m_files.count(filename))
if(!m_files.count(filename))
{
_transaction([&, this]() {
pyname = _db->query_value<model::PYName>(odb_query && odb::query<model::PYName>::file_id == m_files[filename]);
});
throw std::runtime_error("File " + filename + " not found!");
}

return pyname;
return _transaction([&, this]() {
return _db->query_value<model::PYName>(odb_query && odb::query<model::PYName>::file_id == m_files[filename]);
});
}

private:
Expand Down Expand Up @@ -408,6 +407,51 @@ 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);

// Test case below is skipped, the parsing library
// Jedi throws an exception when we try to get the
// definition for built-in function print.

// 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