This repository has been archived by the owner on Nov 26, 2023. It is now read-only.
Add Support for Calls to External Class Instances #61
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Description of the Problem
Up until now, for python code in the form of:
PyCG would capture only the call to the external Class.
This incomplete call graph does not capture the external call to the class instance, resulting in limited analysis capabilities for code using external packages and their class instances.
An enriched call graph with all the external functions or classes called would also significantly improve the later stitching process with the
externalPackage
Root Cause
During the call graph processing phase, when PyCG visits a call within the AST, it aims to find the actual namespace of the calls performed. The namespace provides the complete path or location of the function or class within the program's module structure.
PyCG/pycg/processing/cgprocessor.py
Line 143 in 373751b
Note: The calls might be more than one in cases like the following:
package.Class().method().method()
Then, for each identified call, if it is an external call PyCG will create an edge.
PyCG/pycg/processing/cgprocessor.py
Lines 158 to 166 in 373751b
During the
retrieve_call_names
method the_retrieve_attribute_names
is called to find the call name in the case of an attribute (a call in the form ofsomething.something
)PyCG/pycg/processing/base.py
Lines 473 to 474 in 373751b
Within the
_retrieve_attribute_names
, we call the_retrieve_parent_names
in order to identify the parent namespace of a specific attribute node.To adduce an example,
in the following attribute:
the parent namespace of the attribute is
Class
while on this example:
the parent namespace of the
x.instance_method()
attribute is againClass
So in order to find the parent namespace (and consequently its definition) we just decode the parent node
PyCG/pycg/processing/base.py
Lines 313 to 317 in 373751b
The
decode_node
method is a very fundamental method which tries to find the definition stored in the symbol table of a corresponding AST node.In our case e.g.
the
externalPackage.Class().instance_method()
call the parent node of the.instance_method()`` which will be decoded is
externalPackage.Class()which is instance of
ast.Call`When decoding call nodes, PyCG tries to decode the node performing the call and based on the type of definition, it returns the correct namespace e.g. for functions it returns the return type. for classes it returns the namespace of the class (since it is a class instance). But currently PyCG would ignore calls performed by nodes with external definitions, since it could not resolve for example the return type of such calls.
PyCG/pycg/processing/base.py
Lines 207 to 225 in 373751b
Proposed Change
To tackle the afforementioned issue, we handle external definitions performing calls as internal classes performing calls, e.g. we return the namespace of the external node performing the call. On this way all calls to external functions or classes are stored within the call graph.
With this change, we store calls to external class instances in the call graph. There are two types of such calls.
The first one, is a call in the form of
node = externalPackage.Class().instance_method()
which will be stored asThis wil be a sound representation which will lead to a sound stitching at a later stage.
The second one, is a call in the form of
node = externalPackage.method().instance_method()
weremethod
is a function returning a class and theinstance_method
is an instance of the class returned by themethod()
.This call will be represented in the call graph as following:
This representation will not be sound, since the
externalPackage.method.instance_method
is not the actual namespace of theinstance_method
, but PyCG cannot provide any additional insight on the returned class, and therefore the best it can do is to outsource the resolution for the stitching process.Note:
With the proposed changes, all types of external calls can be handled:
Test Case 1
Test Case 2
Test Case 3