1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
from dataclasses import dataclass
import importlib
import inspect
import pkgutil
import sys
import os
@dataclass
class Reflect:
client_code_path:str
imported_modules = {}
def __post_init__(self):
sys.path.insert(1, self.client_code_path)
self.client_modules = [p for p in pkgutil.iter_modules() if str(p[0])[12:-2] == self.client_code_path]
def import_module(self, module_name):
"""Imports a module. Before reflection can be conducted, a module
must be imported. WARNING: This will execute module code if it isn't
in a if __name__ == "__main__". Takes a module name (that the student made)
as the first argument.
Args:
module_name (str): The name of a student's module to import
"""
for module in self.client_modules:
if module.name == module_name:
self.imported_modules[module_name] = importlib.import_module(module.name)
def get_module_doc(self, module_name):
"""Gets the documentation provided for a module.
Args:
module_name (str): The student's module name to get documentation for
Returns:
str: Provided documentation
"""
return inspect.getdoc(self.imported_modules[module_name])
def get_classes(self, module_name):
"""Gets the classes in a given module. The module must be imported first.
Args:
module_name (str): The name of an imported module to get the name of.
Returns:
dict: Dictionary of classes. The name of the class is the index, followed by
a tuple containing the class object and the classes' documentation.
"""
return {
i[0]: (i[1], inspect.getdoc(i[1]))
for i in inspect.getmembers(self.imported_modules[module_name])
if inspect.isclass(i[1])
}
def get_class_methods(self, module_name, class_name):
"""Gets the user generated methods of a given class. The module must be imported first.
Args:
module_name (str): The name of the module in which the class is contained.
class_name (str): The name of the class.
Returns:
dict: A dictionary of the methods. The index is the function name, followed by a tuple
containing the function object, the documentation, and a list of the named arguments.
WARNING: Does not deal with *args and **kwargs and stuff.
"""
return {
i[0]: (i[1], inspect.getdoc(i[1]), inspect.getfullargspec(i[1])[0])
for i in inspect.getmembers(
self.get_classes(module_name)[class_name][0],
predicate=inspect.isfunction
)
}
def get_functions(self, module_name):
return {
i[0]: (i[1], inspect.getdoc(i[1]), inspect.getfullargspec(i[1])[0])
for i in inspect.getmembers(self.imported_modules[module_name])
if inspect.isfunction(i[1])
}
if __name__ == "__main__":
user_code_path = "/media/veracrypt1/Nextcloud/UniStuff/3.0 - CMP 3rd Year Project/ExampleSubmissions/Submission_A"
reflect = Reflect(user_code_path)
reflect.import_module("pjtool")
print(reflect.get_class_methods("pjtool", "Date"))
|