genocide 236__tar.gz → 237__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. {genocide-236/genocide.egg-info → genocide-237}/PKG-INFO +1 -1
  2. genocide-237/genocide/booting.py +148 -0
  3. genocide-237/genocide/clients.py +206 -0
  4. genocide-237/genocide/command.py +192 -0
  5. genocide-237/genocide/configs.py +44 -0
  6. genocide-237/genocide/daemons.py +61 -0
  7. genocide-237/genocide/defines.py +86 -0
  8. genocide-237/genocide/encoder.py +107 -0
  9. genocide-237/genocide/excepts.py +48 -0
  10. genocide-237/genocide/handler.py +36 -0
  11. genocide-237/genocide/looping.py +73 -0
  12. {genocide-236 → genocide-237}/genocide/message.py +16 -24
  13. genocide-237/genocide/methods.py +243 -0
  14. {genocide-236/genocide/mirrors → genocide-237/genocide/minimal}/__index__.py +1 -1
  15. genocide-236/genocide/modules/__init__.py → genocide-237/genocide/modules/__index__.py +1 -1
  16. {genocide-236 → genocide-237}/genocide/modules/mdl.py +67 -72
  17. genocide-237/genocide/modules/pth.py +19 -0
  18. {genocide-236 → genocide-237}/genocide/modules/req.py +3 -5
  19. {genocide-236 → genocide-237}/genocide/modules/slg.py +3 -5
  20. genocide-237/genocide/modules/ver.py +13 -0
  21. genocide-237/genocide/namings/__index__.py +4 -0
  22. genocide-237/genocide/numbers/manual.html +148 -0
  23. genocide-237/genocide/objects.py +47 -0
  24. genocide-237/genocide/package.py +116 -0
  25. genocide-237/genocide/persist.py +323 -0
  26. genocide-237/genocide/pooling.py +82 -0
  27. genocide-237/genocide/repeats.py +64 -0
  28. genocide-237/genocide/require.py +100 -0
  29. genocide-237/genocide/runtime.py +180 -0
  30. genocide-237/genocide/statics.py +58 -0
  31. genocide-237/genocide/tasking.py +82 -0
  32. genocide-237/genocide/typings.py +34 -0
  33. genocide-237/genocide/utility.py +350 -0
  34. genocide-237/genocide/watcher.py +70 -0
  35. {genocide-236 → genocide-237/genocide.egg-info}/PKG-INFO +1 -1
  36. genocide-237/genocide.egg-info/SOURCES.txt +102 -0
  37. genocide-237/pyproject.toml +94 -0
  38. genocide-236/MANIFEST.in +0 -1
  39. genocide-236/bin/genocide +0 -19
  40. genocide-236/genocide/brokers.py +0 -67
  41. genocide-236/genocide/clients.py +0 -135
  42. genocide-236/genocide/command.py +0 -89
  43. genocide-236/genocide/configs.py +0 -31
  44. genocide-236/genocide/defines.py +0 -61
  45. genocide-236/genocide/engines.py +0 -71
  46. genocide-236/genocide/handler.py +0 -57
  47. genocide-236/genocide/loggers.py +0 -67
  48. genocide-236/genocide/modules/adm.py +0 -69
  49. genocide-236/genocide/modules/cfg.py +0 -44
  50. genocide-236/genocide/modules/eml.py +0 -94
  51. genocide-236/genocide/modules/fnd.py +0 -35
  52. genocide-236/genocide/modules/irc.py +0 -628
  53. genocide-236/genocide/modules/log.py +0 -28
  54. genocide-236/genocide/modules/man.py +0 -251
  55. genocide-236/genocide/modules/pth.py +0 -19
  56. genocide-236/genocide/modules/rss.py +0 -762
  57. genocide-236/genocide/modules/rst.py +0 -134
  58. genocide-236/genocide/modules/shw.py +0 -95
  59. genocide-236/genocide/modules/sil.py +0 -30
  60. genocide-236/genocide/modules/tdo.py +0 -45
  61. genocide-236/genocide/modules/tmr.py +0 -114
  62. genocide-236/genocide/modules/udp.py +0 -126
  63. genocide-236/genocide/modules/web.py +0 -143
  64. genocide-236/genocide/modules/wsd.py +0 -248
  65. genocide-236/genocide/network/manual.html +0 -364
  66. genocide-236/genocide/objects.py +0 -398
  67. genocide-236/genocide/package.py +0 -110
  68. genocide-236/genocide/parsers.py +0 -77
  69. genocide-236/genocide/persist.py +0 -231
  70. genocide-236/genocide/repeats.py +0 -76
  71. genocide-236/genocide/runtime.py +0 -273
  72. genocide-236/genocide/statics.py +0 -90
  73. genocide-236/genocide/threads.py +0 -97
  74. genocide-236/genocide/utility.py +0 -294
  75. genocide-236/genocide.egg-info/SOURCES.txt +0 -112
  76. genocide-236/pyproject.toml +0 -57
  77. {genocide-236 → genocide-237}/README.rst +0 -0
  78. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_images/ECHAabilify.png +0 -0
  79. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_images/ECHAclozapine.png +0 -0
  80. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_images/ECHAhaldol.png +0 -0
  81. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_images/ECHAzyprexa.png +0 -0
  82. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_images/OTP111.png +0 -0
  83. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_images/OTP222.png +0 -0
  84. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_images/abilifynew2.png +0 -0
  85. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_images/abilifypubchem.png +0 -0
  86. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_images/ack3.png +0 -0
  87. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_images/bevestigd.jpg +0 -0
  88. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_images/clozapinenew2.png +0 -0
  89. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_images/clozapinepubchem.png +0 -0
  90. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_images/genocide.png +0 -0
  91. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_images/haldolpubchem.png +0 -0
  92. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_images/haloperidolnew2.png +0 -0
  93. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_images/informed.jpg +0 -0
  94. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_images/kamer2.png +0 -0
  95. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_images/skull3.png +0 -0
  96. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_images/zyprexanew2.png +0 -0
  97. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_images/zyprexapubchem.png +0 -0
  98. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_static/alabaster.css +0 -0
  99. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_static/basic.css +0 -0
  100. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_static/custom.css +0 -0
  101. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_static/doctools.js +0 -0
  102. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_static/documentation_options.js +0 -0
  103. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_static/file.png +0 -0
  104. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_static/genocide.css +0 -0
  105. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_static/language_data.js +0 -0
  106. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_static/minus.png +0 -0
  107. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_static/opensearch.xml +0 -0
  108. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_static/otpcr.css +0 -0
  109. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_static/plus.png +0 -0
  110. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_static/pygments.css +0 -0
  111. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_static/searchtools.js +0 -0
  112. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_static/sitemap.xml +0 -0
  113. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_static/skull3.png +0 -0
  114. {genocide-236/genocide/network → genocide-237/genocide/numbers}/_static/sphinx_highlight.js +0 -0
  115. {genocide-236/genocide/network → genocide-237/genocide/numbers}/about.html +0 -0
  116. {genocide-236/genocide/network → genocide-237/genocide/numbers}/echa.html +0 -0
  117. {genocide-236/genocide/network → genocide-237/genocide/numbers}/evidence.html +0 -0
  118. {genocide-236/genocide/network → genocide-237/genocide/numbers}/genindex-all.html +0 -0
  119. {genocide-236/genocide/network → genocide-237/genocide/numbers}/genindex.html +0 -0
  120. {genocide-236/genocide/network → genocide-237/genocide/numbers}/googlea6568bcc05c8d50b.html +0 -0
  121. {genocide-236/genocide/network → genocide-237/genocide/numbers}/guilty.html +0 -0
  122. {genocide-236/genocide/network → genocide-237/genocide/numbers}/index.html +0 -0
  123. {genocide-236/genocide/network → genocide-237/genocide/numbers}/objects.inv +0 -0
  124. {genocide-236/genocide/network → genocide-237/genocide/numbers}/pdf/EM_Ack_OTP-CR-117_19.pdf +0 -0
  125. {genocide-236/genocide/network → genocide-237/genocide/numbers}/pdf/EM_T04_OTP-CR-117_19.pdf +0 -0
  126. {genocide-236/genocide/network → genocide-237/genocide/numbers}/pdf/EM_T07_OTP-CR-117_19_001.pdf +0 -0
  127. {genocide-236/genocide/network → genocide-237/genocide/numbers}/pdf/Kamer.pdf +0 -0
  128. {genocide-236/genocide/network → genocide-237/genocide/numbers}/pdf/Rome-Statute.pdf +0 -0
  129. {genocide-236/genocide/network → genocide-237/genocide/numbers}/pdf/bevestigd.pdf +0 -0
  130. {genocide-236/genocide/network → genocide-237/genocide/numbers}/pubchem.html +0 -0
  131. {genocide-236/genocide/network → genocide-237/genocide/numbers}/reconsider.html +0 -0
  132. {genocide-236/genocide/network → genocide-237/genocide/numbers}/reconsider2.html +0 -0
  133. {genocide-236/genocide/network → genocide-237/genocide/numbers}/request.html +0 -0
  134. {genocide-236/genocide/network → genocide-237/genocide/numbers}/robots.txt +0 -0
  135. {genocide-236/genocide/network → genocide-237/genocide/numbers}/search.html +0 -0
  136. {genocide-236/genocide/network → genocide-237/genocide/numbers}/searchindex.js +0 -0
  137. {genocide-236/genocide/network → genocide-237/genocide/numbers}/skull.html +0 -0
  138. {genocide-236/genocide/network → genocide-237/genocide/numbers}/writing.html +0 -0
  139. {genocide-236/genocide/network → genocide-237/genocide/numbers}/writings.html +0 -0
  140. {genocide-236 → genocide-237}/genocide.egg-info/dependency_links.txt +0 -0
  141. {genocide-236 → genocide-237}/genocide.egg-info/entry_points.txt +0 -0
  142. {genocide-236 → genocide-237}/genocide.egg-info/top_level.txt +0 -0
  143. {genocide-236 → genocide-237}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: genocide
3
- Version: 236
3
+ Version: 237
4
4
  Summary: Since 4 March 2019
5
5
  Author-email: Bart Thate <bthate@dds.nl>
6
6
  License-Expression: Unlicense
@@ -0,0 +1,148 @@
1
+ # This file is placed in the Public Domain.
2
+
3
+
4
+ "in the beginning"
5
+
6
+
7
+ import os
8
+ import threading
9
+ import time
10
+ import _thread
11
+
12
+
13
+ from .clients import Broker, Client
14
+ from .command import Commands
15
+ from .configs import Main
16
+ from .package import Mods
17
+ from .persist import Workdir
18
+ from .tasking import Task
19
+ from .utility import MD5, Event, Log, Logging, Utils
20
+
21
+
22
+ logger = Log(__name__)
23
+
24
+
25
+ class Boot:
26
+
27
+ "at startup"
28
+
29
+ running = Event()
30
+ stopped = Event()
31
+
32
+ @classmethod
33
+ def banner(cls, force=False):
34
+ "hello"
35
+
36
+ @classmethod
37
+ def boot(cls):
38
+ "prepare for runtime."
39
+ cls.configure()
40
+ MD5.core()
41
+ if Main.verbose:
42
+ cls.banner()
43
+ if Main.all:
44
+ Main.mods = ",".join(Mods.list())
45
+ Commands.table()
46
+ Mods.table()
47
+ if Main.scanner:
48
+ Commands.scanner()
49
+
50
+ @classmethod
51
+ def configure(cls, wdr=""):
52
+ "setup basic variables"
53
+ Workdir.wdr = wdr or Main.wdr or Workdir.wdr or Workdir.home(Main.name)
54
+ Workdir.skel()
55
+ Main.level = Main.level or "warning"
56
+ Logging.size(len(Main.name))
57
+ Logging.level(Main.level)
58
+ # Mods.dir("modules", Workdir.moddir())
59
+ Mods.dir(f"{Main.name}.miminal", Mods.moddir("minimal"))
60
+ Mods.dir(f"{Main.name}.modules", Mods.moddir())
61
+ if Main.path:
62
+ for pth in Utils.spl(Main.path):
63
+ pth = os.path.normpath(pth)
64
+ pkgname = pth.rsplit(os.sep, maxsplit=1)[-1]
65
+ Mods.dir(pkgname, pth)
66
+
67
+ @classmethod
68
+ def forever(cls):
69
+ "run forever until ctrl-c."
70
+ cls.running.set()
71
+ while cls.running.is_set():
72
+ try:
73
+ time.sleep(0.1)
74
+ except (KeyboardInterrupt, EOFError):
75
+ break
76
+ cls.stopped.set()
77
+
78
+ @classmethod
79
+ def init(cls, names, wait=False):
80
+ "call init of modules that have an init function."
81
+ thrs = []
82
+ for name in Utils.spl(names):
83
+ mod = Mods.get(name)
84
+ if not mod or "init" not in dir(mod):
85
+ continue
86
+ thrs.append(Task.start(mod.init))
87
+ if thrs and wait:
88
+ for thr in thrs:
89
+ thr.join()
90
+ return True
91
+
92
+ @classmethod
93
+ def shutdown(cls, wait=True):
94
+ "call stop on clients."
95
+ logger.info("shutdown")
96
+ for client in Broker.objs("wait"):
97
+ logger.debug("wait %s", client)
98
+ try:
99
+ client.wait()
100
+ except (KeyboardInterrupt, EOFError):
101
+ pass
102
+ time.sleep(0.01)
103
+ for client in Broker.objs("stop"):
104
+ logger.debug("stop %s", client)
105
+ try:
106
+ client.stop()
107
+ except (KeyboardInterrupt, EOFError):
108
+ pass
109
+ time.sleep(0.01)
110
+ if wait:
111
+ while True:
112
+ if len(threading.enumerate()) <= 1:
113
+ break
114
+ time.sleep(0.01)
115
+ cls.stopped.set()
116
+
117
+ @classmethod
118
+ def wrapped(cls, func, *args):
119
+ "wrap function in a try/except, silence ctrl-c/ctrl-d."
120
+ try:
121
+ func(*args)
122
+ except (KeyboardInterrupt, EOFError):
123
+ Client.block.set()
124
+ _thread.interrupt_main()
125
+
126
+ @classmethod
127
+ def wrap(cls, func, *args, dofinal=None):
128
+ "restore console."
129
+ import sys
130
+ import termios
131
+ try:
132
+ old = termios.tcgetattr(sys.stdin.fileno())
133
+ except termios.error:
134
+ old = None
135
+ try:
136
+ cls.wrapped(func, *args)
137
+ except (KeyboardInterrupt, EOFError):
138
+ pass
139
+ if old and old[0]:
140
+ termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, old)
141
+ if dofinal:
142
+ dofinal()
143
+
144
+
145
+ def __dir__():
146
+ return (
147
+ 'Boot',
148
+ )
@@ -0,0 +1,206 @@
1
+ # This file is placed in the Public Domain.
2
+
3
+
4
+ "displaying messages"
5
+
6
+
7
+ import _thread
8
+
9
+
10
+ from .handler import Handler
11
+ from .tasking import Task
12
+ from .utility import Event, RLock, Queue
13
+
14
+
15
+ class Broker:
16
+
17
+ "map repr(obj) to obj"
18
+
19
+ objects = {}
20
+
21
+ @classmethod
22
+ def add(cls, obj):
23
+ "add object to the broker, key is repr(obj)."
24
+ cls.objects[repr(obj)] = obj
25
+
26
+ @classmethod
27
+ def get(cls, origin):
28
+ "object by repr(obj)."
29
+ return cls.objects.get(origin)
30
+
31
+ @classmethod
32
+ def has(cls, obj):
33
+ "whether the Broker has object."
34
+ return repr(obj) in cls.objects
35
+
36
+ @classmethod
37
+ def like(cls, text):
38
+ "all keys with a substring in their key."
39
+ for orig in cls.objects:
40
+ if text in orig.split()[0]:
41
+ yield (orig, cls.get(orig))
42
+
43
+ @classmethod
44
+ def objs(cls, attr):
45
+ "objects with a certain attribute."
46
+ for obj in cls.objects.values():
47
+ if attr in dir(obj):
48
+ yield obj
49
+
50
+ @classmethod
51
+ def remove(cls, obj):
52
+ "remove object."
53
+ del cls.objects[repr(obj)]
54
+
55
+
56
+ class Display:
57
+
58
+ "unit of display"
59
+
60
+ block = Event()
61
+ last = ""
62
+
63
+ def __init__(self):
64
+ self.olock = RLock()
65
+ self.silent = False
66
+ Broker.add(self)
67
+
68
+ def announce(self, text):
69
+ "announce text to all channels."
70
+ if not self.silent:
71
+ self.raw(text)
72
+
73
+ def display(self, msg):
74
+ "display msg results."
75
+ with self.olock:
76
+ for txt in msg.result:
77
+ if self.block.is_set():
78
+ return
79
+ self.dosay(msg.channel, txt)
80
+ del txt
81
+ del msg
82
+
83
+ def dosay(self, channel, text):
84
+ "say called by display."
85
+ self.say(channel, text)
86
+
87
+ def raw(self, text):
88
+ "raw output."
89
+ raise NotImplementedError
90
+
91
+ def say(self, channel, text):
92
+ "say text in channel."
93
+ self.last = channel
94
+ self.raw(text)
95
+
96
+
97
+ class Output(Display):
98
+
99
+ "dedicated output loop"
100
+
101
+ def __init__(self):
102
+ Display.__init__(self)
103
+ self.oqueue = Queue()
104
+ self.ostopped = Event()
105
+
106
+ def display(self, msg):
107
+ "do actual display."
108
+
109
+ def output(self):
110
+ "output loop."
111
+ while not self.ostopped.is_set():
112
+ try:
113
+ msg = self.oqueue.get()
114
+ except (KeyboardInterrupt, EOFError):
115
+ _thread.interrupt_main()
116
+ if msg is None:
117
+ self.oqueue.task_done()
118
+ break
119
+ self.display(msg)
120
+ self.oqueue.task_done()
121
+
122
+ def raw(self, text):
123
+ "raw output."
124
+ raise NotImplementedError
125
+
126
+ def start(self, daemon=True, name=None):
127
+ "start output loop."
128
+ self.ostopped.clear()
129
+ Task.start(self.output, daemon=daemon, name=name)
130
+
131
+ def stop(self):
132
+ "stop output loop."
133
+ self.ostopped.set()
134
+ self.oqueue.put(None)
135
+
136
+ def wait(self):
137
+ "wait for output to finish."
138
+ try:
139
+ self.oqueue.join()
140
+ except (KeyboardInterrupt, EOFError):
141
+ _thread.interrupt_main()
142
+
143
+
144
+ class Client(Handler, Display):
145
+
146
+ "display with coupled handler"
147
+
148
+ def __init__(self):
149
+ Handler.__init__(self)
150
+ Display.__init__(self)
151
+
152
+ def raw(self, text):
153
+ "raw output."
154
+ raise NotImplementedError
155
+
156
+
157
+ class BufferedClient(Handler, Output):
158
+
159
+ "buffered output"
160
+
161
+ def __init__(self):
162
+ Handler.__init__(self)
163
+ Output.__init__(self)
164
+
165
+ def raw(self, text):
166
+ "raw output."
167
+ raise NotImplementedError
168
+
169
+ def start(self, daemon=True, name=None):
170
+ "start output loop."
171
+ Handler.start(self)
172
+ Output.start(self, daemon=daemon, name=name)
173
+
174
+ def stop(self):
175
+ "stop output loop."
176
+ Handler.stop(self)
177
+ Output.stop(self)
178
+
179
+
180
+ class Clients:
181
+
182
+ "collection of clients"
183
+
184
+ @staticmethod
185
+ def announce(text):
186
+ "announce text on all clients."
187
+ for obj in Broker.objs("announce"):
188
+ obj.announce(text)
189
+
190
+ @staticmethod
191
+ def display(msg):
192
+ "display results."
193
+ bot = Broker.get(msg.orig)
194
+ if bot:
195
+ bot.display(msg)
196
+
197
+
198
+ def __dir__():
199
+ return (
200
+ 'Broker',
201
+ 'BufferedClient',
202
+ 'Client',
203
+ 'Clients',
204
+ 'Display',
205
+ 'Output'
206
+ )
@@ -0,0 +1,192 @@
1
+ # This file is placed in the Public Domain.
2
+
3
+
4
+ "program your own commands"
5
+
6
+
7
+ import inspect
8
+
9
+
10
+ from .clients import Clients
11
+ from .message import Message
12
+ from .methods import Method
13
+ from .package import Mods
14
+ from .utility import Utils
15
+
16
+
17
+ class Commands:
18
+
19
+ "command dispatch"
20
+
21
+ cmds = {}
22
+ names = {}
23
+
24
+ @classmethod
25
+ def add(cls, *funcs):
26
+ "register a command."
27
+ for func in funcs:
28
+ name = Utils.funcname(func)
29
+ cls.cmds[name] = func
30
+
31
+ @classmethod
32
+ def cmd(cls, txt):
33
+ "parse single line to command and run it."
34
+ msg = Message()
35
+ msg.text = txt
36
+ msg.kind = "command"
37
+ cls.command(msg)
38
+ return msg.result
39
+
40
+ @classmethod
41
+ def command(cls, msg):
42
+ "command callback."
43
+ Parser.parse(msg, msg.text)
44
+ func = cls.cmds.get(msg.cmd, cls.ondemand(msg.cmd))
45
+ if func:
46
+ func(msg)
47
+ Clients.display(msg)
48
+ msg.ready()
49
+
50
+ @classmethod
51
+ def list(cls):
52
+ "scan for a list of all commands."
53
+ result = []
54
+ for modname in Mods.list():
55
+ mod = Mods.get(modname)
56
+ if not mod:
57
+ continue
58
+ result.extend([Utils.funcname(x) for x in cls.scan(mod, True)])
59
+ return result
60
+
61
+ @classmethod
62
+ def ondemand(cls, name):
63
+ "ondemand loading of commands."
64
+ modname = cls.names.get(name, None)
65
+ if not modname:
66
+ return None
67
+ mod = Mods.get(modname)
68
+ if not mod:
69
+ return None
70
+ cls.scan(mod)
71
+ return cls.cmds.get(name, None)
72
+
73
+ @classmethod
74
+ def scan(cls, mod, skip=False):
75
+ "scan module for commands."
76
+ result = []
77
+ for nme, func in inspect.getmembers(mod, inspect.isfunction):
78
+ if "cb_" in nme:
79
+ continue
80
+ if 'msg' in inspect.signature(func).parameters:
81
+ if not skip:
82
+ cls.add(func)
83
+ result.append(func)
84
+ return result
85
+
86
+ @classmethod
87
+ def scanner(cls):
88
+ "scan all modules."
89
+ for name in Mods.list():
90
+ mod = Mods.get(name)
91
+ if not mod:
92
+ continue
93
+ cls.scan(mod)
94
+
95
+ @classmethod
96
+ def statics(cls):
97
+ "read table."
98
+ try:
99
+ from .statics import NAMES
100
+ cls.names.update(NAMES)
101
+ except (ImportError, SyntaxError, ValueError):
102
+ pass
103
+
104
+ @classmethod
105
+ def table(cls):
106
+ "read static tables."
107
+ cls.statics()
108
+ if not cls.names:
109
+ cls.scanner()
110
+
111
+
112
+ cmd = Commands.cmd
113
+ typed = Method.typed
114
+
115
+
116
+ class Parser:
117
+
118
+ "parsing for commands"
119
+
120
+ @classmethod
121
+ def init(cls, obj, text, clean=False):
122
+ "set default values."
123
+ data = {
124
+ "args": [],
125
+ "cmd": "",
126
+ "gets": {},
127
+ "index": None,
128
+ "init": "",
129
+ "mod": "",
130
+ "opts": "",
131
+ "otxt": text,
132
+ "rest": "",
133
+ "silent": {},
134
+ "sets": {},
135
+ "text": text
136
+ }
137
+ for k, v in data.items():
138
+ if not clean:
139
+ setattr(obj, k, data.get(k, v) or v)
140
+ else:
141
+ setattr(obj, k, v)
142
+
143
+ @classmethod
144
+ def parse(cls, obj, text, clean=False):
145
+ "parse text for command and arguments."
146
+ cls.init(obj, text, clean)
147
+ args = []
148
+ nr = -1
149
+ for spli in text.split():
150
+ if spli.startswith("--"):
151
+ obj.opts += f",{spli[2:]}"
152
+ continue
153
+ if spli.startswith("-"):
154
+ try:
155
+ obj.index = int(spli[1:])
156
+ except ValueError:
157
+ obj.opts += spli[1:]
158
+ continue
159
+ if "-=" in spli:
160
+ key, value = spli.split("-=", maxsplit=1)
161
+ typed(obj.silent, key, value)
162
+ typed(obj.gets, key, value)
163
+ continue
164
+ if "==" in spli:
165
+ key, value = spli.split("==", maxsplit=1)
166
+ typed(obj.gets, key, value)
167
+ continue
168
+ if "=" in spli:
169
+ key, value = spli.split("=", maxsplit=1)
170
+ typed(obj.sets, key, value)
171
+ continue
172
+ nr += 1
173
+ if nr == 0:
174
+ obj.cmd = spli
175
+ continue
176
+ args.append(spli)
177
+ if args:
178
+ obj.args = args
179
+ obj.rest = " ".join(obj.args)
180
+ obj.text = obj.cmd + " " + obj.rest
181
+ if "v" in obj.opts:
182
+ obj.verbose = True
183
+ if "t" in obj.opts:
184
+ obj.threaded = True
185
+
186
+
187
+ def __dir__():
188
+ return (
189
+ 'Commands',
190
+ 'Parser',
191
+ 'cmd'
192
+ )
@@ -0,0 +1,44 @@
1
+ # This file is placed in the Public Domain.
2
+
3
+
4
+ "one config to rule them all"
5
+
6
+
7
+ from .methods import Method
8
+
9
+
10
+ class Config(type):
11
+
12
+ "config meta class"
13
+
14
+ def __getattr__(cls, key):
15
+ if key in dir(cls):
16
+ return cls.__getattribute__(key)
17
+ return ""
18
+
19
+ def __str__(cls):
20
+ return str(Method.skip(cls))
21
+
22
+
23
+ class Main(metaclass=Config):
24
+
25
+ "main config"
26
+
27
+ level = ""
28
+ mods = ""
29
+ name = Method.pkgname(Config)
30
+ otxt = ""
31
+ path = ""
32
+
33
+
34
+ class Cfg(metaclass=Config):
35
+
36
+ "inheritable config"
37
+
38
+
39
+ def __dir__():
40
+ return (
41
+ 'Cfg',
42
+ 'Config',
43
+ 'Main'
44
+ )
@@ -0,0 +1,61 @@
1
+ # This file is placed in the Public Domain.
2
+
3
+
4
+ "background daemon"
5
+
6
+
7
+ from .configs import Main
8
+ from .persist import Workdir
9
+
10
+
11
+ class Daemon:
12
+
13
+ "detach from console"
14
+
15
+ @classmethod
16
+ def daemon(cls):
17
+ "run in the background."
18
+ import os
19
+ import sys
20
+ pid = os.fork()
21
+ if pid != 0:
22
+ os._exit(0)
23
+ os.setsid()
24
+ pid2 = os.fork()
25
+ if pid2 != 0:
26
+ os._exit(0)
27
+ if not Main.verbose:
28
+ cls.null(sys.stdin)
29
+ cls.null(sys.stdout)
30
+ cls.null(sys.stderr)
31
+ os.umask(0o077)
32
+ os.chdir("/")
33
+ os.nice(10)
34
+
35
+ @classmethod
36
+ def null(cls, iostream):
37
+ "route to /dev/null."
38
+ import os
39
+ with open('/dev/null', 'r', encoding="utf-8") as sis:
40
+ os.dup2(sis.fileno(), iostream.fileno())
41
+
42
+ @classmethod
43
+ def pid(cls):
44
+ "return pid path."
45
+ return Workdir.pid(Main.name)
46
+
47
+ @classmethod
48
+ def privileges(cls):
49
+ "drop privileges."
50
+ import getpass
51
+ import os
52
+ import pwd
53
+ pwnam2 = pwd.getpwnam(getpass.getuser())
54
+ os.setgid(pwnam2.pw_gid)
55
+ os.setuid(pwnam2.pw_uid)
56
+
57
+
58
+ def __dir__():
59
+ return (
60
+ 'Daemon',
61
+ )