Skip to content

Commit 9650a09

Browse files
committed
Primary Issue: When cogs were first loaded, Python compiled them to .pyc files in __pycache__ directories
Reload Problem: When modules were reloaded after source code changes, Python continued using the cached .pyc
1 parent 00e1544 commit 9650a09

1 file changed

Lines changed: 52 additions & 3 deletions

File tree

redbot/core/core_commands.py

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import contextlib
33
import datetime
44
import importlib
5+
import importlib.util
56
import itertools
67
import keyword
78
import 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

Comments
 (0)