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
4 changes: 3 additions & 1 deletion app.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@


dataset_controller = DatasetController.get_instance()
dataset_name = 'No project selected' if dataset_controller.current_dataset is None else dataset_controller.current_dataset.dataset_name
dataset_name = 'No project selected' if dataset_controller.current_dataset is None else dataset_controller.current_dataset.name

app = Flask(__name__)
app.config['SECRET_KEY'] = 'test_key'
Expand Down Expand Up @@ -134,3 +134,5 @@ def test_all_objects():
# TODO tidy up the logs
# TODO file explorer for load dataset
# TODO make sure it runs from docker also
# TODO fetch Variables, FunctionArgs, Imports
# TODO add pystruct commands for --uml, --report, --structure
37 changes: 37 additions & 0 deletions pystruct/objects/imports_data_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,44 @@
from pystruct.objects.data_objects import DataframeObjectABC, HTMLTableObjectABC, HTMLObjectABC
from pystruct.objects.metric_obj import IsScriptFile
from pystruct.objects.python_object import PObject
from pystruct.python.code_structure import ClassMethodObj
from pystruct.reports.import_graph import CollectImportsVisitor
from pystruct.visitors.visitor import TreeNodeVisitor


class ClassMethodsRawDataframe(DataframeObjectABC):
class CollectClassMethodsVisitor(TreeNodeVisitor):
def __init__(self):
self._class_methods_list = []

def visit_class_method(self, node):
class_name = node.parent.data.name
method = node.data.name.split('.')[-1]
self._class_methods_list.append({'class': class_name, 'method': method})

def to_dataframe(self):
return pd.DataFrame(self._class_methods_list, columns=['class', 'method'])

def build(self):
pobj = PObject().python_source_object()
class_methods = ClassMethodsRawDataframe.CollectClassMethodsVisitor()
pobj.use_visitor(class_methods)
return class_methods.to_dataframe()


class ClassMethodsEnrichedDataframe(DataframeObjectABC):
def build(self):
df = ClassMethodsRawDataframe().dataframe()
self.enrich_df(df)
return df

def enrich_df(self, df):
df['visibility'] = df['method'].apply(lambda method: 'private' if method[:2] == '__'
else 'protected' if method[:1] == '_' else 'public')


if __name__ == '__main__':
ClassMethodsEnrichedDataframe().dataframe()


class ImportsRawDataframe(DataframeObjectABC):
Expand Down
4 changes: 4 additions & 0 deletions pystruct/python/code_structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ class ModuleObj(PythonCodeObj):
type = "module"


class ImportObj(PythonCodeObj):
type = "import"


class ClassObj(CompoundStatementCodeMixin):
type = "class"

Expand Down
7 changes: 7 additions & 0 deletions pystruct/python/python_obj_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ def module(self, name, code, parent=None):
module_obj = ModuleObj(name, path, code)
return TreeNode(parent, data=module_obj)

def import_node(self, name, code, parent=None):
path = parent.data.path
import_obj = ImportObj(name, path, code)
return TreeNode(parent, data=import_obj)

def class_node(self, name, code, parent=None):
path = parent.data.path
class_obj = ClassObj(name, path, code)
Expand All @@ -50,6 +55,8 @@ def node_dict_to_object(self, name, node_dict):
return DirectoryObj(name=name, path=None)
elif node_dict['type'] == "module":
return ModuleObj(name, None, node_dict['code'])
elif node_dict['type'] == "import":
return ImportObj(name, None, node_dict['code'])
elif node_dict['type'] == "class":
return ClassObj(name, None, node_dict['code'])
elif node_dict['type'] == "function":
Expand Down
2 changes: 1 addition & 1 deletion pystruct/utils/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from io import StringIO
import shutil

from git import Repo
# from git import Repo

from pystruct.utils.python_utils import MultiSingleton
from pystruct.utils.logs import log_disk_ops
Expand Down
10 changes: 7 additions & 3 deletions pystruct/visitors/init_visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from pystruct.utils.python_file_utils import is_python_file
from pystruct.visitors.visitor import TreeNodeVisitor


# https://python-ast-explorer.com/
class PythonObjInitializer(TreeNodeVisitor):

def __init__(self, obj_factory):
Expand All @@ -33,8 +33,12 @@ def visit_directory(self, node):

def visit_module(self, node):
log_cyan(f".Visiting module {node}", verbosity=3)
asts_to_fetch = {ast.ClassDef: self._obj_factory.class_node,
ast.FunctionDef: self._obj_factory.function}
asts_to_fetch = {
# ast.Import: self._obj_factory.import_node,
# ast.ImportFrom: self._obj_factory.import_node,
ast.ClassDef: self._obj_factory.class_node,
ast.FunctionDef: self._obj_factory.function
}

list_objs = []

Expand Down
5 changes: 5 additions & 0 deletions pystruct/visitors/visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ def visit(self, node):
self.visit_directory(node)
elif node.data.type == "module":
self.visit_module(node)
elif node.data.type == "import":
self.visit_import(node)
elif node.data.type == "class":
self.visit_class(node)
elif node.data.type == "function":
Expand All @@ -35,6 +37,9 @@ def visit_directory(self, node):
def visit_module(self, node):
pass

def visit_import(self, node):
pass

def visit_class(self, node):
pass

Expand Down
41 changes: 40 additions & 1 deletion tests/python/test_python_source_obj.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def a_class_method(self):

"""

expected_dict_str = r"""{"pystruct": {"type": "directory", "code": null, "branches": ["pystruct.a"]}, "pystruct.a": {"type": "module", "code": "\nimport one_package\nfrom another_package import that_module\n\ndef a_function(some, arguments):\n var = \"something\"\n return var\n\nclass a_class:\n def a_class_method(self):\n return 1\n\n", "branches": ["pystruct.a.a_function", "pystruct.a.a_class"]}, "pystruct.a.a_function": {"type": "function", "code": "def a_function(some, arguments):\n var = \"something\"\n return var", "branches": []}, "pystruct.a.a_class": {"type": "class", "code": "class a_class:\n def a_class_method(self):\n return 1", "branches": ["pystruct.a.a_class.a_class_method"]}, "pystruct.a.a_class.a_class_method": {"type": "class_method", "code": "def a_class_method(self):\n return 1", "branches": []}}"""
# expected_dict_str = r"""{"pystruct": {"type": "directory", "code": null, "branches": ["pystruct.a"]}, "pystruct.a": {"type": "module", "code": "\nimport one_package\nfrom another_package import that_module\n\ndef a_function(some, arguments):\n var = \"something\"\n return var\n\nclass a_class:\n def a_class_method(self):\n return 1\n\n", "branches": ["pystruct.a.a_function", "pystruct.a.a_class"]}, "pystruct.a.a_function": {"type": "function", "code": "def a_function(some, arguments):\n var = \"something\"\n return var", "branches": []}, "pystruct.a.a_class": {"type": "class", "code": "class a_class:\n def a_class_method(self):\n return 1", "branches": ["pystruct.a.a_class.a_class_method"]}, "pystruct.a.a_class.a_class_method": {"type": "class_method", "code": "def a_class_method(self):\n return 1", "branches": []}}"""


class TestPythonSourceObj(unittest.TestCase):
Expand All @@ -29,6 +29,7 @@ def setUp(self):
f.write(a_py_file_content)

def test_create_from_project_source(self):
expected_dict_str = open('/tests/res_files/example_PObject.json').read()
expected_dict = json.loads(expected_dict_str.replace('pystruct', os.path.basename(self.tmp_dir.name)))

pobj = PythonSourceObj.from_dict(expected_dict)
Expand All @@ -37,6 +38,8 @@ def test_create_from_project_source(self):
self.assertEqual(test_dict, expected_dict)

def test_create_from_dict(self):
expected_dict_str = open('/tests/res_files/example_PObject.json').read()

pobj = PythonSourceObj.from_dict(self.tmp_dir.name)

test_dict = pobj.to_dict()
Expand All @@ -45,5 +48,41 @@ def test_create_from_dict(self):
self.assertEqual(test_dict, expected_dict)


class TestPythonSourceObjIntegration(unittest.TestCase):
def setUp(self) -> None:
path = "../res_files/example_project"
self.pso = PythonSourceObj.from_project_source(path)
self.pso_dict = self.pso.to_dict()
self.expected_dict = json.loads(open('../res_files/example_PObject.json').read())
self.maxDiff = None

def test_directories(self):
type = 'directory'
example_obj = {k: v for k, v in self.pso_dict.items() if v['type'] == type}
expected_obj = {k: v for k, v in self.expected_dict.items() if v['type'] == type}
self.assertDictEqual(example_obj, expected_obj)

def test_modules(self):
type = 'module'
example_obj = {k: v for k, v in self.pso_dict.items() if v['type'] == type}
expected_obj = {k: v for k, v in self.expected_dict.items() if v['type'] == type}
self.assertDictEqual(example_obj, expected_obj)

def test_functions(self):
type = 'function'
example_obj = {k: v for k, v in self.pso_dict.items() if v['type'] == type}
expected_obj = {k: v for k, v in self.expected_dict.items() if v['type'] == type}
self.assertDictEqual(example_obj, expected_obj)

def test_classes(self):
type = 'class'
example_obj = {k: v for k, v in self.pso_dict.items() if v['type'] == type}
expected_obj = {k: v for k, v in self.expected_dict.items() if v['type'] == type}
self.assertDictEqual(example_obj, expected_obj)

def test_result(self):
self.assertDictEqual(self.pso_dict, self.expected_dict)


if __name__ == '__main__':
unittest.main()
34 changes: 34 additions & 0 deletions tests/res_files/example_PObject.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"example_project": {
"type": "directory",
"code": null,
"branches": [
"example_project.example_module1"
]
},
"example_project.example_module1": {
"type": "module",
"code": "import one_package\nfrom another_package import that_module\n\ndef a_function(some, arguments):\n var = \"something\"\n return var\n\nclass a_class:\n def a_class_method(self):\n return 1\n",
"branches": [
"example_project.example_module1.a_function",
"example_project.example_module1.a_class"
]
},
"example_project.example_module1.a_function": {
"type": "function",
"code": "def a_function(some, arguments):\n var = \"something\"\n return var",
"branches": []
},
"example_project.example_module1.a_class": {
"type": "class",
"code": "class a_class:\n def a_class_method(self):\n return 1",
"branches": [
"example_project.example_module1.a_class.a_class_method"
]
},
"pystruct.a.a_class.a_class_method": {
"type": "class_method",
"code": "def a_class_method(self):\n return 1",
"branches": []
}
}
10 changes: 10 additions & 0 deletions tests/res_files/example_project/example_module1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import one_package
from another_package import that_module

def a_function(some, arguments):
var = "something"
return var

class a_class:
def a_class_method(self):
return 1