dimples 1.6.2__tar.gz → 1.6.4__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 (189) hide show
  1. {dimples-1.6.2 → dimples-1.6.4}/PKG-INFO +1 -1
  2. {dimples-1.6.2 → dimples-1.6.4}/dimples/conn/gatekeeper.py +50 -1
  3. {dimples-1.6.2 → dimples-1.6.4}/dimples/utils/__init__.py +2 -0
  4. dimples-1.6.4/dimples/utils/conf_item.py +240 -0
  5. {dimples-1.6.2 → dimples-1.6.4}/dimples/utils/config.py +15 -168
  6. {dimples-1.6.2 → dimples-1.6.4}/dimples.egg-info/PKG-INFO +1 -1
  7. {dimples-1.6.2 → dimples-1.6.4}/dimples.egg-info/SOURCES.txt +1 -0
  8. {dimples-1.6.2 → dimples-1.6.4}/setup.py +1 -1
  9. {dimples-1.6.2 → dimples-1.6.4}/LICENSE +0 -0
  10. {dimples-1.6.2 → dimples-1.6.4}/README.md +0 -0
  11. {dimples-1.6.2 → dimples-1.6.4}/dimples/__init__.py +0 -0
  12. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/__init__.py +0 -0
  13. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/checker.py +0 -0
  14. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/checkpoint.py +0 -0
  15. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/compat/__init__.py +0 -0
  16. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/compat/loader.py +0 -0
  17. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/cpu/__init__.py +0 -0
  18. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/cpu/app/__init__.py +0 -0
  19. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/cpu/app/filter.py +0 -0
  20. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/cpu/app/group.py +0 -0
  21. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/cpu/app/handler.py +0 -0
  22. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/cpu/commands.py +0 -0
  23. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/cpu/creator.py +0 -0
  24. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/cpu/customized.py +0 -0
  25. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/cpu/group.py +0 -0
  26. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/cpu/grp_expel.py +0 -0
  27. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/cpu/grp_invite.py +0 -0
  28. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/cpu/grp_join.py +0 -0
  29. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/cpu/grp_query.py +0 -0
  30. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/cpu/grp_quit.py +0 -0
  31. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/cpu/grp_reset.py +0 -0
  32. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/cpu/grp_resign.py +0 -0
  33. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/cpu/handshake.py +0 -0
  34. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/facebook.py +0 -0
  35. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/messenger.py +0 -0
  36. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/network/__init__.py +0 -0
  37. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/network/session.py +0 -0
  38. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/network/state.py +0 -0
  39. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/network/transition.py +0 -0
  40. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/packer.py +0 -0
  41. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/processor.py +0 -0
  42. {dimples-1.6.2 → dimples-1.6.4}/dimples/client/terminal.py +0 -0
  43. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/__init__.py +0 -0
  44. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/anonymous.py +0 -0
  45. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/ans.py +0 -0
  46. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/archivist.py +0 -0
  47. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/checker.py +0 -0
  48. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/compat/__init__.py +0 -0
  49. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/compat/address.py +0 -0
  50. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/compat/compatible.py +0 -0
  51. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/compat/compressor.py +0 -0
  52. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/compat/entity.py +0 -0
  53. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/compat/loader.py +0 -0
  54. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/compat/meta.py +0 -0
  55. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/compat/network.py +0 -0
  56. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/dbi/__init__.py +0 -0
  57. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/dbi/account.py +0 -0
  58. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/dbi/message.py +0 -0
  59. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/dbi/session.py +0 -0
  60. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/facebook.py +0 -0
  61. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/messenger.py +0 -0
  62. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/mkm/__init__.py +0 -0
  63. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/mkm/bot.py +0 -0
  64. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/mkm/station.py +0 -0
  65. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/mkm/utils.py +0 -0
  66. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/packer.py +0 -0
  67. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/processer.py +0 -0
  68. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/protocol/__init__.py +0 -0
  69. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/protocol/ans.py +0 -0
  70. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/protocol/app.py +0 -0
  71. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/protocol/block.py +0 -0
  72. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/protocol/groups.py +0 -0
  73. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/protocol/grp_admin.py +0 -0
  74. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/protocol/handshake.py +0 -0
  75. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/protocol/login.py +0 -0
  76. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/protocol/mute.py +0 -0
  77. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/protocol/password.py +0 -0
  78. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/protocol/report.py +0 -0
  79. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/protocol/roles.py +0 -0
  80. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/protocol/utils.py +0 -0
  81. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/protocol/version.py +0 -0
  82. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/queue.py +0 -0
  83. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/register.py +0 -0
  84. {dimples-1.6.2 → dimples-1.6.4}/dimples/common/session.py +0 -0
  85. {dimples-1.6.2 → dimples-1.6.4}/dimples/conn/__init__.py +0 -0
  86. {dimples-1.6.2 → dimples-1.6.4}/dimples/conn/flexible.py +0 -0
  87. {dimples-1.6.2 → dimples-1.6.4}/dimples/conn/gate.py +0 -0
  88. {dimples-1.6.2 → dimples-1.6.4}/dimples/conn/mars.py +0 -0
  89. {dimples-1.6.2 → dimples-1.6.4}/dimples/conn/mtp.py +0 -0
  90. {dimples-1.6.2 → dimples-1.6.4}/dimples/conn/protocol/__init__.py +0 -0
  91. {dimples-1.6.2 → dimples-1.6.4}/dimples/conn/protocol/mars.py +0 -0
  92. {dimples-1.6.2 → dimples-1.6.4}/dimples/conn/protocol/ws.py +0 -0
  93. {dimples-1.6.2 → dimples-1.6.4}/dimples/conn/queue.py +0 -0
  94. {dimples-1.6.2 → dimples-1.6.4}/dimples/conn/seeker.py +0 -0
  95. {dimples-1.6.2 → dimples-1.6.4}/dimples/conn/session.py +0 -0
  96. {dimples-1.6.2 → dimples-1.6.4}/dimples/conn/ws.py +0 -0
  97. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/__init__.py +0 -0
  98. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/account.py +0 -0
  99. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/dos/__init__.py +0 -0
  100. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/dos/base.py +0 -0
  101. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/dos/document.py +0 -0
  102. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/dos/group.py +0 -0
  103. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/dos/group_history.py +0 -0
  104. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/dos/group_keys.py +0 -0
  105. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/dos/login.py +0 -0
  106. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/dos/meta.py +0 -0
  107. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/dos/private.py +0 -0
  108. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/dos/station.py +0 -0
  109. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/dos/user.py +0 -0
  110. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/message.py +0 -0
  111. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/redis/__init__.py +0 -0
  112. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/redis/base.py +0 -0
  113. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/redis/document.py +0 -0
  114. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/redis/group.py +0 -0
  115. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/redis/grp_history.py +0 -0
  116. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/redis/grp_keys.py +0 -0
  117. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/redis/login.py +0 -0
  118. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/redis/message.py +0 -0
  119. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/redis/meta.py +0 -0
  120. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/redis/station.py +0 -0
  121. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/redis/user.py +0 -0
  122. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/session.py +0 -0
  123. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/t_base.py +0 -0
  124. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/t_cipherkey.py +0 -0
  125. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/t_document.py +0 -0
  126. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/t_group.py +0 -0
  127. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/t_group_history.py +0 -0
  128. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/t_group_keys.py +0 -0
  129. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/t_login.py +0 -0
  130. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/t_message.py +0 -0
  131. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/t_meta.py +0 -0
  132. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/t_private.py +0 -0
  133. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/t_station.py +0 -0
  134. {dimples-1.6.2 → dimples-1.6.4}/dimples/database/t_user.py +0 -0
  135. {dimples-1.6.2 → dimples-1.6.4}/dimples/edge/__init__.py +0 -0
  136. {dimples-1.6.2 → dimples-1.6.4}/dimples/edge/messenger.py +0 -0
  137. {dimples-1.6.2 → dimples-1.6.4}/dimples/edge/octopus.py +0 -0
  138. {dimples-1.6.2 → dimples-1.6.4}/dimples/edge/shared.py +0 -0
  139. {dimples-1.6.2 → dimples-1.6.4}/dimples/edge/start.py +0 -0
  140. {dimples-1.6.2 → dimples-1.6.4}/dimples/emitter.py +0 -0
  141. {dimples-1.6.2 → dimples-1.6.4}/dimples/group/__init__.py +0 -0
  142. {dimples-1.6.2 → dimples-1.6.4}/dimples/group/admin.py +0 -0
  143. {dimples-1.6.2 → dimples-1.6.4}/dimples/group/builder.py +0 -0
  144. {dimples-1.6.2 → dimples-1.6.4}/dimples/group/delegate.py +0 -0
  145. {dimples-1.6.2 → dimples-1.6.4}/dimples/group/emitter.py +0 -0
  146. {dimples-1.6.2 → dimples-1.6.4}/dimples/group/helper.py +0 -0
  147. {dimples-1.6.2 → dimples-1.6.4}/dimples/group/manager.py +0 -0
  148. {dimples-1.6.2 → dimples-1.6.4}/dimples/group/packer.py +0 -0
  149. {dimples-1.6.2 → dimples-1.6.4}/dimples/group/shared.py +0 -0
  150. {dimples-1.6.2 → dimples-1.6.4}/dimples/register/__init__.py +0 -0
  151. {dimples-1.6.2 → dimples-1.6.4}/dimples/register/base.py +0 -0
  152. {dimples-1.6.2 → dimples-1.6.4}/dimples/register/ext.py +0 -0
  153. {dimples-1.6.2 → dimples-1.6.4}/dimples/register/run.py +0 -0
  154. {dimples-1.6.2 → dimples-1.6.4}/dimples/register/shared.py +0 -0
  155. {dimples-1.6.2 → dimples-1.6.4}/dimples/server/__init__.py +0 -0
  156. {dimples-1.6.2 → dimples-1.6.4}/dimples/server/checker.py +0 -0
  157. {dimples-1.6.2 → dimples-1.6.4}/dimples/server/cpu/__init__.py +0 -0
  158. {dimples-1.6.2 → dimples-1.6.4}/dimples/server/cpu/ans.py +0 -0
  159. {dimples-1.6.2 → dimples-1.6.4}/dimples/server/cpu/creator.py +0 -0
  160. {dimples-1.6.2 → dimples-1.6.4}/dimples/server/cpu/document.py +0 -0
  161. {dimples-1.6.2 → dimples-1.6.4}/dimples/server/cpu/handshake.py +0 -0
  162. {dimples-1.6.2 → dimples-1.6.4}/dimples/server/cpu/login.py +0 -0
  163. {dimples-1.6.2 → dimples-1.6.4}/dimples/server/cpu/report.py +0 -0
  164. {dimples-1.6.2 → dimples-1.6.4}/dimples/server/deliver.py +0 -0
  165. {dimples-1.6.2 → dimples-1.6.4}/dimples/server/dis_roamer.py +0 -0
  166. {dimples-1.6.2 → dimples-1.6.4}/dimples/server/dispatcher.py +0 -0
  167. {dimples-1.6.2 → dimples-1.6.4}/dimples/server/facebook.py +0 -0
  168. {dimples-1.6.2 → dimples-1.6.4}/dimples/server/messenger.py +0 -0
  169. {dimples-1.6.2 → dimples-1.6.4}/dimples/server/packer.py +0 -0
  170. {dimples-1.6.2 → dimples-1.6.4}/dimples/server/processor.py +0 -0
  171. {dimples-1.6.2 → dimples-1.6.4}/dimples/server/push.py +0 -0
  172. {dimples-1.6.2 → dimples-1.6.4}/dimples/server/session.py +0 -0
  173. {dimples-1.6.2 → dimples-1.6.4}/dimples/server/session_center.py +0 -0
  174. {dimples-1.6.2 → dimples-1.6.4}/dimples/server/trace.py +0 -0
  175. {dimples-1.6.2 → dimples-1.6.4}/dimples/station/__init__.py +0 -0
  176. {dimples-1.6.2 → dimples-1.6.4}/dimples/station/handler.py +0 -0
  177. {dimples-1.6.2 → dimples-1.6.4}/dimples/station/shared.py +0 -0
  178. {dimples-1.6.2 → dimples-1.6.4}/dimples/station/start.py +0 -0
  179. {dimples-1.6.2 → dimples-1.6.4}/dimples/utils/cache.py +0 -0
  180. {dimples-1.6.2 → dimples-1.6.4}/dimples/utils/checker.py +0 -0
  181. {dimples-1.6.2 → dimples-1.6.4}/dimples/utils/digest.py +0 -0
  182. {dimples-1.6.2 → dimples-1.6.4}/dimples/utils/http.py +0 -0
  183. {dimples-1.6.2 → dimples-1.6.4}/dimples/utils/log.py +0 -0
  184. {dimples-1.6.2 → dimples-1.6.4}/dimples.egg-info/dependency_links.txt +0 -0
  185. {dimples-1.6.2 → dimples-1.6.4}/dimples.egg-info/entry_points.txt +0 -0
  186. {dimples-1.6.2 → dimples-1.6.4}/dimples.egg-info/requires.txt +0 -0
  187. {dimples-1.6.2 → dimples-1.6.4}/dimples.egg-info/top_level.txt +0 -0
  188. {dimples-1.6.2 → dimples-1.6.4}/setup.cfg +0 -0
  189. {dimples-1.6.2 → dimples-1.6.4}/tests/test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dimples
3
- Version: 1.6.2
3
+ Version: 1.6.4
4
4
  Summary: DIMP Library for Edges and Stations
5
5
  Home-page: https://github.com/dimchat/demo-py
6
6
  Author: Albert Moky
@@ -31,6 +31,7 @@
31
31
  import socket
32
32
  import time
33
33
  import traceback
34
+ import weakref
34
35
  from typing import Optional
35
36
 
36
37
  from dimsdk import ReliableMessage
@@ -38,7 +39,8 @@ from dimsdk import ReliableMessage
38
39
  from startrek.types import SocketAddress
39
40
  from startrek import Channel, Hub
40
41
  from startrek import BaseChannel
41
- from startrek import Connection, ConnectionDelegate, BaseConnection
42
+ from startrek import Connection, ConnectionDelegate
43
+ from startrek import BaseConnection, ActiveConnection
42
44
  from startrek import Arrival, Departure
43
45
  from startrek import Porter, PorterStatus, PorterDelegate
44
46
  from startrek import SocketHelper
@@ -88,6 +90,22 @@ class StreamServerHub(ServerHub):
88
90
  remote: SocketAddress, local: Optional[SocketAddress]) -> Optional[Connection]:
89
91
  return super()._remove_connection(connection=connection, remote=remote, local=None)
90
92
 
93
+ # Override
94
+ def _create_connection(self, remote: SocketAddress, local: Optional[SocketAddress]) -> Optional[Connection]:
95
+ conn = ServerConnection(remote=remote, local=local)
96
+ conn.delegate = self.delegate # gate
97
+ return conn
98
+
99
+
100
+ class ServerConnection(BaseConnection):
101
+
102
+ # Override
103
+ async def start(self, hub: Hub):
104
+ # 1. start state machine
105
+ await self._start_machine()
106
+ # 2. open channel via the hub
107
+ await self._open_channel(hub=hub)
108
+
91
109
 
92
110
  class StreamClientHub(ClientHub):
93
111
 
@@ -122,6 +140,37 @@ class StreamClientHub(ClientHub):
122
140
  remote: SocketAddress, local: Optional[SocketAddress]) -> Optional[Connection]:
123
141
  return super()._remove_connection(connection=connection, remote=remote, local=None)
124
142
 
143
+ # Override
144
+ def _create_connection(self, remote: SocketAddress, local: Optional[SocketAddress]) -> Optional[Connection]:
145
+ conn = ClientConnection(remote=remote, local=local)
146
+ conn.delegate = self.delegate # gate
147
+ return conn
148
+
149
+
150
+ class ClientConnection(ActiveConnection):
151
+
152
+ def __init__(self, remote: SocketAddress, local: Optional[SocketAddress]):
153
+ super().__init__(remote=remote, local=local)
154
+ self.__hub_ref = None
155
+
156
+ @property # Override
157
+ def hub(self) -> Optional[Hub]:
158
+ ref = self.__hub_ref
159
+ if ref is not None:
160
+ return ref()
161
+
162
+ # Override
163
+ async def start(self, hub: Hub):
164
+ # 1. start state machine
165
+ await self._start_machine()
166
+ # 2. open channel via the hub
167
+ await self._open_channel(hub=hub)
168
+ # 3. weak reference for the hub
169
+ self.__hub_ref = weakref.ref(hub)
170
+ # 4. start an async task to check channel
171
+ Runner.async_task(coro=self.run())
172
+ # await self.run()
173
+
125
174
 
126
175
  def reset_send_buffer_size(conn: Connection = None, sock: socket.socket = None) -> bool:
127
176
  if sock is None:
@@ -64,6 +64,7 @@ from .cache import CachePool, SharedCacheManager
64
64
 
65
65
  from .http import HttpSession, HttpClient
66
66
 
67
+ from .conf_item import IConfig, MessageTransferAgent, Supervisor, NeighborLoader
67
68
  from .config import Config
68
69
 
69
70
 
@@ -147,6 +148,7 @@ __all__ = [
147
148
 
148
149
  'FrequencyChecker', 'RecentTimeChecker',
149
150
 
151
+ 'IConfig', 'MessageTransferAgent', 'Supervisor', 'NeighborLoader',
150
152
  'Config',
151
153
 
152
154
  'is_before',
@@ -0,0 +1,240 @@
1
+ # -*- coding: utf-8 -*-
2
+ # ==============================================================================
3
+ # MIT License
4
+ #
5
+ # Copyright (c) 2026 Albert Moky
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in all
15
+ # copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE.
24
+ # ==============================================================================
25
+
26
+ import weakref
27
+ from abc import ABC, abstractmethod
28
+ from typing import Optional, Any, Set, List, Dict
29
+ from typing import Iterable
30
+
31
+ from aiou import JSONFile
32
+
33
+ from dimsdk import JSON
34
+ from dimsdk import Dictionary
35
+ from dimsdk import EntityType, ID
36
+ from dimsdk import Facebook
37
+
38
+ from .log import Logging
39
+ from .http import HttpClient
40
+
41
+
42
+ class IConfig(ABC):
43
+
44
+ @abstractmethod
45
+ def get_section(self, section: str) -> Optional[Dict]:
46
+ raise NotImplementedError('NotImplemented')
47
+
48
+ @abstractmethod
49
+ def get_integer(self, section: str, option: str) -> int:
50
+ raise NotImplementedError('NotImplemented')
51
+
52
+ @abstractmethod
53
+ def get_boolean(self, section: str, option: str) -> bool:
54
+ raise NotImplementedError('NotImplemented')
55
+
56
+ @abstractmethod
57
+ def get_string(self, section: str, option: str) -> Optional[str]:
58
+ raise NotImplementedError('NotImplemented')
59
+
60
+ @abstractmethod
61
+ def get_list(self, section: str, option: str, separator: str = ',') -> Optional[List[str]]:
62
+ raise NotImplementedError('NotImplemented')
63
+
64
+
65
+ class MessageTransferAgent(Dictionary):
66
+ """ DIM Network Node """
67
+
68
+ # Override
69
+ def __str__(self) -> str:
70
+ clazz = self.__class__.__name__
71
+ return '<%s host="%s" port=%d id="%s" />' % (clazz, self.host, self.port, self.identifier)
72
+
73
+ # Override
74
+ def __repr__(self) -> str:
75
+ clazz = self.__class__.__name__
76
+ return '<%s host="%s" port=%d id="%s" />' % (clazz, self.host, self.port, self.identifier)
77
+
78
+ @property
79
+ def identifier(self) -> Optional[ID]:
80
+ string = self.get(key='did')
81
+ if string is None:
82
+ string = self.get(key='ID')
83
+ return ID.parse(identifier=string)
84
+
85
+ @property
86
+ def host(self) -> str:
87
+ return self.get(key='host', default='')
88
+
89
+ @property
90
+ def port(self) -> int:
91
+ return self.get(key='port', default=0)
92
+
93
+ @classmethod
94
+ def parse(cls, node: Any):
95
+ if node is None:
96
+ return None
97
+ elif isinstance(node, MessageTransferAgent):
98
+ return node
99
+ elif isinstance(node, Dictionary):
100
+ node = node.to_dict()
101
+ host = node.get('host')
102
+ port = node.get('port')
103
+ if host is not None and port is not None and port > 0:
104
+ return cls(dictionary=node)
105
+
106
+ @classmethod
107
+ def convert(cls, array: Iterable[Any]):
108
+ stations = []
109
+ for node in array:
110
+ item = cls.parse(node=node)
111
+ if item is not None:
112
+ stations.append(item)
113
+ return stations
114
+
115
+ @classmethod
116
+ def revert(cls, stations: Iterable) -> List[Dict]:
117
+ array = []
118
+ for node in stations:
119
+ assert isinstance(node, MessageTransferAgent), 'station node error: %s' % node
120
+ info = node.to_dict()
121
+ array.append(info)
122
+ return array
123
+
124
+
125
+ class Supervisor(Logging):
126
+ """ System Administrators """
127
+
128
+ def __init__(self, facebook: Facebook):
129
+ super().__init__()
130
+ self.__facebook = weakref.ref(facebook)
131
+
132
+ @property
133
+ def facebook(self) -> Optional[Facebook]:
134
+ ref = self.__facebook
135
+ if ref is not None:
136
+ return ref()
137
+
138
+ # noinspection PyMethodMayBeStatic
139
+ def check_user(self, identifier: ID) -> bool:
140
+ """ Filter user """
141
+ return identifier.type == EntityType.USER
142
+
143
+ # noinspection PyMethodMayBeStatic
144
+ def get_identifiers(self, config: IConfig, section: str, option: str) -> List[ID]:
145
+ """ Get ID list from config """
146
+ array = config.get_list(section=section, option=option)
147
+ return [] if array is None else ID.convert(array=array)
148
+
149
+ async def get_users(self, config: IConfig, section: str = 'system', option: str = 'supervisors') -> Set[ID]:
150
+ """ Get system administrators from config """
151
+ all_users = set()
152
+ array = self.get_identifiers(config=config, section=section, option=option)
153
+ if array is None or len(array) == 0:
154
+ return all_users
155
+ facebook = self.facebook
156
+ if facebook is None:
157
+ # only filter user
158
+ for item in array:
159
+ if self.check_user(identifier=item):
160
+ all_users.add(item)
161
+ return all_users
162
+ # extract group members
163
+ for item in array:
164
+ if item.is_user:
165
+ if self.check_user(identifier=item):
166
+ all_users.add(item)
167
+ continue
168
+ assert item.is_group, 'group ID error: %s' % item
169
+ group_members = await facebook.get_members(identifier=item)
170
+ if group_members is None or len(group_members) == 0:
171
+ self.warning(msg='failed to get members for group: %s' % item)
172
+ continue
173
+ for member in group_members:
174
+ if self.check_user(identifier=member):
175
+ all_users.add(member)
176
+ return all_users
177
+
178
+
179
+ class NeighborLoader(Logging):
180
+
181
+ def __init__(self):
182
+ super().__init__()
183
+ self.__http = HttpClient()
184
+
185
+ async def load_stations(self, config: IConfig) -> Optional[List[MessageTransferAgent]]:
186
+ # check remote URL
187
+ source = config.get_string(section='neighbors', option='source')
188
+ if source is None:
189
+ stations = None
190
+ else:
191
+ stations = await self._download_stations(url=source)
192
+ # check local path
193
+ output = config.get_string(section='neighbors', option='output')
194
+ if output is None:
195
+ self.warning(msg='neighbors path not set')
196
+ elif stations is None:
197
+ stations = await self._load_stations(path=output)
198
+ else:
199
+ await self._save_stations(stations=stations, path=output)
200
+ # OK
201
+ return stations
202
+
203
+ async def _download_stations(self, url: str) -> Optional[List[MessageTransferAgent]]:
204
+ self.info(msg='downloading stations: %s' % url)
205
+ http = self.__http
206
+ try:
207
+ response = http.cache_get(url=url)
208
+ if response is None or response.status_code != 200:
209
+ self.error(msg='failed to get URL: %s response: %s' % (url, response))
210
+ return None
211
+ else:
212
+ text = response.text
213
+ stations = JSON.decode(string=text)
214
+ except Exception as error:
215
+ self.error(msg='failed to download stations: %s, %s' % (error, url))
216
+ return None
217
+ if isinstance(stations, Dict):
218
+ stations = stations.get('stations')
219
+ if isinstance(stations, List):
220
+ return MessageTransferAgent.convert(array=stations)
221
+
222
+ async def _load_stations(self, path: str) -> Optional[List[MessageTransferAgent]]:
223
+ self.info(msg='loading stations: %s' % path)
224
+ try:
225
+ stations = await JSONFile(path=path).read()
226
+ except Exception as error:
227
+ self.error(msg='failed to load stations: %s, %s' % (error, path))
228
+ return None
229
+ if isinstance(stations, Dict):
230
+ stations = stations.get('stations')
231
+ if isinstance(stations, List):
232
+ return MessageTransferAgent.convert(array=stations)
233
+
234
+ async def _save_stations(self, stations: List[MessageTransferAgent], path: str) -> bool:
235
+ info = MessageTransferAgent.revert(stations=stations)
236
+ self.info(msg='saving %d station(s): %s' % (len(stations), path))
237
+ try:
238
+ return await JSONFile(path=path).write(info)
239
+ except Exception as error:
240
+ self.error(msg='failed to save stations: %s, %s' % (error, path))
@@ -24,83 +24,20 @@
24
24
  # ==============================================================================
25
25
 
26
26
  from configparser import ConfigParser
27
- from typing import Optional, Any, List, Dict
28
- from typing import Iterable
27
+ from typing import Optional, List, Dict
29
28
 
30
- from aiou import JSONFile
31
29
  from aiou import RedisConnector
32
30
 
33
- from dimsdk import JSON
34
- from dimsdk import Dictionary
35
31
  from dimsdk import ID
36
- from dimsdk import Facebook
37
32
 
38
33
  from .log import Log, Logging
39
- from .http import HttpClient
40
-
41
-
42
- class MessageTransferAgent(Dictionary):
43
- """ DIM Network Node """
44
-
45
- # Override
46
- def __str__(self) -> str:
47
- clazz = self.__class__.__name__
48
- return '<%s host="%s" port=%d id="%s" />' % (clazz, self.host, self.port, self.identifier)
49
-
50
- # Override
51
- def __repr__(self) -> str:
52
- clazz = self.__class__.__name__
53
- return '<%s host="%s" port=%d id="%s" />' % (clazz, self.host, self.port, self.identifier)
54
-
55
- @property
56
- def identifier(self) -> Optional[ID]:
57
- string = self.get(key='did')
58
- if string is None:
59
- string = self.get(key='ID')
60
- return ID.parse(identifier=string)
61
-
62
- @property
63
- def host(self) -> str:
64
- return self.get(key='host', default='')
65
-
66
- @property
67
- def port(self) -> int:
68
- return self.get(key='port', default=0)
69
-
70
- @classmethod
71
- def parse(cls, node: Any):
72
- if node is None:
73
- return None
74
- elif isinstance(node, MessageTransferAgent):
75
- return node
76
- elif isinstance(node, Dictionary):
77
- node = node.to_dict()
78
- host = node.get('host')
79
- port = node.get('port')
80
- if host is not None and port is not None and port > 0:
81
- return cls(dictionary=node)
82
-
83
- @classmethod
84
- def convert(cls, array: Iterable[Any]):
85
- stations = []
86
- for node in array:
87
- item = cls.parse(node=node)
88
- if item is not None:
89
- stations.append(item)
90
- return stations
91
-
92
- @classmethod
93
- def revert(cls, stations: Iterable) -> List[Dict]:
94
- array = []
95
- for node in stations:
96
- assert isinstance(node, MessageTransferAgent), 'station node error: %s' % node
97
- info = node.to_dict()
98
- array.append(info)
99
- return array
34
+ from .conf_item import IConfig
35
+ from .conf_item import MessageTransferAgent
36
+ from .conf_item import NeighborLoader
100
37
 
101
38
 
102
39
  # @Singleton
103
- class Config(Logging):
40
+ class Config(IConfig, Logging):
104
41
  """ Config info from ini file """
105
42
 
106
43
  def __init__(self):
@@ -149,11 +86,13 @@ class Config(Logging):
149
86
  def __repr__(self) -> str:
150
87
  return 'Config: %s' % self.to_dict()
151
88
 
89
+ # Override
152
90
  def get_section(self, section: str) -> Optional[Dict]:
153
91
  parser = self.__parser
154
92
  if parser is not None:
155
93
  return _section_options(parser=parser, section=section)
156
94
 
95
+ # Override
157
96
  def get_integer(self, section: str, option: str) -> int:
158
97
  parser = self.__parser
159
98
  if parser is None:
@@ -164,6 +103,7 @@ class Config(Logging):
164
103
  self.error(msg='failed to get integer: %s, %s, %s' % (section, option, error))
165
104
  return 0
166
105
 
106
+ # Override
167
107
  def get_boolean(self, section: str, option: str) -> bool:
168
108
  parser = self.__parser
169
109
  if parser is None:
@@ -173,6 +113,7 @@ class Config(Logging):
173
113
  except Exception as error:
174
114
  self.error(msg='failed to get boolean: %s, %s, %s' % (section, option, error))
175
115
 
116
+ # Override
176
117
  def get_string(self, section: str, option: str) -> Optional[str]:
177
118
  parser = self.__parser
178
119
  if parser is None:
@@ -182,15 +123,12 @@ class Config(Logging):
182
123
  except Exception as error:
183
124
  self.error(msg='failed to get string : %s, %s, %s' % (section, option, error))
184
125
 
185
- def get_identifier(self, section: str, option: str) -> Optional[ID]:
186
- value = self.get_string(section=section, option=option)
187
- return ID.parse(identifier=value)
188
-
189
- def get_list(self, section: str, option: str, separator: str = ',') -> List[str]:
126
+ # Override
127
+ def get_list(self, section: str, option: str, separator: str = ',') -> Optional[List[str]]:
190
128
  """ get str and separate to a list """
191
129
  text = self.get_string(section=section, option=option)
192
130
  if text is None:
193
- return []
131
+ return None
194
132
  result = []
195
133
  array = text.split(separator)
196
134
  for item in array:
@@ -199,36 +137,9 @@ class Config(Logging):
199
137
  result.append(string)
200
138
  return result
201
139
 
202
- #
203
- # ID list
204
- #
205
-
206
- def get_identifiers(self, section: str, option: str) -> List[ID]:
207
- array = self.get_list(section=section, option=option)
208
- return ID.convert(array=array)
209
-
210
- async def get_users(self, section: str, option: str, facebook: Facebook) -> List[ID]:
211
- users = []
212
- array = self.get_identifiers(section=section, option=option)
213
- for item in array:
214
- if item.is_user:
215
- if item not in users:
216
- users.append(item)
217
- continue
218
- # extract group members
219
- members = await facebook.get_members(identifier=item)
220
- for usr in members:
221
- if usr not in users:
222
- users.append(usr)
223
- return users
224
-
225
- async def get_supervisors(self, section: str = 'admin', option: str = 'supervisors',
226
- facebook: Facebook = None) -> List[ID]:
227
- """ extract group members when facebook available """
228
- if facebook is None:
229
- return self.get_identifiers(section=section, option=option)
230
- else:
231
- return await self.get_users(section=section, option=option, facebook=facebook)
140
+ def get_identifier(self, section: str, option: str) -> Optional[ID]:
141
+ value = self.get_string(section=section, option=option)
142
+ return ID.parse(identifier=value)
232
143
 
233
144
  #
234
145
  # database
@@ -339,70 +250,6 @@ class Config(Logging):
339
250
  return neighbor_stations
340
251
 
341
252
 
342
- class NeighborLoader(Logging):
343
-
344
- def __init__(self):
345
- super().__init__()
346
- self.__http = HttpClient()
347
-
348
- async def load_stations(self, config: Config) -> Optional[List[MessageTransferAgent]]:
349
- # check remote URL
350
- source = config.get_string(section='neighbors', option='source')
351
- if source is None:
352
- stations = None
353
- else:
354
- stations = await self._download_stations(url=source)
355
- # check local path
356
- output = config.get_string(section='neighbors', option='output')
357
- if output is None:
358
- self.warning(msg='neighbors path not set')
359
- elif stations is None:
360
- stations = await self._load_stations(path=output)
361
- else:
362
- await self._save_stations(stations=stations, path=output)
363
- # OK
364
- return stations
365
-
366
- async def _download_stations(self, url: str) -> Optional[List[MessageTransferAgent]]:
367
- self.info(msg='downloading stations: %s' % url)
368
- http = self.__http
369
- try:
370
- response = http.cache_get(url=url)
371
- if response is None or response.status_code != 200:
372
- self.error(msg='failed to get URL: %s response: %s' % (url, response))
373
- return None
374
- else:
375
- text = response.text
376
- stations = JSON.decode(string=text)
377
- except Exception as error:
378
- self.error(msg='failed to download stations: %s, %s' % (error, url))
379
- return None
380
- if isinstance(stations, Dict):
381
- stations = stations.get('stations')
382
- if isinstance(stations, List):
383
- return MessageTransferAgent.convert(array=stations)
384
-
385
- async def _load_stations(self, path: str) -> Optional[List[MessageTransferAgent]]:
386
- self.info(msg='loading stations: %s' % path)
387
- try:
388
- stations = await JSONFile(path=path).read()
389
- except Exception as error:
390
- self.error(msg='failed to load stations: %s, %s' % (error, path))
391
- return None
392
- if isinstance(stations, Dict):
393
- stations = stations.get('stations')
394
- if isinstance(stations, List):
395
- return MessageTransferAgent.convert(array=stations)
396
-
397
- async def _save_stations(self, stations: List[MessageTransferAgent], path: str) -> bool:
398
- info = MessageTransferAgent.revert(stations=stations)
399
- self.info(msg='saving %d station(s): %s' % (len(stations), path))
400
- try:
401
- return await JSONFile(path=path).write(info)
402
- except Exception as error:
403
- self.error(msg='failed to save stations: %s, %s' % (error, path))
404
-
405
-
406
253
  def _update_sections(info: Dict, parser: ConfigParser) -> Dict:
407
254
  sections = parser.sections()
408
255
  for name in sections:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dimples
3
- Version: 1.6.2
3
+ Version: 1.6.4
4
4
  Summary: DIMP Library for Edges and Stations
5
5
  Home-page: https://github.com/dimchat/demo-py
6
6
  Author: Albert Moky
@@ -179,6 +179,7 @@ dimples/station/start.py
179
179
  dimples/utils/__init__.py
180
180
  dimples/utils/cache.py
181
181
  dimples/utils/checker.py
182
+ dimples/utils/conf_item.py
182
183
  dimples/utils/config.py
183
184
  dimples/utils/digest.py
184
185
  dimples/utils/http.py
@@ -14,7 +14,7 @@ import io
14
14
 
15
15
  from setuptools import setup, find_packages
16
16
 
17
- __version__ = '1.6.2'
17
+ __version__ = '1.6.4'
18
18
  __author__ = 'Albert Moky'
19
19
  __contact__ = 'albert.moky@gmail.com'
20
20
 
File without changes
File without changes
File without changes