m1n1.utils.Reloadable: Only reload each module once

This avoids confusion with types changing ID when the same module is
repeatedly reloaded. Now we use the file mtime and only reload things
which have changed since last time, and dependent modules.

Signed-off-by: Hector Martin <marcan@marcan.st>
This commit is contained in:
Hector Martin 2021-06-21 15:43:37 +09:00
parent ba478f2de5
commit a714c74e1a

View file

@ -1,6 +1,6 @@
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
from enum import Enum from enum import Enum
import bisect, copy, heapq, importlib, sys, itertools import bisect, copy, heapq, importlib, sys, itertools, time, os
from construct import Adapter, Int64ul, Int32ul, Int16ul, Int8ul from construct import Adapter, Int64ul, Int32ul, Int16ul, Int8ul
__all__ = [] __all__ = []
@ -53,24 +53,40 @@ def chexdump32(s, st=0, abbreviate=True):
last = val last = val
skip = False skip = False
class Reloadable: class ReloadableMeta(type):
def __new__(cls, name, bases, dct):
m = super().__new__(cls, name, bases, dct)
m._load_time = time.time()
return m
class Reloadable(metaclass=ReloadableMeta):
@classmethod @classmethod
def _reloadcls(cls): def _reloadcls(cls):
mods = [] mods = []
for c in cls.mro(): for c in cls.mro():
mods.append(sys.modules[c.__module__]) mod = sys.modules[c.__module__]
cur_cls = getattr(mod, c.__name__)
mods.append((cur_cls, mod))
if c.__name__ == "Reloadable": if c.__name__ == "Reloadable":
break break
for mod in mods[::-1]: reloaded = set()
mod = importlib.reload(mod) newest = 0
for pcls, mod in mods[::-1]:
source = getattr(mod, "__file__", None)
if not source:
continue
newest = max(newest, os.stat(source).st_mtime, pcls._load_time)
if (reloaded or pcls._load_time < newest) and mod.__name__ not in reloaded:
mod = importlib.reload(mod)
reloaded.add(mod.__name__)
return getattr(mod, cls.__name__) return getattr(mods[0][1], cls.__name__)
def _reloadme(self): def _reloadme(self):
self.__class__ = self._reloadcls() self.__class__ = self._reloadcls()
class RegisterMeta(type): class RegisterMeta(ReloadableMeta):
def __new__(cls, name, bases, dct): def __new__(cls, name, bases, dct):
m = super().__new__(cls, name, bases, dct) m = super().__new__(cls, name, bases, dct)
@ -546,7 +562,7 @@ class NdRange:
iters = (i[j] for i, j in zip(self.ranges, item)) iters = (i[j] for i, j in zip(self.ranges, item))
return map(sum, itertools.product(*(([i] if isinstance(i, int) else i) for i in iters))) return map(sum, itertools.product(*(([i] if isinstance(i, int) else i) for i in iters)))
class RegMapMeta(type): class RegMapMeta(ReloadableMeta):
def __new__(cls, name, bases, dct): def __new__(cls, name, bases, dct):
m = super().__new__(cls, name, bases, dct) m = super().__new__(cls, name, bases, dct)
m._addrmap = {} m._addrmap = {}