objectinfo.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. import warnings
  2. from rope.base import exceptions, resourceobserver
  3. from rope.base.oi import objectdb, memorydb, transform
  4. class ObjectInfoManager(object):
  5. """Stores object information
  6. It uses an instance of `objectdb.ObjectDB` for storing
  7. information.
  8. """
  9. def __init__(self, project):
  10. self.project = project
  11. self.to_textual = transform.PyObjectToTextual(project)
  12. self.to_pyobject = transform.TextualToPyObject(project)
  13. self.doi_to_pyobject = transform.DOITextualToPyObject(project)
  14. self._init_objectdb()
  15. if project.prefs.get('validate_objectdb', False):
  16. self._init_validation()
  17. def _init_objectdb(self):
  18. dbtype = self.project.get_prefs().get('objectdb_type', None)
  19. persist = None
  20. if dbtype is not None:
  21. warnings.warn(
  22. '"objectdb_type" project config is deprecated;\n'
  23. 'Use "save_objectdb" instead in your project '
  24. 'config file.\n(".ropeproject/config.py" by default)\n',
  25. DeprecationWarning)
  26. if dbtype != 'memory' and self.project.ropefolder is not None:
  27. persist = True
  28. self.validation = TextualValidation(self.to_pyobject)
  29. db = memorydb.MemoryDB(self.project, persist=persist)
  30. self.objectdb = objectdb.ObjectDB(db, self.validation)
  31. def _init_validation(self):
  32. self.objectdb.validate_files()
  33. observer = resourceobserver.ResourceObserver(
  34. changed=self._resource_changed, moved=self._resource_moved,
  35. removed=self._resource_moved)
  36. files = []
  37. for path in self.objectdb.get_files():
  38. resource = self.to_pyobject.path_to_resource(path)
  39. if resource is not None and resource.project == self.project:
  40. files.append(resource)
  41. self.observer = resourceobserver.FilteredResourceObserver(observer,
  42. files)
  43. self.objectdb.add_file_list_observer(_FileListObserver(self))
  44. self.project.add_observer(self.observer)
  45. def _resource_changed(self, resource):
  46. try:
  47. self.objectdb.validate_file(
  48. self.to_textual.resource_to_path(resource))
  49. except exceptions.ModuleSyntaxError:
  50. pass
  51. def _resource_moved(self, resource, new_resource=None):
  52. self.observer.remove_resource(resource)
  53. if new_resource is not None:
  54. old = self.to_textual.resource_to_path(resource)
  55. new = self.to_textual.resource_to_path(new_resource)
  56. self.objectdb.file_moved(old, new)
  57. self.observer.add_resource(new_resource)
  58. def get_returned(self, pyobject, args):
  59. result = self.get_exact_returned(pyobject, args)
  60. if result is not None:
  61. return result
  62. path, key = self._get_scope(pyobject)
  63. if path is None:
  64. return None
  65. for call_info in self.objectdb.get_callinfos(path, key):
  66. returned = call_info.get_returned()
  67. if returned and returned[0] not in ('unknown', 'none'):
  68. result = returned
  69. break
  70. if result is None:
  71. result = returned
  72. if result is not None:
  73. return self.to_pyobject(result)
  74. def get_exact_returned(self, pyobject, args):
  75. path, key = self._get_scope(pyobject)
  76. if path is not None:
  77. returned = self.objectdb.get_returned(
  78. path, key, self._args_to_textual(pyobject, args))
  79. if returned is not None:
  80. return self.to_pyobject(returned)
  81. def _args_to_textual(self, pyfunction, args):
  82. parameters = list(pyfunction.get_param_names(special_args=False))
  83. arguments = args.get_arguments(parameters)[:len(parameters)]
  84. textual_args = tuple([self.to_textual(arg)
  85. for arg in arguments])
  86. return textual_args
  87. def get_parameter_objects(self, pyobject):
  88. path, key = self._get_scope(pyobject)
  89. if path is None:
  90. return None
  91. arg_count = len(pyobject.get_param_names(special_args=False))
  92. unknowns = arg_count
  93. parameters = [None] * arg_count
  94. for call_info in self.objectdb.get_callinfos(path, key):
  95. args = call_info.get_parameters()
  96. for index, arg in enumerate(args[:arg_count]):
  97. old = parameters[index]
  98. if self.validation.is_more_valid(arg, old):
  99. parameters[index] = arg
  100. if self.validation.is_value_valid(arg):
  101. unknowns -= 1
  102. if unknowns == 0:
  103. break
  104. if unknowns < arg_count:
  105. return [self.to_pyobject(parameter)
  106. for parameter in parameters]
  107. def get_passed_objects(self, pyfunction, parameter_index):
  108. path, key = self._get_scope(pyfunction)
  109. if path is None:
  110. return []
  111. result = []
  112. for call_info in self.objectdb.get_callinfos(path, key):
  113. args = call_info.get_parameters()
  114. if len(args) > parameter_index:
  115. parameter = self.to_pyobject(args[parameter_index])
  116. if parameter is not None:
  117. result.append(parameter)
  118. return result
  119. def doa_data_received(self, data):
  120. def doi_to_normal(textual):
  121. pyobject = self.doi_to_pyobject(textual)
  122. return self.to_textual(pyobject)
  123. function = doi_to_normal(data[0])
  124. args = tuple([doi_to_normal(textual) for textual in data[1]])
  125. returned = doi_to_normal(data[2])
  126. if function[0] == 'defined' and len(function) == 3:
  127. self._save_data(function, args, returned)
  128. def function_called(self, pyfunction, params, returned=None):
  129. function_text = self.to_textual(pyfunction)
  130. params_text = tuple([self.to_textual(param)
  131. for param in params])
  132. returned_text = ('unknown',)
  133. if returned is not None:
  134. returned_text = self.to_textual(returned)
  135. self._save_data(function_text, params_text, returned_text)
  136. def save_per_name(self, scope, name, data):
  137. path, key = self._get_scope(scope.pyobject)
  138. if path is not None:
  139. self.objectdb.add_pername(path, key, name, self.to_textual(data))
  140. def get_per_name(self, scope, name):
  141. path, key = self._get_scope(scope.pyobject)
  142. if path is not None:
  143. result = self.objectdb.get_pername(path, key, name)
  144. if result is not None:
  145. return self.to_pyobject(result)
  146. def _save_data(self, function, args, returned=('unknown',)):
  147. self.objectdb.add_callinfo(function[1], function[2], args, returned)
  148. def _get_scope(self, pyobject):
  149. resource = pyobject.get_module().get_resource()
  150. if resource is None:
  151. return None, None
  152. textual = self.to_textual(pyobject)
  153. if textual[0] == 'defined':
  154. path = textual[1]
  155. if len(textual) == 3:
  156. key = textual[2]
  157. else:
  158. key = ''
  159. return path, key
  160. return None, None
  161. def sync(self):
  162. self.objectdb.sync()
  163. def __str__(self):
  164. return str(self.objectdb)
  165. class TextualValidation(object):
  166. def __init__(self, to_pyobject):
  167. self.to_pyobject = to_pyobject
  168. def is_value_valid(self, value):
  169. # ???: Should none and unknown be considered valid?
  170. if value is None or value[0] in ('none', 'unknown'):
  171. return False
  172. return self.to_pyobject(value) is not None
  173. def is_more_valid(self, new, old):
  174. if old is None:
  175. return True
  176. return new[0] not in ('unknown', 'none')
  177. def is_file_valid(self, path):
  178. return self.to_pyobject.path_to_resource(path) is not None
  179. def is_scope_valid(self, path, key):
  180. if key == '':
  181. textual = ('defined', path)
  182. else:
  183. textual = ('defined', path, key)
  184. return self.to_pyobject(textual) is not None
  185. class _FileListObserver(object):
  186. def __init__(self, object_info):
  187. self.object_info = object_info
  188. self.observer = self.object_info.observer
  189. self.to_pyobject = self.object_info.to_pyobject
  190. def removed(self, path):
  191. resource = self.to_pyobject.path_to_resource(path)
  192. if resource is not None:
  193. self.observer.remove_resource(resource)
  194. def added(self, path):
  195. resource = self.to_pyobject.path_to_resource(path)
  196. if resource is not None:
  197. self.observer.add_resource(resource)