Some internal code butchering again.
This commit is contained in:
parent
3cac448f6e
commit
03d96ad0ea
44
i3pystatus/core/imputil.py
Normal file
44
i3pystatus/core/imputil.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import inspect
|
||||||
|
import types
|
||||||
|
|
||||||
|
class ClassFinder:
|
||||||
|
"""Support class to find classes of specific bases in a module"""
|
||||||
|
|
||||||
|
def __init__(self, baseclass):
|
||||||
|
self.baseclass = baseclass
|
||||||
|
|
||||||
|
def predicate_factory(self, module):
|
||||||
|
def predicate(obj):
|
||||||
|
return (
|
||||||
|
inspect.isclass(obj) and
|
||||||
|
issubclass(obj, self.baseclass) and
|
||||||
|
obj.__module__ == module.__name__
|
||||||
|
)
|
||||||
|
return predicate
|
||||||
|
|
||||||
|
def search_module(self, module):
|
||||||
|
return list(zip(*inspect.getmembers(module, self.predicate_factory(module))))[1]
|
||||||
|
|
||||||
|
def get_class(self, module):
|
||||||
|
classes = self.search_module(module)
|
||||||
|
|
||||||
|
if len(classes) > 1:
|
||||||
|
# If there are multiple Module clases bundled in one module,
|
||||||
|
# well, we can't decide for the user.
|
||||||
|
raise ConfigAmbigiousClassesError(module.__name__, classes)
|
||||||
|
elif not classes:
|
||||||
|
raise ConfigInvalidModuleError(module.__name__)
|
||||||
|
|
||||||
|
return classes[0]
|
||||||
|
|
||||||
|
def get_module(self, module):
|
||||||
|
return getattr(__import__("i3pystatus.{module}".format(module=module), globals(), {}, []), module)
|
||||||
|
|
||||||
|
def instanciate_class_from_module(self, module, *args, **kwargs):
|
||||||
|
if isinstance(module, types.ModuleType):
|
||||||
|
return self.get_class(module)(*args, **kwargs)
|
||||||
|
elif isinstance(module, str):
|
||||||
|
return self.instanciate_class_from_module(self.get_module(module), *args, **kwargs)
|
||||||
|
elif args or kwargs:
|
||||||
|
raise ValueError("Additional arguments are invalid if 'module' is already an object")
|
||||||
|
return module
|
@ -1,7 +1,7 @@
|
|||||||
from threading import Thread
|
from threading import Thread
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from .util import SettingsBase
|
from .settings import SettingsBase
|
||||||
from .threads import AutomagicManager
|
from .threads import AutomagicManager
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
61
i3pystatus/core/settings.py
Normal file
61
i3pystatus/core/settings.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
from .util import KeyConstraintDict
|
||||||
|
from .exceptions import ConfigKeyError, ConfigMissingError
|
||||||
|
|
||||||
|
class SettingsBase:
|
||||||
|
"""
|
||||||
|
Support class for providing a nice and flexible settings interface
|
||||||
|
|
||||||
|
Classes inherit from this class and define what settings they provide and
|
||||||
|
which are required.
|
||||||
|
|
||||||
|
The constructor is either passed a dictionary containing these settings, or
|
||||||
|
keyword arguments specifying the same.
|
||||||
|
|
||||||
|
Settings are stored as attributes of self
|
||||||
|
"""
|
||||||
|
|
||||||
|
settings = tuple()
|
||||||
|
"""settings should be tuple containing two types of elements:
|
||||||
|
* bare strings, which must be valid identifiers.
|
||||||
|
* two-tuples, the first element being a identifier (as above) and the second
|
||||||
|
a docstring for the particular setting"""
|
||||||
|
|
||||||
|
required = tuple()
|
||||||
|
"""required can list settings which are required"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
def flatten_setting(setting):
|
||||||
|
return setting[0] if isinstance(setting, tuple) else setting
|
||||||
|
def flatten_settings(settings):
|
||||||
|
return tuple(flatten_setting(setting) for setting in settings)
|
||||||
|
|
||||||
|
def get_argument_dict(args, kwargs):
|
||||||
|
if len(args) == 1 and not kwargs:
|
||||||
|
# User can also pass in a dict for their settings
|
||||||
|
# Note: you could do that anyway, with the ** syntax
|
||||||
|
return args[0]
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
self.settings = flatten_settings(self.settings)
|
||||||
|
|
||||||
|
sm = KeyConstraintDict(self.settings, self.required)
|
||||||
|
settings_source = get_argument_dict(args, kwargs)
|
||||||
|
|
||||||
|
try:
|
||||||
|
sm.update(settings_source)
|
||||||
|
except KeyError as exc:
|
||||||
|
raise ConfigKeyError(type(self).__name__, key=exc.args[0]) from exc
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.__dict__.update(sm)
|
||||||
|
except KeyConstraintDict.MissingKeys as exc:
|
||||||
|
raise ConfigMissingError(type(self).__name__, missing=exc.keys) from exc
|
||||||
|
|
||||||
|
self.__name__ = "{}.{}".format(self.__module__, self.__class__.__name__)
|
||||||
|
|
||||||
|
self.init()
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
"""Convenience method which is called after all settings are set
|
||||||
|
|
||||||
|
In case you don't want to type that super()…blabla :-)"""
|
@ -1,9 +1,9 @@
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
import collections
|
|
||||||
|
from .util import partition
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from setproctitle import setproctitle
|
from setproctitle import setproctitle
|
||||||
@ -11,74 +11,76 @@ except ImportError:
|
|||||||
def setproctitle(title):
|
def setproctitle(title):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if hasattr(time, "perf_counter"):
|
timer = time.perf_counter if hasattr(time, "perf_counter") else time.clock
|
||||||
timer = time.perf_counter
|
|
||||||
else:
|
|
||||||
timer = time.clock
|
|
||||||
|
|
||||||
class ExceptionWrapper:
|
class Wrapper:
|
||||||
def __init__(self, workload):
|
def __init__(self, workload):
|
||||||
self.workload = workload
|
self.workload = workload
|
||||||
|
|
||||||
def __call__(self):
|
|
||||||
try:
|
|
||||||
self.workload()
|
|
||||||
except Exception as exc:
|
|
||||||
traceback.print_exception(*sys.exc_info(), file=sys.stderr)
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return repr(self.workload)
|
return repr(self.workload)
|
||||||
|
|
||||||
class WorkloadWrapper:
|
class ExceptionWrapper(Wrapper):
|
||||||
def __init__(self, workload):
|
def __call__(self):
|
||||||
self.workload = workload
|
try:
|
||||||
self.time = 0.0
|
self.workload()
|
||||||
|
except Exception as exc:
|
||||||
|
sys.stderr.write("Exception in {thread}".format(thread=threading.current_thread().name))
|
||||||
|
traceback.print_exception(*sys.exc_info(), file=sys.stderr)
|
||||||
|
sys.stderr.flush()
|
||||||
|
|
||||||
|
class WorkloadWrapper(Wrapper):
|
||||||
|
time = 0.0
|
||||||
|
|
||||||
def __call__(self):
|
def __call__(self):
|
||||||
tp1 = timer()
|
tp1 = timer()
|
||||||
self.workload()
|
self.workload()
|
||||||
self.time = timer() - tp1
|
self.time = timer() - tp1
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return repr(self.workload)
|
|
||||||
|
|
||||||
class Thread(threading.Thread):
|
class Thread(threading.Thread):
|
||||||
def __init__(self, target_interval, workloads=None, start_barrier=1):
|
def __init__(self, target_interval, workloads=None, start_barrier=1):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
self.workloads = workloads or []
|
||||||
self.workloads = workloads if workloads is not None else []
|
|
||||||
self.target_interval = target_interval
|
self.target_interval = target_interval
|
||||||
self.start_barrier = start_barrier
|
self.start_barrier = start_barrier
|
||||||
self.daemon = True
|
self.daemon = True
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return iter(self.workloads)
|
return iter(self.workloads)
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self.workloads)
|
return len(self.workloads)
|
||||||
|
|
||||||
def pop(self):
|
def pop(self):
|
||||||
return self.workloads.pop()
|
return self.workloads.pop()
|
||||||
|
|
||||||
def append(self, workload):
|
def append(self, workload):
|
||||||
self.workloads.append(workload)
|
self.workloads.append(workload)
|
||||||
|
|
||||||
def run(self):
|
|
||||||
while len(self) <= self.start_barrier:
|
|
||||||
time.sleep(0.3)
|
|
||||||
|
|
||||||
setproctitle("i3pystatus: {name}/{workloads}".format(name=self.name, workloads=list(map(repr, self.workloads))))
|
|
||||||
|
|
||||||
while self:
|
|
||||||
for workload in self:
|
|
||||||
workload()
|
|
||||||
self.workloads.sort(key=lambda workload: workload.time)
|
|
||||||
|
|
||||||
filltime = self.target_interval - self.time
|
|
||||||
if filltime > 0:
|
|
||||||
time.sleep(filltime)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def time(self):
|
def time(self):
|
||||||
return sum(map(lambda workload: workload.time, self))
|
return sum(map(lambda workload: workload.time, self))
|
||||||
|
|
||||||
|
def wait_for_start_barrier(self):
|
||||||
|
while len(self) <= self.start_barrier:
|
||||||
|
time.sleep(0.4)
|
||||||
|
|
||||||
|
def setproctitle(self):
|
||||||
|
setproctitle("i3pystatus {name}: {workloads}".format(name=self.name, workloads=list(map(repr, self.workloads))))
|
||||||
|
|
||||||
|
def execute_workloads(self):
|
||||||
|
for workload in self:
|
||||||
|
workload()
|
||||||
|
self.workloads.sort(key=lambda workload: workload.time)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.setproctitle()
|
||||||
|
while self:
|
||||||
|
self.execute_workloads()
|
||||||
|
filltime = self.target_interval - self.time
|
||||||
|
if filltime > 0:
|
||||||
|
time.sleep(filltime)
|
||||||
|
|
||||||
class AutomagicManager:
|
class AutomagicManager:
|
||||||
def __init__(self, target_interval):
|
def __init__(self, target_interval):
|
||||||
self.target_interval = target_interval
|
self.target_interval = target_interval
|
||||||
@ -91,33 +93,27 @@ class AutomagicManager:
|
|||||||
|
|
||||||
def __call__(self):
|
def __call__(self):
|
||||||
separate = []
|
separate = []
|
||||||
|
|
||||||
for thread in self.threads:
|
for thread in self.threads:
|
||||||
separate.extend(self.optimize(thread, thread.time))
|
separate.extend(self.branch(thread, thread.time))
|
||||||
|
|
||||||
self.create_threads(self.partition(separate))
|
self.create_threads(self.partition(separate))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "Manager"
|
||||||
|
|
||||||
def wrap(self, workload):
|
def wrap(self, workload):
|
||||||
return WorkloadWrapper(ExceptionWrapper(workload))
|
return WorkloadWrapper(ExceptionWrapper(workload))
|
||||||
|
|
||||||
def optimize(self, thread, vtime):
|
# def calculate_sparse_times():
|
||||||
|
# return ((self.lower_bound - thread.time, thread) for thread in self.threads)
|
||||||
|
|
||||||
|
def branch(self, thread, vtime):
|
||||||
if len(thread) > 1 and vtime > self.upper_bound:
|
if len(thread) > 1 and vtime > self.upper_bound:
|
||||||
remove = thread.pop()
|
remove = thread.pop()
|
||||||
return [remove] + self.optimize(thread, vtime - remove.time)
|
return [remove] + self.branch(thread, vtime - remove.time)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def partition(self, workloads):
|
def partition(self, workloads):
|
||||||
timesum = 0.0
|
return partition(workloads, self.lower_bound, lambda workload: workload.time)
|
||||||
new_threads = []
|
|
||||||
current_thread = []
|
|
||||||
for workload in workloads:
|
|
||||||
current_thread.append(workload)
|
|
||||||
timesum += workload.time
|
|
||||||
if timesum > self.lower_bound:
|
|
||||||
new_threads.append(current_thread)
|
|
||||||
current_thread = []
|
|
||||||
timesum = 0
|
|
||||||
return new_threads
|
|
||||||
|
|
||||||
def create_threads(self, threads):
|
def create_threads(self, threads):
|
||||||
for workloads in threads:
|
for workloads in threads:
|
||||||
|
@ -1,17 +1,31 @@
|
|||||||
|
|
||||||
import inspect
|
|
||||||
import types
|
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
from .exceptions import *
|
from .exceptions import *
|
||||||
|
from .imputil import ClassFinder
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"SettingsBase",
|
"ModuleList", "KeyConstraintDict", "PrefixedKeyDict",
|
||||||
"ClassFinder",
|
|
||||||
"ModuleList",
|
|
||||||
"KeyConstraintDict", "PrefixedKeyDict",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def popwhile(predicate, iterable):
|
||||||
|
while iterable:
|
||||||
|
item = iterable.pop()
|
||||||
|
if predicate(item):
|
||||||
|
yield item
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
def partition(iterable, limit, key=None):
|
||||||
|
key = key or (lambda x: x)
|
||||||
|
partitions = []
|
||||||
|
while iterable:
|
||||||
|
sum = 0.0
|
||||||
|
partitions.append(list(
|
||||||
|
popwhile(lambda x: sum + key(x) or sum < limit, iterable)
|
||||||
|
))
|
||||||
|
return partitions
|
||||||
|
|
||||||
def round_dict(dic, places):
|
def round_dict(dic, places):
|
||||||
for key, value in dic.items():
|
for key, value in dic.items():
|
||||||
dic[key] = round(value, places)
|
dic[key] = round(value, places)
|
||||||
@ -63,104 +77,3 @@ class KeyConstraintDict(collections.UserDict):
|
|||||||
|
|
||||||
def missing(self):
|
def missing(self):
|
||||||
return self.required_keys - (self.seen_keys & self.required_keys)
|
return self.required_keys - (self.seen_keys & self.required_keys)
|
||||||
|
|
||||||
class SettingsBase:
|
|
||||||
"""
|
|
||||||
Support class for providing a nice and flexible settings interface
|
|
||||||
|
|
||||||
Classes inherit from this class and define what settings they provide and
|
|
||||||
which are required.
|
|
||||||
|
|
||||||
The constructor is either passed a dictionary containing these settings, or
|
|
||||||
keyword arguments specifying the same.
|
|
||||||
|
|
||||||
Settings are stored as attributes of self
|
|
||||||
"""
|
|
||||||
|
|
||||||
settings = tuple()
|
|
||||||
"""settings should be tuple containing two types of elements:
|
|
||||||
* bare strings, which must be valid identifiers.
|
|
||||||
* two-tuples, the first element being a identifier (as above) and the second
|
|
||||||
a docstring for the particular setting"""
|
|
||||||
|
|
||||||
required = tuple()
|
|
||||||
"""required can list settings which are required"""
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
def flatten_setting(setting):
|
|
||||||
return setting[0] if isinstance(setting, tuple) else setting
|
|
||||||
def flatten_settings(settings):
|
|
||||||
return tuple(flatten_setting(setting) for setting in settings)
|
|
||||||
|
|
||||||
def get_argument_dict(args, kwargs):
|
|
||||||
if len(args) == 1 and not kwargs:
|
|
||||||
# User can also pass in a dict for their settings
|
|
||||||
# Note: you could do that anyway, with the ** syntax
|
|
||||||
return args[0]
|
|
||||||
return kwargs
|
|
||||||
|
|
||||||
self.settings = flatten_settings(self.settings)
|
|
||||||
|
|
||||||
sm = KeyConstraintDict(self.settings, self.required)
|
|
||||||
settings_source = get_argument_dict(args, kwargs)
|
|
||||||
|
|
||||||
try:
|
|
||||||
sm.update(settings_source)
|
|
||||||
except KeyError as exc:
|
|
||||||
raise ConfigKeyError(type(self).__name__, key=exc.args[0]) from exc
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.__dict__.update(sm)
|
|
||||||
except KeyConstraintDict.MissingKeys as exc:
|
|
||||||
raise ConfigMissingError(type(self).__name__, missing=exc.keys) from exc
|
|
||||||
|
|
||||||
self.__name__ = "{}.{}".format(self.__module__, self.__class__.__name__)
|
|
||||||
|
|
||||||
self.init()
|
|
||||||
|
|
||||||
def init(self):
|
|
||||||
"""Convenience method which is called after all settings are set
|
|
||||||
|
|
||||||
In case you don't want to type that super()…blabla :-)"""
|
|
||||||
|
|
||||||
class ClassFinder:
|
|
||||||
"""Support class to find classes of specific bases in a module"""
|
|
||||||
|
|
||||||
def __init__(self, baseclass):
|
|
||||||
self.baseclass = baseclass
|
|
||||||
|
|
||||||
def predicate_factory(self, module):
|
|
||||||
def predicate(obj):
|
|
||||||
return (
|
|
||||||
inspect.isclass(obj) and
|
|
||||||
issubclass(obj, self.baseclass) and
|
|
||||||
obj.__module__ == module.__name__
|
|
||||||
)
|
|
||||||
return predicate
|
|
||||||
|
|
||||||
def search_module(self, module):
|
|
||||||
return list(zip(*inspect.getmembers(module, self.predicate_factory(module))))[1]
|
|
||||||
|
|
||||||
def get_class(self, module):
|
|
||||||
classes = self.search_module(module)
|
|
||||||
|
|
||||||
if len(classes) > 1:
|
|
||||||
# If there are multiple Module clases bundled in one module,
|
|
||||||
# well, we can't decide for the user.
|
|
||||||
raise ConfigAmbigiousClassesError(module.__name__, classes)
|
|
||||||
elif not classes:
|
|
||||||
raise ConfigInvalidModuleError(module.__name__)
|
|
||||||
|
|
||||||
return classes[0]
|
|
||||||
|
|
||||||
def get_module(self, module):
|
|
||||||
return getattr(__import__("i3pystatus.{module}".format(module=module), globals(), {}, []), module)
|
|
||||||
|
|
||||||
def instanciate_class_from_module(self, module, *args, **kwargs):
|
|
||||||
if isinstance(module, types.ModuleType):
|
|
||||||
return self.get_class(module)(*args, **kwargs)
|
|
||||||
elif isinstance(module, str):
|
|
||||||
return self.instanciate_class_from_module(self.get_module(module), *args, **kwargs)
|
|
||||||
elif args or kwargs:
|
|
||||||
raise ValueError("Additional arguments are invalid if 'module' is already an object")
|
|
||||||
return module
|
|
||||||
|
@ -9,7 +9,7 @@ import textwrap
|
|||||||
import i3pystatus
|
import i3pystatus
|
||||||
import i3pystatus.mail
|
import i3pystatus.mail
|
||||||
|
|
||||||
from .core.util import ClassFinder
|
from .core.imputil import ClassFinder
|
||||||
|
|
||||||
IGNORE = ("__main__", "mkdocs")
|
IGNORE = ("__main__", "mkdocs")
|
||||||
MODULE_FORMAT = """
|
MODULE_FORMAT = """
|
||||||
@ -36,21 +36,16 @@ class Module:
|
|||||||
else:
|
else:
|
||||||
self.name = "{module}.{cls}".format(module=module_name, cls=self.cls.__name__)
|
self.name = "{module}.{cls}".format(module=module_name, cls=self.cls.__name__)
|
||||||
|
|
||||||
if self.cls.__doc__ is not None:
|
self.doc = self.cls.__doc__ or module.__doc__ or ""
|
||||||
self.doc = self.cls.__doc__
|
|
||||||
elif module.__doc__ is not None:
|
|
||||||
self.doc = module.__doc__
|
|
||||||
else:
|
|
||||||
self.doc = ""
|
|
||||||
|
|
||||||
if hasattr(self.cls, "_endstring"):
|
if hasattr(self.cls, "_endstring"):
|
||||||
self.endstring = self.cls._endstring
|
self.endstring = self.cls._endstring
|
||||||
|
|
||||||
self.get_settings()
|
self.read_settings()
|
||||||
|
|
||||||
def get_settings(self):
|
def read_settings(self):
|
||||||
for setting in self.cls.settings:
|
for setting in self.cls.settings:
|
||||||
self.settings.append(Setting(self, setting))
|
self.settings.append(Setting(self.cls, setting))
|
||||||
|
|
||||||
def format_settings(self):
|
def format_settings(self):
|
||||||
return "\n".join(map(str, self.settings))
|
return "\n".join(map(str, self.settings))
|
||||||
@ -65,22 +60,21 @@ class Module:
|
|||||||
)
|
)
|
||||||
|
|
||||||
class Setting:
|
class Setting:
|
||||||
name = ""
|
|
||||||
doc = ""
|
doc = ""
|
||||||
required = False
|
required = False
|
||||||
default = sentinel = object()
|
default = sentinel = object()
|
||||||
|
|
||||||
def __init__(self, mod, setting):
|
def __init__(self, cls, setting):
|
||||||
if isinstance(setting, tuple):
|
if isinstance(setting, tuple):
|
||||||
self.name = setting[0]
|
self.name = setting[0]
|
||||||
self.doc = setting[1]
|
self.doc = setting[1]
|
||||||
else:
|
else:
|
||||||
self.name = setting
|
self.name = setting
|
||||||
|
|
||||||
if setting in mod.cls.required:
|
if setting in cls.required:
|
||||||
self.required = True
|
self.required = True
|
||||||
elif hasattr(mod.cls, self.name):
|
elif hasattr(cls, self.name):
|
||||||
self.default = getattr(mod.cls, self.name)
|
self.default = getattr(cls, self.name)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
attrs = []
|
attrs = []
|
||||||
@ -99,15 +93,11 @@ class Setting:
|
|||||||
|
|
||||||
return formatted
|
return formatted
|
||||||
|
|
||||||
def get_modules(path=None):
|
def get_modules(path):
|
||||||
if path is None:
|
|
||||||
path = i3pystatus.get_path()
|
|
||||||
|
|
||||||
modules = []
|
modules = []
|
||||||
for finder, modname, ispkg in pkgutil.iter_modules(path):
|
for finder, modname, ispkg in pkgutil.iter_modules(path):
|
||||||
if modname not in IGNORE:
|
if modname not in IGNORE:
|
||||||
modules.append(get_module(finder, modname))
|
modules.append(get_module(finder, modname))
|
||||||
|
|
||||||
return modules
|
return modules
|
||||||
|
|
||||||
def get_module(finder, modname):
|
def get_module(finder, modname):
|
||||||
@ -116,12 +106,11 @@ def get_module(finder, modname):
|
|||||||
|
|
||||||
def get_all(module_path, heading, finder=None):
|
def get_all(module_path, heading, finder=None):
|
||||||
mods = []
|
mods = []
|
||||||
if finder is None:
|
if not finder:
|
||||||
finder = i3pystatus.ClassFinder(i3pystatus.Module)
|
finder = ClassFinder(i3pystatus.Module)
|
||||||
|
|
||||||
for name, module in get_modules(module_path):
|
for name, module in get_modules(module_path):
|
||||||
classes = finder.search_module(module)
|
classes = finder.search_module(module)
|
||||||
|
|
||||||
for cls in classes:
|
for cls in classes:
|
||||||
mods.append(Module(cls, neighbours=len(classes), module_name=name, module=module, heading=heading))
|
mods.append(Module(cls, neighbours=len(classes), module_name=name, module=module, heading=heading))
|
||||||
|
|
||||||
@ -131,10 +120,9 @@ def generate_doc_for_module(module_path, heading="###", finder=None):
|
|||||||
return "".join(map(str, get_all(module_path, heading, finder)))
|
return "".join(map(str, get_all(module_path, heading, finder)))
|
||||||
|
|
||||||
with open("README.tpl.md", "r") as template:
|
with open("README.tpl.md", "r") as template:
|
||||||
|
|
||||||
tpl = template.read()
|
tpl = template.read()
|
||||||
tpl = tpl.replace("!!module_doc!!", generate_doc_for_module(i3pystatus.__path__))
|
tpl = tpl.replace("!!module_doc!!", generate_doc_for_module(i3pystatus.__path__))
|
||||||
finder = i3pystatus.ClassFinder(baseclass=i3pystatus.mail.Backend)
|
finder = ClassFinder(baseclass=i3pystatus.mail.Backend)
|
||||||
tpl = tpl.replace("!!i3pystatus.mail!!", generate_doc_for_module(i3pystatus.mail.__path__, "###", finder).replace("\n", "\n> "))
|
tpl = tpl.replace("!!i3pystatus.mail!!", generate_doc_for_module(i3pystatus.mail.__path__, "###", finder).replace("\n", "\n> "))
|
||||||
|
with open("README.md", "w") as output:
|
||||||
print(tpl)
|
output.write(tpl + "\n")
|
||||||
|
Loading…
Reference in New Issue
Block a user