22import contextlib
33import datetime
44import importlib
5+ import importlib .util
56import itertools
67import keyword
78import logging
@@ -185,7 +186,6 @@ async def _load(self, pkg_names: Iterable[str]) -> Dict[str, Union[List[str], Di
185186
186187 async for spec , name in AsyncIter (pkg_specs , steps = 10 ):
187188 try :
188- self ._cleanup_and_refresh_modules (spec .name )
189189 await bot .load_extension (spec )
190190 except errors .PackageAlreadyLoaded :
191191 alreadyloaded_packages .append (name )
@@ -251,7 +251,27 @@ def maybe_reload(new_name):
251251 except KeyError :
252252 pass
253253 else :
254- importlib ._bootstrap ._exec (lib .__spec__ , lib )
254+ # Create a new spec from the file to get updated source code
255+ if hasattr (lib .__spec__ , 'origin' ) and lib .__spec__ .origin :
256+ try :
257+ # Create fresh spec and reload source
258+ new_spec = importlib .util .spec_from_file_location (
259+ lib .__spec__ .name , lib .__spec__ .origin
260+ )
261+ if new_spec and new_spec .loader :
262+ # Update the module's spec to the fresh one
263+ lib .__spec__ = new_spec
264+ # Execute with the fresh spec to load updated source
265+ new_spec .loader .exec_module (lib )
266+ else :
267+ # Fallback to original method if spec creation fails
268+ importlib ._bootstrap ._exec (lib .__spec__ , lib )
269+ except Exception :
270+ # Fallback to original method if anything fails
271+ importlib ._bootstrap ._exec (lib .__spec__ , lib )
272+ else :
273+ # Fallback to original method for non-file modules
274+ importlib ._bootstrap ._exec (lib .__spec__ , lib )
255275
256276 # noinspection PyTypeChecker
257277 modules = itertools .accumulate (splitted , "{}.{}" .format )
@@ -264,7 +284,7 @@ def maybe_reload(new_name):
264284 if name == module_name or name .startswith (f"{ module_name } ." )
265285 }
266286 for child_name , lib in children .items ():
267- importlib . _bootstrap . _exec ( lib . __spec__ , lib )
287+ maybe_reload ( child_name )
268288
269289 async def _unload (self , pkg_names : Iterable [str ]) -> Dict [str , List [str ]]:
270290 """
@@ -290,7 +310,35 @@ async def _unload(self, pkg_names: Iterable[str]) -> Dict[str, List[str]]:
290310
291311 for name in pkg_names :
292312 if name in bot .extensions :
313+ # Find the extension module and clear its .pyc cache before unloading
314+ if name in sys .modules :
315+ module = sys .modules [name ]
316+ if hasattr (module , '__file__' ) and module .__file__ :
317+ # Clear .pyc cache by removing __pycache__ directory
318+ import os
319+ import shutil
320+ pycache_dir = os .path .join (os .path .dirname (module .__file__ ), '__pycache__' )
321+ if os .path .exists (pycache_dir ):
322+ try :
323+ shutil .rmtree (pycache_dir )
324+ except (OSError , IOError ):
325+ # Ignore errors removing cache directory
326+ pass
327+
293328 await bot .unload_extension (name )
329+
330+ # Manually remove related modules from sys.modules to force fresh reload
331+ modules_to_remove = []
332+ for module_name in sys .modules :
333+ if module_name == name or module_name .startswith (f"{ name } ." ):
334+ modules_to_remove .append (module_name )
335+
336+ for module_name in modules_to_remove :
337+ del sys .modules [module_name ]
338+
339+ # Clear import caches to ensure fresh loading
340+ importlib .invalidate_caches ()
341+
294342 await bot .remove_loaded_package (name )
295343 unloaded_packages .append (name )
296344 else :
@@ -5767,6 +5815,7 @@ async def rpc_load(self, request):
57675815 if spec is None :
57685816 raise LookupError ("No such cog found." )
57695817
5818+ print (f"DEBUG: RPC loading cog { cog_name } , spec name: { spec .name } " )
57705819 self ._cleanup_and_refresh_modules (spec .name )
57715820
57725821 await self .bot .load_extension (spec )
0 commit comments