Sfoglia il codice sorgente

Ajout de l'objet Mention, refact du script d'analyse

olivier.massot 7 anni fa
parent
commit
5692f4eb41
2 ha cambiato i file con 80 aggiunte e 87 eliminazioni
  1. 9 9
      Viewer.py
  2. 71 78
      core.py

+ 9 - 9
Viewer.py

@@ -356,24 +356,24 @@ class Viewer(QMainWindow):
         self.ui.txtPanel.clear()
         self.ui.treeWidget.clear()
 
-        if self.ui.radioRefsOnly.isChecked():
-            mode = core.REFS_ONLY
-        elif self.ui.radioDepsOnly.isChecked():
-            mode = core.DEPS_ONLY
-        else:
-            mode = core.DEPS_AND_REFS
-        print(mode)
+#         if self.ui.radioRefsOnly.isChecked():
+#             mode = core.REFS_ONLY
+#         elif self.ui.radioDepsOnly.isChecked():
+#             mode = core.DEPS_ONLY
+#         else:
+#             mode = core.DEPS_AND_REFS
+#         print(mode)
 
         QApplication.setOverrideCursor(Qt.WaitCursor)
 
-        core.Analyse.run(source_dir, mode)
+        core.Analyse.run(source_dir)
 
         QApplication.restoreOverrideCursor()
 
         if core.Analyse.duplicated_names:
             QMessageBox.warning(self, "Risque d'instabilités", "Attention! Des doublons ont été trouvés dans les noms des objets suivants:\n{}".format(", ".join(core.Analyse.duplicated_names)))
 
-        QMessageBox.information(self, "test", "{} objets chargés".format(len(core.Analyse.objects)))
+#         QMessageBox.information(self, "test", "{} objets chargés".format(len(core.Analyse.objects)))
 
         self.ui.progressBar.setVisible(False)
         self.ui.stackedWidget.setCurrentIndex(1)

+ 71 - 78
core.py

@@ -5,8 +5,10 @@
 '''
 import re
 
+from _regex_core import MULTILINE
 from path import Path
 
+
 # TODO: gérer les cas où des objets de types différents portent le même nom. Ex: une table et un formulaire... Note: une requete et une table ne peuvent pas porter le même nom.
 # TODO: ignorer les commentaires dans les modules
 # TODO: ignorer les labels dans les formulaires et états
@@ -14,8 +16,6 @@ from path import Path
 # TODO: Stocker un aperçu du contexte de la ou des références dans le code source, pour contrôle ultérieur
 # TODO: Permettre de supprimer / ajouter des réferences
 # TODO: Verifier que la recherche puisse être Case sensitive?
-
-
 def recurse(acc_obj):
     deptree = []
     for dep in acc_obj.deps:
@@ -30,14 +30,12 @@ class InvalidFileExt(IOError):
 class AccessObject():
     type_ = "<unknown>"
     _valid_file_exts = (".bas")
-    _re_w_sep = """^|\s|\[|\]|&|\(|\)|\.|!|"|'"""
 
     def __init__(self, name_):
         self.name_ = name_
         self.functions = []
         self.sourcefile = ""
-        self.deps = []
-        self.refs = []
+        self.deps = {}
         self._sourcecode = ""
 
     def __repr__(self):
@@ -59,14 +57,6 @@ class AccessObject():
             self._sourcecode = self.sourcefile.text()
         return self._sourcecode
 
-    def add_dep(self, obj):
-        if not obj in self.deps:
-            self.deps.append(obj)
-
-    def add_ref(self, obj):
-        if not obj in self.refs:
-            self.refs.append(obj)
-
     SUBSTR = {92: "\\", 47: "/", 58: ":", 42: "*", 63:"?", 34:"\"", 60:"<", 62:">", 124:"|" }
     @staticmethod
     def path_to_name(path):
@@ -75,22 +65,6 @@ class AccessObject():
             name_ = name_.replace("[{}]".format(ascii_code), char)
         return name_
 
-    def search_me_regex(self):
-        return re.compile(r"""({sep}){name_}({sep})""".format(sep=self._re_w_sep, name_=self.name_))
-
-    def containsRefsTo(self, access_object):
-        if access_object is self:
-            return False
-
-        if type(access_object) is ModuleObject:  # L'objet peut contenir des references aux fonctions de l'objet module
-            for fname in access_object.functions:
-                rx = re.compile(r"(^|\W)({})($|\W)".format(fname))
-                if rx.search(self.sourcecode):
-                    return True
-
-        rx = access_object.search_me_regex()
-        return rx.search(self.sourcecode)
-
 class TableObject(AccessObject):
     type_ = "Table"
     _valid_file_exts = (".xml", ".lnkd")
@@ -121,45 +95,31 @@ class RelationObject(AccessObject):
     type_ = "Relation"
     _valid_file_exts = (".txt")
 
-REFS_ONLY = 1
-DEPS_ONLY = 2
-DEPS_AND_REFS = 3
+class Mention():
+    def __init__(self, line, objname, quote):
+        self.line = line
+        self.objname = objname
+        self.quote = quote
 
 class Analyse():
     objects = []
     duplicated_names = []
 
-    @staticmethod
-    def report(current, total, msg=""):
-        pass
-
-    @staticmethod
-    def ended():
-        pass
-
     @classmethod
-    def register(cls, obj):
-        if obj.name_ in [other.name_ for other in cls.objects] and not obj.name_ in cls.duplicated_names:
-            cls.duplicated_names.append(obj.name_)
-        cls.objects.append(obj)
+    def report(cls, current, total, msg=""):
+        pass
 
     @classmethod
-    def register_dep(cls, subject, target, mode=DEPS_AND_REFS):
-        if mode in (DEPS_AND_REFS, DEPS_ONLY):
-            subject.add_dep(target)
-        if mode in (DEPS_AND_REFS, REFS_ONLY):
-            target.add_ref(subject)
+    def ended(cls):
+        pass
 
     @classmethod
-    def run(cls, source_dir, mode=DEPS_AND_REFS):
+    def load_objects(cls, source_dir):
         source_dir = Path(source_dir)
 
         cls.objects = []
         cls.duplicated_names = []
 
-        # Liste les objets à partir de l'arborescence du repertoire des sources
-        cls.report(0, 100, "Analyse du répertoire")
-
         sourcemap = {
                     "forms": FormObject,
                     "reports": ReportObject,
@@ -174,31 +134,59 @@ class Analyse():
             for file in Path(source_dir / dirname).files():
                 try:
                     obj = accobj.from_file(file)
-                    cls.register(obj)
+                    if obj.name_ in [other.name_ for other in cls.objects] and not obj.name_ in cls.duplicated_names:
+                        cls.duplicated_names.append(obj.name_)
+                    cls.objects.append(obj)
                 except InvalidFileExt:
                     print("Ignored unrecognized file: {}".format(file))
 
-        total = len(cls.objects)
-        cls.report(0, total, "> {} objets trouvés".format(total))
+    @classmethod
+    def find_deps(cls, subject):
+        for candidate in cls.objects:
+            if candidate is subject:
+                continue
+
+            mentions = []
+
+            if type(candidate) is ModuleObject:  # L'objet peut contenir des references aux fonctions de l'objet module
+                for fname in candidate.functions:
+                    rx = re.compile(r"((?:.*\n)*)(.*(?:^|\W)({})(?:$|\W).*)".format(fname), MULTILINE)
+                    for match in rx.finditer(subject.sourcecode):
+                        line = len(match.group(1).split("\n")) + 2 if match.group(1) is not None else 1
+                        quote = match.group(2)
+                        objname = match.group(3)
+                        mentions.append(Mention(line, objname, quote))
+
+            rx = re.compile("""((?:.*\n)*)(.*(?:^|\s|\[|\]|&|\(|\)|\.|!|"|')({})(?:^|\s|\[|\]|&|\(|\)|\.|!|"|').*)""".format(candidate.name_), MULTILINE)
+            for match in rx.finditer(subject.sourcecode):
+                line = len(match.group(1).split("\n")) if match.group(1) is not None else 1
+                quote = match.group(2)
+                objname = match.group(3)
+                mentions.append(Mention(line, objname, quote))
+
+            if mentions:
+                subject.deps[candidate] = mentions
 
+    @classmethod
+    def analyse_all(cls):
         # Mise à jour des dépendances:
         # # parcourt les objets, et recherche dans le code source de chacun des mentions du nom des autres objets.
-        # # Si l'objet dont on recherche le nom fait partie des noms dupliqués, essaie de deviner son type.
-
+        total = len(cls.objects)
         for index, subject in enumerate(cls.objects):
-
             cls.report(index, total, "* {}: {}".format(subject.type_, subject.name_))
+            cls.find_deps(subject)
 
-            for candidate in cls.objects[:index] + cls.objects[index + 1:]:
-
-                if subject.containsRefsTo(candidate):
+    @classmethod
+    def run(cls, source_dir):
+        # Liste les objets à partir de l'arborescence du repertoire des sources
+        cls.report(0, 100, "Analyse du répertoire")
+        cls.load_objects(source_dir)
 
-                    if candidate.name_ in cls.duplicated_names:
-                        pass
+        cls.report(0, 100, "> {} objets trouvés".format(len(cls.objects)))
 
-                    cls.register_dep(subject, candidate, mode)
+        cls.analyse_all()
 
-        cls.report(total, total, "Analyse terminée")
+        cls.report(100, 100, "Analyse terminée")
         cls.ended()
 
         return cls.objects
@@ -206,24 +194,29 @@ class Analyse():
 
 
 if __name__ == "__main__":
-    source_dir = Path(r"c:\dev\access\Analytique\source")
 
     here = Path(__file__).parent.abspath()
-    source_dir = here / "source"
-    datafile = here / "access_analyser.txt"
-    datafile.remove_p()
+    source_dir = here / r"test\source"
+    resultfile = here / r"test\analyse.txt"
+    resultfile.remove_p()
 
-    def main_report(*_, msg=""):
-        print(msg)
-    Analyse.report = main_report
+    def print_(i, total, msg):
+        print("({}/{}) {}".format(i, total, msg))
+
+    Analyse.report = print_
 
     Analyse.run(source_dir)
 
-    with open("data.txt", "w+") as f:
-        for o in Analyse.objects:
-            msg = "# {}: '{}'".format(o.type_, o.name_)
-            msg += "\n\t Utilise: {}".format(", ".join(["'{}'".format(d) for d in o.deps]))
-            msg += "\n\t Est utilisé par: {}".format(", ".join(["'{}'".format(d) for d in o.refs]))
+    with open(resultfile, "w+") as f:
+        for obj in Analyse.objects:
+            msg = "# L'objet [{}] '{}' mentionne dans son code-source:".format(obj.type_, obj.name_)
+            for dep, mentions in obj.deps.items():
+                msg += "\n\t* [{}] '{}'".format(dep.type_, dep.name_)
+                for mention in mentions:
+                    msg += "\n\t\tLine: {} >> {}".format(mention.line, mention.quote)
+            if not obj.deps:
+                msg += "\n\t (pas de dépendances)"
+
             msg += "\n"
             f.write(msg)