files.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. """
  2. Функции для работы с файлами.
  3. класс для хранения файлов плагинов.
  4. Система импорта плагинов.
  5. Author: Milinuri Nirvalen
  6. """
  7. from .ui import Logger, Ui
  8. from .plugins import Plugin
  9. import toml
  10. import os
  11. import importlib.util
  12. import re
  13. class Config:
  14. """управление конфигурационными файлами *.toml
  15. :optional param name: имя (ключ словаря файла)
  16. :optional param model: вид конфигурационного файла по умолчанию
  17. :optional param filepath: путь до конфигурационного файла"""
  18. def __init__(self, name=None, model=None, filepath=None):
  19. super(Config, self).__init__()
  20. self.name = name
  21. self.model = model
  22. self.filepath = filepath or 'config.toml'
  23. self.file_data = {}
  24. self.group_data = {} or self.model
  25. self.data = self.group_data or self.model or self.file_data
  26. self.l = Logger(f'config: {self.name or self.filepath}')
  27. self.u = Ui(f'config: {self.name or self.filepath}')
  28. self.lock = False
  29. if not self.name and not self.filepath and self.model:
  30. self.lock = True
  31. self.l.log('заблокировано', 'w')
  32. if not self.lock:
  33. self.load()
  34. def save(self):
  35. """сохранение данных конфигурационного файла"""
  36. if self.name:
  37. self.file_data[self.name] = self.group_data
  38. elif self.model and not self.file_data:
  39. self.file_data = self.model
  40. with open(self.filepath, 'w') as f:
  41. toml.dump(self.file_data, f)
  42. self.l.log('сохранено!', 'c')
  43. def load(self):
  44. """загрузка данных из toml файла"""
  45. if os.path.exists(self.filepath):
  46. self.file_data = toml.load(self.filepath)
  47. if self.name:
  48. if self.name in self.file_data:
  49. self.group_data = self.file_data[self.name]
  50. else:
  51. self.l.log('группа данных не найдена', 'e')
  52. self.save()
  53. elif self.model:
  54. self.l.log('файл не найден', 'e')
  55. self.save()
  56. def print_config(self, data={}):
  57. # выводит содержимое конфигурационного файла
  58. if not data:
  59. data = self.file_data
  60. for k, v in self.file_data.items():
  61. print()
  62. self.u.print_list(v, k)
  63. def fcheck(path, create=False):
  64. '''проверяет существование папки
  65. :param path: путь до папки
  66. :param create: создать ли папку при её отцуцтвии'''
  67. l = Logger(f'check folder [{path}]')
  68. if os.path.exists(path):
  69. return True
  70. if create:
  71. os.mkdir(path)
  72. l.log(f'создание директории', 'c')
  73. else:
  74. l.log(f'дирректория не найдена', 'w')
  75. # ########################
  76. # import system
  77. # была скопирована из... забыл, честно
  78. # врочем от неё тут мало чего осталось родного
  79. # ########################
  80. def import_module(name, path):
  81. """Import module from specified path with specified name.
  82. :param name: module's name
  83. :param path: path to module's file
  84. :returns: imported module"""
  85. spec = importlib.util.spec_from_file_location(name, os.path.abspath(path))
  86. module = importlib.util.module_from_spec(spec)
  87. spec.loader.exec_module(module)
  88. return module
  89. def load_plugins_from_file(app, path):
  90. """Load plugins from specified path. Plugins can be in module-level
  91. variable "plugin" and in module-level variable "plugins" (with list of
  92. plugins).
  93. :param app: Chio instance
  94. :param path: path to file with module
  95. :returns: loaded module or None if no plugin found
  96. """
  97. if app.secure_call:
  98. try:
  99. mod = import_module(path, path)
  100. except Exception as e:
  101. app.l.log(e, 'e')
  102. return True
  103. else:
  104. mod = import_module(path, path)
  105. if mod:
  106. for pl in [getattr(mod, "p", None),
  107. getattr(mod, "plugin", None),
  108. *getattr(mod, "plugins", ())]:
  109. # пропускаем если pl не является экземляром плашига
  110. if not isinstance(pl, Plugin):
  111. continue
  112. name = path.split('/')[-1]
  113. pid = path.split('/')[-2]
  114. l = Logger(f'{pid}:{name.strip(".py")}')
  115. # пропускаем, если это указано в настройках
  116. if pid in app.ignore_pid:
  117. l.warn('disabled')
  118. continue
  119. # пропускаем если у плагина есть атрибут disabled
  120. if pl.__dict__.get('disabled'):
  121. l.warn('disabled')
  122. continue
  123. # получаем список движков
  124. target_backend = pl.__dict__.get('backend', '*')
  125. backend = app.backend.backend_type
  126. if target_backend == '*' or target_backend in backend:
  127. # задаём аргументы имени и пути к плагину
  128. sp = pl.__dict__.get("sp", [])
  129. pl.filename = name.split('.')[0]
  130. pl.path = path.split(name)[0]
  131. pl.pid = pid
  132. # добавляем плагин к общему списку плагинов
  133. # добавляем счётчик команд
  134. app.plugins.append(pl)
  135. app.commands += pl.commands
  136. # добавляем обрвботчику к приложению
  137. for k, v in pl.handlers.items():
  138. for h in v:
  139. app.add_event_handler(h, k)
  140. for k, v in pl.check_funcs.items():
  141. if k in app.check_funcs:
  142. l.warn(f'ChF {l.red}[{k}]{l.yellow} будет перезаписана')
  143. else:
  144. l.complite(f'ChF {l.cyan}[{k}]{l.green} зарегистрирована')
  145. app.check_funcs[k] = v
  146. if sp:
  147. for p in sp:
  148. if p in app.sp:
  149. l.log(f'доп. префикс: "{p}" будет дополнен')
  150. else:
  151. app.sp[p] = {}
  152. for k, v in pl.routers.items():
  153. if k == '_':
  154. l.warn(f'[{l.lred}{path}{l.yellow}]: не рекомендуется использовать "_" как имя функции')
  155. for x in v["cmds"]:
  156. n = p+'.'+x
  157. if n in app.triggers:
  158. l.warn(f'имя: "{n}" уже используется [{l.lred}{app.triggers[n]}{l.yellow}] и будет перезаписанo')
  159. app.triggers[n] = f"{path}: {v['id'].split('/')[1]}"
  160. app.sp[p][k] = v
  161. else:
  162. for k, v in pl.routers.items():
  163. if k == '_':
  164. l.warn(f'[{l.lred}{path}{l.yellow}]: не рекомендуется использовать "_" как имя функции')
  165. for n in v["cmds"]:
  166. if n in app.triggers:
  167. l.warn(f'имя: "{n}" уже используется [{l.lred}{app.triggers[n]}{l.yellow}] и будет перезаписано')
  168. app.triggers[n] = f"{path}:{v['id'].split('/')[1]} {v['cmds']}"
  169. app.routers[k] = v
  170. l.complite(f'loaded')
  171. else:
  172. l.log(f'несовместимый движок: {", ".join(backend)}')
  173. def count_files(app, folder, count=0):
  174. """рекурсивная функция счёта файлов
  175. :param app: экземляр чио
  176. :param folder: папка для поиска
  177. :optional param count: кол-во найденных файлов
  178. :return: кол-во найденных файлов"""
  179. for name in os.listdir(folder):
  180. path = os.path.join(folder, name)
  181. if os.path.isdir(path) and not name.startswith('_') and path not in app.ignore_path:
  182. count = count_files(app, path, count)
  183. elif re.match(r"^[^_].*\.py$", name) and name not in app.ignore_files:
  184. count += 1
  185. return count
  186. def load_plugins(app, folder='', main_folder=True, count=0, files=0):
  187. """Рекурсивно загружает все пакеты.
  188. :optional param folder: путь до требуемой папки
  189. :optional param main_foler: является ли папка точкой входа
  190. :optianal param count: кол-во загруженных плагинов
  191. :optional param files: кол-во модулей в папке
  192. :returns: number of loaded plugins"""
  193. u = Ui()
  194. if main_folder:
  195. folder = 'plugins/' + folder
  196. fcheck(folder, True)
  197. files = count_files(app, folder)
  198. app.l.log(f'загрузка {files} плагинов из: {folder}', "i")
  199. for name in os.listdir(folder):
  200. path = os.path.join(folder, name)
  201. if os.path.isdir(path) and not name.startswith('_') and path not in app.ignore_path:
  202. count = load_plugins(app, path, False, count, files)
  203. elif re.match(r"^[^_].*\.py$", name) and name not in app.ignore_files:
  204. count += 1
  205. load_plugins_from_file(app, path)
  206. if app.__dict__.get('progress_bar'):
  207. u.progress_bar(count, files, printEnd='\r')
  208. if count == files and main_folder:
  209. print()
  210. return count