88from collections import defaultdict
99from operator import attrgetter
1010from itertools import chain
11+ from pathlib import Path
1112
1213import sqlalchemy
1314
@@ -119,7 +120,10 @@ def load_all(self, plugin_dir):
119120
120121 :type plugin_dir: str
121122 """
122- path_list = glob .iglob (os .path .join (plugin_dir , '*.py' ))
123+ plugin_dir = Path (plugin_dir )
124+ # Load all .py files in the plugins directory and any subdirectory
125+ # But ignore files starting with _
126+ path_list = plugin_dir .rglob ("[!_]*.py" )
123127 # Load plugins asynchronously :O
124128 yield from asyncio .gather (* [self .load_plugin (path ) for path in path_list ], loop = self .bot .loop )
125129
@@ -136,27 +140,30 @@ def load_plugin(self, path):
136140
137141 Won't load any plugins listed in "disabled_plugins".
138142
139- :type path: str
143+ :type path: str | Path
140144 """
141145
142- file_path = os .path .abspath (path )
143- file_name = os .path .basename (path )
144- title = os .path .splitext (file_name )[0 ]
146+ path = Path (path )
147+ file_path = path .resolve ()
148+ file_name = file_path .name
149+ # Resolve the path relative to the current directory
150+ plugin_path = file_path .relative_to (self .bot .base_dir )
151+ title = '.' .join (plugin_path .parts [1 :]).rsplit ('.' , 1 )[0 ]
145152
146153 if "plugin_loading" in self .bot .config :
147154 pl = self .bot .config .get ("plugin_loading" )
148155
149156 if pl .get ("use_whitelist" , False ):
150157 if title not in pl .get ("whitelist" , []):
151- logger .info ('Not loading plugin module "{}": plugin not whitelisted' .format (file_name ))
158+ logger .info ('Not loading plugin module "{}": plugin not whitelisted' .format (title ))
152159 return
153160 else :
154161 if title in pl .get ("blacklist" , []):
155- logger .info ('Not loading plugin module "{}": plugin blacklisted' .format (file_name ))
162+ logger .info ('Not loading plugin module "{}": plugin blacklisted' .format (title ))
156163 return
157164
158165 # make sure to unload the previously loaded plugin from this path, if it was loaded.
159- if file_name in self .plugins :
166+ if file_path in self .plugins :
160167 yield from self .unload_plugin (file_path )
161168
162169 module_name = "plugins.{}" .format (title )
@@ -166,11 +173,11 @@ def load_plugin(self, path):
166173 if hasattr (plugin_module , "_cloudbot_loaded" ):
167174 importlib .reload (plugin_module )
168175 except Exception :
169- logger .exception ("Error loading {}:" .format (file_name ))
176+ logger .exception ("Error loading {}:" .format (title ))
170177 return
171178
172179 # create the plugin
173- plugin = Plugin (file_path , file_name , title , plugin_module )
180+ plugin = Plugin (str ( file_path ) , file_name , title , plugin_module )
174181
175182 # proceed to register hooks
176183
@@ -187,7 +194,7 @@ def load_plugin(self, path):
187194 plugin .unregister_tables (self .bot )
188195 return
189196
190- self .plugins [plugin .file_name ] = plugin
197+ self .plugins [plugin .file_path ] = plugin
191198
192199 for on_cap_available_hook in plugin .on_cap_available :
193200 for cap in on_cap_available_hook .caps :
@@ -275,21 +282,26 @@ def unload_plugin(self, path):
275282
276283 Returns True if the plugin was unloaded, False if the plugin wasn't loaded in the first place.
277284
278- :type path: str
285+ :type path: str | Path
279286 :rtype: bool
280287 """
281- file_name = os .path .basename (path )
282- title = os .path .splitext (file_name )[0 ]
288+ path = Path (path )
289+ file_path = path .resolve ()
290+ plugin_path = file_path .relative_to (self .bot .base_dir )
291+
292+ title = '.' .join (plugin_path .parts [1 :]).rsplit ('.' , 1 )[0 ]
283293 if "disabled_plugins" in self .bot .config and title in self .bot .config ['disabled_plugins' ]:
284294 # this plugin hasn't been loaded, so no need to unload it
285295 return False
286296
297+ module_name = "plugins.{}" .format (title )
298+
287299 # make sure this plugin is actually loaded
288- if not file_name in self .plugins :
300+ if str ( file_path ) not in self .plugins :
289301 return False
290302
291303 # get the loaded plugin
292- plugin = self .plugins [file_name ]
304+ plugin = self .plugins [str ( file_path ) ]
293305
294306 for task in plugin .tasks :
295307 task .cancel ()
@@ -358,10 +370,10 @@ def unload_plugin(self, path):
358370 plugin .unregister_tables (self .bot )
359371
360372 # remove last reference to plugin
361- del self .plugins [plugin .file_name ]
373+ del self .plugins [plugin .file_path ]
362374
363375 if self .bot .config .get ("logging" , {}).get ("show_plugin_loading" , True ):
364- logger .info ("Unloaded all plugins from {}.py " .format (plugin .title ))
376+ logger .info ("Unloaded all plugins from {}" .format (plugin .title ))
365377
366378 return True
367379
0 commit comments