core.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. '''
  2. @author: olivier.massot
  3. '''
  4. import re
  5. from path import Path
  6. objects = []
  7. # TODO: gérer les cas où des objets de types différents portent le même nom. Ex: une table et un formulaire...
  8. # TODO: ignorer les commentaires dans le modules
  9. # TODO: ignorer les labels dans les formulaires et états
  10. # NB: une requete et une table ne peuvent pas porter le même nom.
  11. def recurse(acc_obj):
  12. deptree = []
  13. for dep in acc_obj.deps:
  14. deptree.append(dep)
  15. if dep.deps:
  16. deptree += recurse(dep)
  17. return deptree
  18. class InvalidFileExt(IOError):
  19. pass
  20. class AccessObject():
  21. type_ = "<unknown>"
  22. _valid_file_exts = (".bas")
  23. def __init__(self, nom):
  24. self.nom = nom
  25. self.functions = []
  26. self.sourcefile = ""
  27. self.deps = []
  28. self.refs = []
  29. self._sourcecode = ""
  30. def __repr__(self):
  31. return "<{}: {}>".format(self.type_, self.nom)
  32. @classmethod
  33. def from_file(cls, file):
  34. file = Path(file)
  35. if file.ext not in cls._valid_file_exts:
  36. raise InvalidFileExt("Format de fichier d'entée non valide ({})".format(file.name))
  37. obj = cls(AccessObject.path_to_name(file))
  38. obj.sourcefile = file
  39. obj._sourcecode = file.text()
  40. return obj
  41. @property
  42. def sourcecode(self):
  43. if not self._sourcecode:
  44. self._sourcecode = self.sourcefile.text()
  45. return self._sourcecode
  46. def add_dep(self, obj):
  47. if not obj in self.deps:
  48. self.deps.append(obj)
  49. def add_ref(self, obj):
  50. if not obj in self.refs:
  51. self.refs.append(obj)
  52. SUBSTR = {92: "\\", 47: "/", 58: ":", 42: "*", 63:"?", 34:"\"", 60:"<", 62:">", 124:"|" }
  53. @staticmethod
  54. def path_to_name(path):
  55. name_ = path.name.stripext()
  56. for ascii_code, char in AccessObject.SUBSTR.items():
  57. name_ = name_.replace("[{}]".format(ascii_code), char)
  58. return name_
  59. class TableObject(AccessObject):
  60. type_ = "Table"
  61. _valid_file_exts = (".xml", ".lnkd")
  62. class QueryObject(AccessObject):
  63. type_ = "Query"
  64. class FormObject(AccessObject):
  65. type_ = "Form"
  66. class ReportObject(AccessObject):
  67. type_ = "Report"
  68. class MacroObject(AccessObject):
  69. type_ = "Macro"
  70. class ModuleObject(AccessObject):
  71. type_ = "Module"
  72. @classmethod
  73. def from_file(cls, file):
  74. obj = super(ModuleObject, cls).from_file(file)
  75. rx = re.compile(r"Sub|Function ([^(]+)\(")
  76. obj.functions = [fname for fname in rx.findall(file.text()) if fname]
  77. return obj
  78. class RelationObject(AccessObject):
  79. type_ = "Relation"
  80. _valid_file_exts = (".txt")
  81. REFS_ONLY = 1
  82. DEPS_ONLY = 2
  83. DEPS_AND_REFS = 3
  84. class Analyse():
  85. objects = []
  86. duplicated_names = []
  87. @staticmethod
  88. def report(current, total, msg=""):
  89. pass
  90. @staticmethod
  91. def ended():
  92. pass
  93. @classmethod
  94. def register(cls, obj):
  95. if obj.nom in [other.nom for other in cls.objects] and not obj.nom in cls.duplicated_names:
  96. cls.duplicated_names.append(obj.nom)
  97. cls.objects.append(obj)
  98. @staticmethod
  99. def containsRefsTo(source, target):
  100. if type(target) is ModuleObject: # L'objet peut contenir des references aux fonctions de l'objet module
  101. for fname in target.functions:
  102. rx = re.compile(r"(?:^|\W)({})(?:$|\W)".format(fname))
  103. if rx.search(source.sourcecode):
  104. return True
  105. else:
  106. rx = re.compile(r"(?:^|\W)({})(?:$|\W)".format(target.nom))
  107. return rx.search(source.sourcecode)
  108. @classmethod
  109. def run(cls, source_dir, mode=DEPS_AND_REFS):
  110. source_dir = Path(source_dir)
  111. cls.objects = []
  112. cls.duplicated_names = []
  113. # Liste les objets à partir de l'arborescence du repertoire des sources
  114. cls.report(0, 100, "Analyse du répertoire")
  115. sourcemap = {
  116. "forms": FormObject,
  117. "reports": ReportObject,
  118. "relations": RelationObject,
  119. "scripts": MacroObject,
  120. "queries": QueryObject,
  121. "tables": TableObject,
  122. "modules": ModuleObject,
  123. }
  124. for dirname, accobj in sourcemap.items():
  125. for file in Path(source_dir / dirname).files():
  126. try:
  127. obj = accobj.from_file(file)
  128. cls.register(obj)
  129. except InvalidFileExt:
  130. print("Ignored unrecognized file: {}".format(file))
  131. total = len(cls.objects)
  132. cls.report(0, total, "> {} objets trouvés".format(total))
  133. # met à jour les dependances en listant les noms d'objets mentionnés dans le fichier subject
  134. for index, subject in enumerate(cls.objects):
  135. cls.report(index, total, "* {}: {}".format(subject.type_, subject.nom))
  136. for candidate in cls.objects[:index] + cls.objects[index + 1:]:
  137. if Analyse.containsRefsTo(subject, candidate):
  138. if mode in (DEPS_AND_REFS, DEPS_ONLY):
  139. subject.add_dep(candidate)
  140. if mode in (DEPS_AND_REFS, REFS_ONLY):
  141. candidate.add_ref(subject)
  142. #
  143. # for fname in candidate.functions:
  144. # if Analyse.lookFor(fname, source):
  145. # if mode in (DEPS_AND_REFS, DEPS_ONLY):
  146. # subject.add_dep(candidate)
  147. # if mode in (DEPS_AND_REFS, REFS_ONLY):
  148. # candidate.add_ref(subject)
  149. # break
  150. cls.report(total, total, "Analyse terminée")
  151. cls.ended()
  152. return cls.objects
  153. if __name__ == "__main__":
  154. source_dir = Path(r"c:\dev\access\Analytique\source")
  155. here = Path(__file__).parent.abspath()
  156. datafile = here / "access_data.txt"
  157. datafile.remove_p()
  158. def main_report(*_, msg=""):
  159. print(msg)
  160. Analyse.report = main_report
  161. Analyse.run(source_dir)
  162. with open("data.txt", "w+") as f:
  163. for o in Analyse.objects:
  164. f.write("* {} - {}{}\n\tdeps > {}\n".format(o.type_,
  165. o.nom,
  166. "\n\tfunctions > ".format(", ".join(o.functions)) if o.functions else "",
  167. o.deps))
  168. print("# Terminé")