|
|
@@ -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)
|
|
|
|