dimp 2.3.1__tar.gz → 2.3.3__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 (53) hide show
  1. {dimp-2.3.1 → dimp-2.3.3}/PKG-INFO +68 -47
  2. {dimp-2.3.1 → dimp-2.3.3}/README.md +67 -46
  3. {dimp-2.3.1 → dimp-2.3.3}/dimp/__init__.py +11 -9
  4. {dimp-2.3.1 → dimp-2.3.3}/dimp/crypto/keys.py +45 -21
  5. {dimp-2.3.1 → dimp-2.3.3}/dimp/crypto/pnf.py +12 -9
  6. {dimp-2.3.1 → dimp-2.3.3}/dimp/crypto/ted.py +16 -16
  7. {dimp-2.3.1 → dimp-2.3.3}/dimp/dkd/__init__.py +26 -22
  8. {dimp-2.3.1 → dimp-2.3.3}/dimp/mkm/docs.py +19 -15
  9. {dimp-2.3.1 → dimp-2.3.3}/dimp/mkm/document.py +8 -12
  10. {dimp-2.3.1 → dimp-2.3.3}/dimp/mkm/meta.py +7 -7
  11. {dimp-2.3.1 → dimp-2.3.3}/dimp/msg/base.py +2 -2
  12. {dimp-2.3.1 → dimp-2.3.3}/dimp/msg/envelope.py +19 -16
  13. {dimp-2.3.1 → dimp-2.3.3}/dimp/msg/instant.py +8 -6
  14. {dimp-2.3.1 → dimp-2.3.3}/dimp/msg/reliable.py +2 -2
  15. {dimp-2.3.1 → dimp-2.3.3}/dimp/msg/secure.py +15 -9
  16. {dimp-2.3.1 → dimp-2.3.3}/dimp/plugins/__init__.py +2 -2
  17. {dimp-2.3.1 → dimp-2.3.3}/dimp/protocol/__init__.py +10 -8
  18. {dimp-2.3.1/dimp/dkd → dimp-2.3.3/dimp/protocol}/base.py +141 -16
  19. {dimp-2.3.1/dimp/dkd → dimp-2.3.3/dimp/protocol}/commands.py +127 -31
  20. {dimp-2.3.1/dimp/dkd → dimp-2.3.3/dimp/protocol}/contents.py +170 -136
  21. dimp-2.3.3/dimp/protocol/customized.py +113 -0
  22. {dimp-2.3.1 → dimp-2.3.3}/dimp/protocol/files.py +150 -9
  23. dimp-2.3.3/dimp/protocol/forward.py +253 -0
  24. dimp-2.3.3/dimp/protocol/groups.py +498 -0
  25. {dimp-2.3.1/dimp/dkd → dimp-2.3.3/dimp/protocol}/money.py +93 -27
  26. {dimp-2.3.1 → dimp-2.3.3}/dimp/protocol/quote.py +51 -34
  27. {dimp-2.3.1/dimp/dkd → dimp-2.3.3/dimp/protocol}/receipt.py +83 -11
  28. {dimp-2.3.1 → dimp-2.3.3}/dimp.egg-info/PKG-INFO +68 -47
  29. {dimp-2.3.1 → dimp-2.3.3}/dimp.egg-info/SOURCES.txt +3 -10
  30. dimp-2.3.3/dimp.egg-info/requires.txt +2 -0
  31. {dimp-2.3.1 → dimp-2.3.3}/setup.py +3 -3
  32. dimp-2.3.1/dimp/dkd/files.py +0 -181
  33. dimp-2.3.1/dimp/dkd/group_admins.py +0 -142
  34. dimp-2.3.1/dimp/dkd/groups.py +0 -234
  35. dimp-2.3.1/dimp/dkd/quote.py +0 -145
  36. dimp-2.3.1/dimp/plugins/cmd.py +0 -79
  37. dimp-2.3.1/dimp/protocol/commands.py +0 -247
  38. dimp-2.3.1/dimp/protocol/contents.py +0 -279
  39. dimp-2.3.1/dimp/protocol/groups.py +0 -286
  40. dimp-2.3.1/dimp/protocol/money.py +0 -124
  41. dimp-2.3.1/dimp/protocol/receipt.py +0 -130
  42. dimp-2.3.1/dimp.egg-info/requires.txt +0 -2
  43. {dimp-2.3.1 → dimp-2.3.3}/LICENSE +0 -0
  44. {dimp-2.3.1 → dimp-2.3.3}/dimp/crypto/__init__.py +0 -0
  45. {dimp-2.3.1 → dimp-2.3.3}/dimp/crypto/algorithms.py +0 -0
  46. {dimp-2.3.1 → dimp-2.3.3}/dimp/mkm/__init__.py +0 -0
  47. {dimp-2.3.1 → dimp-2.3.3}/dimp/msg/__init__.py +0 -0
  48. {dimp-2.3.1 → dimp-2.3.3}/dimp/protocol/docs.py +0 -0
  49. {dimp-2.3.1 → dimp-2.3.3}/dimp/protocol/helpers.py +0 -0
  50. {dimp-2.3.1 → dimp-2.3.3}/dimp/protocol/types.py +0 -0
  51. {dimp-2.3.1 → dimp-2.3.3}/dimp.egg-info/dependency_links.txt +0 -0
  52. {dimp-2.3.1 → dimp-2.3.3}/dimp.egg-info/top_level.txt +0 -0
  53. {dimp-2.3.1 → dimp-2.3.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dimp
3
- Version: 2.3.1
3
+ Version: 2.3.3
4
4
  Summary: Decentralized Instant Messaging Protocol
5
5
  Home-page: https://github.com/dimchat/core-py
6
6
  Author: Albert Moky
@@ -45,8 +45,9 @@ License-File: LICENSE
45
45
  3. (S-C) handshake success
46
46
 
47
47
  ```python
48
+ from abc import ABC, abstractmethod
48
49
  from enum import IntEnum
49
- from typing import Optional, Any, Dict
50
+ from typing import Optional, Dict
50
51
 
51
52
  from dimp import Command, BaseCommand
52
53
 
@@ -58,7 +59,20 @@ class HandshakeState(IntEnum):
58
59
  Success = 3 # S -> C, handshake accepted
59
60
 
60
61
 
61
- class HandshakeCommand(BaseCommand):
62
+ def handshake_state(title: str, session: str = None) -> HandshakeState:
63
+ # Server -> Client
64
+ if title == 'DIM!': # or title == 'OK!':
65
+ return HandshakeState.Success
66
+ if title == 'DIM?':
67
+ return HandshakeState.Again
68
+ # Client -> Server: "Hello world!"
69
+ if session is None or len(session) == 0:
70
+ return HandshakeState.Start
71
+ else:
72
+ return HandshakeState.Restart
73
+
74
+
75
+ class HandshakeCommand(Command, ABC):
62
76
  """
63
77
  Handshake Command
64
78
  ~~~~~~~~~~~~~~~~~
@@ -74,37 +88,24 @@ class HandshakeCommand(BaseCommand):
74
88
  """
75
89
  HANDSHAKE = 'handshake'
76
90
 
77
- def __init__(self, content: Dict[str, Any] = None, title: str = None, session: str = None):
78
- if content is None:
79
- # 1. new command with title & session key
80
- assert title is not None, 'handshake command error: %s' % session
81
- cmd = self.HANDSHAKE
82
- super().__init__(cmd=cmd)
83
- self['title'] = title
84
- self['message'] = title # TODO: remove after all clients upgraded
85
- if session is not None:
86
- self['session'] = session
87
- else:
88
- # 2. command info from network
89
- assert title is None and session is None, 'params error: %s, %s, %s' % (content, title, session)
90
- super().__init__(content)
91
-
92
91
  @property
92
+ @abstractmethod
93
93
  def title(self) -> str:
94
- # TODO: modify after all clients upgraded
95
- text = self.get_str(key='title', default=None)
96
- if text is None:
97
- # compatible with v1.0
98
- text = self.get_str(key='message', default='')
99
- return text
94
+ raise NotImplemented
100
95
 
101
96
  @property
97
+ @abstractmethod
102
98
  def session(self) -> Optional[str]:
103
- return self.get_str(key='session', default=None)
99
+ raise NotImplemented
104
100
 
105
101
  @property
102
+ @abstractmethod
106
103
  def state(self) -> HandshakeState:
107
- return handshake_state(title=self.title, session=self.session)
104
+ raise NotImplemented
105
+
106
+ #
107
+ # Factories
108
+ #
108
109
 
109
110
  @classmethod
110
111
  def offer(cls, session: str = None) -> Command:
@@ -114,7 +115,7 @@ class HandshakeCommand(BaseCommand):
114
115
  :param session: Old session key
115
116
  :return: HandshakeCommand object
116
117
  """
117
- return HandshakeCommand(title='Hello world!', session=session)
118
+ return BaseHandshakeCommand(title='Hello world!', session=session)
118
119
 
119
120
  @classmethod
120
121
  def ask(cls, session: str) -> Command:
@@ -124,7 +125,7 @@ class HandshakeCommand(BaseCommand):
124
125
  :param session: New session key
125
126
  :return: HandshakeCommand object
126
127
  """
127
- return HandshakeCommand(title='DIM?', session=session)
128
+ return BaseHandshakeCommand(title='DIM?', session=session)
128
129
 
129
130
  @classmethod
130
131
  def accepted(cls, session: str = None) -> Command:
@@ -133,7 +134,7 @@ class HandshakeCommand(BaseCommand):
133
134
 
134
135
  :return: HandshakeCommand object
135
136
  """
136
- return HandshakeCommand(title='DIM!', session=session)
137
+ return BaseHandshakeCommand(title='DIM!', session=session)
137
138
 
138
139
  start = offer # (1. C->S) first handshake, without session
139
140
  again = ask # (2. S->C) ask client to handshake with new session key
@@ -141,17 +142,34 @@ class HandshakeCommand(BaseCommand):
141
142
  success = accepted # (4. S->C) notice the client that handshake accepted
142
143
 
143
144
 
144
- def handshake_state(title: str, session: str = None) -> HandshakeState:
145
- # Server -> Client
146
- if title == 'DIM!': # or title == 'OK!':
147
- return HandshakeState.Success
148
- if title == 'DIM?':
149
- return HandshakeState.Again
150
- # Client -> Server: "Hello world!"
151
- if session is None or len(session) == 0:
152
- return HandshakeState.Start
153
- else:
154
- return HandshakeState.Restart
145
+ class BaseHandshakeCommand(BaseCommand, HandshakeCommand):
146
+
147
+ def __init__(self, content: Dict = None, title: str = None, session: str = None):
148
+ if content is None:
149
+ # 1. new command with title & session key
150
+ assert title is not None, 'handshake command error: %s' % session
151
+ cmd = self.HANDSHAKE
152
+ super().__init__(cmd=cmd)
153
+ self['title'] = title
154
+ self['message'] = title # TODO: remove after all clients upgraded
155
+ if session is not None:
156
+ self['session'] = session
157
+ else:
158
+ # 2. command info from network
159
+ assert title is None and session is None, 'params error: %s, %s, %s' % (content, title, session)
160
+ super().__init__(content)
161
+
162
+ @property
163
+ def title(self) -> str:
164
+ return self.get_str(key='title', default='')
165
+
166
+ @property
167
+ def session(self) -> Optional[str]:
168
+ return self.get_str(key='session')
169
+
170
+ @property
171
+ def state(self) -> HandshakeState:
172
+ return handshake_state(title=self.title, session=self.session)
155
173
  ```
156
174
 
157
175
  ### extends Content
@@ -165,13 +183,13 @@ from dimp import Content
165
183
  from dimp import BaseContent
166
184
 
167
185
 
168
- class CustomizedContent(Content, ABC):
186
+ class AppContent(Content, ABC):
169
187
  """
170
188
  Application Customized message
171
189
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
172
190
 
173
191
  data format: {
174
- type : 0xCC,
192
+ type : 0xA0,
175
193
  sn : 123,
176
194
 
177
195
  app : "{APP_ID}", // application (e.g.: "chat.dim.sechat")
@@ -199,22 +217,25 @@ class CustomizedContent(Content, ABC):
199
217
  """ Action Name """
200
218
  raise NotImplemented
201
219
 
220
+ #
221
+ # Factory
222
+ #
202
223
  @classmethod
203
224
  def create(cls, app: str, mod: str, act: str):
204
- return AppCustomizedContent(app=app, mod=mod, act=act)
225
+ return ApplicationContent(app=app, mod=mod, act=act)
205
226
 
206
227
 
207
- class AppCustomizedContent(BaseContent, CustomizedContent):
228
+ class ApplicationContent(BaseContent, AppContent):
208
229
 
209
230
  def __init__(self, content: Dict[str, Any] = None,
210
- msg_type: int = None,
231
+ msg_type: str = None,
211
232
  app: str = None, mod: str = None, act: str = None):
212
233
  if content is None:
213
234
  # 1. new content with type, application, module & action
214
235
  assert app is not None and mod is not None and act is not None, \
215
236
  'customized content error: %s, %s, %s, %s' % (msg_type, app, mod, act)
216
237
  if msg_type is None:
217
- msg_type = ContentType.CUSTOMIZED.value
238
+ msg_type = ContentType.APPLICATION
218
239
  super().__init__(None, msg_type)
219
240
  self['app'] = app
220
241
  self['mod'] = mod
@@ -244,5 +265,5 @@ class AppCustomizedContent(BaseContent, CustomizedContent):
244
265
 
245
266
  ----
246
267
 
247
- Copyright © 2018 Albert Moky
268
+ Copyright © 2018-2025 Albert Moky
248
269
  [![Followers](https://img.shields.io/github/followers/moky)](https://github.com/moky?tab=followers)
@@ -31,8 +31,9 @@
31
31
  3. (S-C) handshake success
32
32
 
33
33
  ```python
34
+ from abc import ABC, abstractmethod
34
35
  from enum import IntEnum
35
- from typing import Optional, Any, Dict
36
+ from typing import Optional, Dict
36
37
 
37
38
  from dimp import Command, BaseCommand
38
39
 
@@ -44,7 +45,20 @@ class HandshakeState(IntEnum):
44
45
  Success = 3 # S -> C, handshake accepted
45
46
 
46
47
 
47
- class HandshakeCommand(BaseCommand):
48
+ def handshake_state(title: str, session: str = None) -> HandshakeState:
49
+ # Server -> Client
50
+ if title == 'DIM!': # or title == 'OK!':
51
+ return HandshakeState.Success
52
+ if title == 'DIM?':
53
+ return HandshakeState.Again
54
+ # Client -> Server: "Hello world!"
55
+ if session is None or len(session) == 0:
56
+ return HandshakeState.Start
57
+ else:
58
+ return HandshakeState.Restart
59
+
60
+
61
+ class HandshakeCommand(Command, ABC):
48
62
  """
49
63
  Handshake Command
50
64
  ~~~~~~~~~~~~~~~~~
@@ -60,37 +74,24 @@ class HandshakeCommand(BaseCommand):
60
74
  """
61
75
  HANDSHAKE = 'handshake'
62
76
 
63
- def __init__(self, content: Dict[str, Any] = None, title: str = None, session: str = None):
64
- if content is None:
65
- # 1. new command with title & session key
66
- assert title is not None, 'handshake command error: %s' % session
67
- cmd = self.HANDSHAKE
68
- super().__init__(cmd=cmd)
69
- self['title'] = title
70
- self['message'] = title # TODO: remove after all clients upgraded
71
- if session is not None:
72
- self['session'] = session
73
- else:
74
- # 2. command info from network
75
- assert title is None and session is None, 'params error: %s, %s, %s' % (content, title, session)
76
- super().__init__(content)
77
-
78
77
  @property
78
+ @abstractmethod
79
79
  def title(self) -> str:
80
- # TODO: modify after all clients upgraded
81
- text = self.get_str(key='title', default=None)
82
- if text is None:
83
- # compatible with v1.0
84
- text = self.get_str(key='message', default='')
85
- return text
80
+ raise NotImplemented
86
81
 
87
82
  @property
83
+ @abstractmethod
88
84
  def session(self) -> Optional[str]:
89
- return self.get_str(key='session', default=None)
85
+ raise NotImplemented
90
86
 
91
87
  @property
88
+ @abstractmethod
92
89
  def state(self) -> HandshakeState:
93
- return handshake_state(title=self.title, session=self.session)
90
+ raise NotImplemented
91
+
92
+ #
93
+ # Factories
94
+ #
94
95
 
95
96
  @classmethod
96
97
  def offer(cls, session: str = None) -> Command:
@@ -100,7 +101,7 @@ class HandshakeCommand(BaseCommand):
100
101
  :param session: Old session key
101
102
  :return: HandshakeCommand object
102
103
  """
103
- return HandshakeCommand(title='Hello world!', session=session)
104
+ return BaseHandshakeCommand(title='Hello world!', session=session)
104
105
 
105
106
  @classmethod
106
107
  def ask(cls, session: str) -> Command:
@@ -110,7 +111,7 @@ class HandshakeCommand(BaseCommand):
110
111
  :param session: New session key
111
112
  :return: HandshakeCommand object
112
113
  """
113
- return HandshakeCommand(title='DIM?', session=session)
114
+ return BaseHandshakeCommand(title='DIM?', session=session)
114
115
 
115
116
  @classmethod
116
117
  def accepted(cls, session: str = None) -> Command:
@@ -119,7 +120,7 @@ class HandshakeCommand(BaseCommand):
119
120
 
120
121
  :return: HandshakeCommand object
121
122
  """
122
- return HandshakeCommand(title='DIM!', session=session)
123
+ return BaseHandshakeCommand(title='DIM!', session=session)
123
124
 
124
125
  start = offer # (1. C->S) first handshake, without session
125
126
  again = ask # (2. S->C) ask client to handshake with new session key
@@ -127,17 +128,34 @@ class HandshakeCommand(BaseCommand):
127
128
  success = accepted # (4. S->C) notice the client that handshake accepted
128
129
 
129
130
 
130
- def handshake_state(title: str, session: str = None) -> HandshakeState:
131
- # Server -> Client
132
- if title == 'DIM!': # or title == 'OK!':
133
- return HandshakeState.Success
134
- if title == 'DIM?':
135
- return HandshakeState.Again
136
- # Client -> Server: "Hello world!"
137
- if session is None or len(session) == 0:
138
- return HandshakeState.Start
139
- else:
140
- return HandshakeState.Restart
131
+ class BaseHandshakeCommand(BaseCommand, HandshakeCommand):
132
+
133
+ def __init__(self, content: Dict = None, title: str = None, session: str = None):
134
+ if content is None:
135
+ # 1. new command with title & session key
136
+ assert title is not None, 'handshake command error: %s' % session
137
+ cmd = self.HANDSHAKE
138
+ super().__init__(cmd=cmd)
139
+ self['title'] = title
140
+ self['message'] = title # TODO: remove after all clients upgraded
141
+ if session is not None:
142
+ self['session'] = session
143
+ else:
144
+ # 2. command info from network
145
+ assert title is None and session is None, 'params error: %s, %s, %s' % (content, title, session)
146
+ super().__init__(content)
147
+
148
+ @property
149
+ def title(self) -> str:
150
+ return self.get_str(key='title', default='')
151
+
152
+ @property
153
+ def session(self) -> Optional[str]:
154
+ return self.get_str(key='session')
155
+
156
+ @property
157
+ def state(self) -> HandshakeState:
158
+ return handshake_state(title=self.title, session=self.session)
141
159
  ```
142
160
 
143
161
  ### extends Content
@@ -151,13 +169,13 @@ from dimp import Content
151
169
  from dimp import BaseContent
152
170
 
153
171
 
154
- class CustomizedContent(Content, ABC):
172
+ class AppContent(Content, ABC):
155
173
  """
156
174
  Application Customized message
157
175
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
158
176
 
159
177
  data format: {
160
- type : 0xCC,
178
+ type : 0xA0,
161
179
  sn : 123,
162
180
 
163
181
  app : "{APP_ID}", // application (e.g.: "chat.dim.sechat")
@@ -185,22 +203,25 @@ class CustomizedContent(Content, ABC):
185
203
  """ Action Name """
186
204
  raise NotImplemented
187
205
 
206
+ #
207
+ # Factory
208
+ #
188
209
  @classmethod
189
210
  def create(cls, app: str, mod: str, act: str):
190
- return AppCustomizedContent(app=app, mod=mod, act=act)
211
+ return ApplicationContent(app=app, mod=mod, act=act)
191
212
 
192
213
 
193
- class AppCustomizedContent(BaseContent, CustomizedContent):
214
+ class ApplicationContent(BaseContent, AppContent):
194
215
 
195
216
  def __init__(self, content: Dict[str, Any] = None,
196
- msg_type: int = None,
217
+ msg_type: str = None,
197
218
  app: str = None, mod: str = None, act: str = None):
198
219
  if content is None:
199
220
  # 1. new content with type, application, module & action
200
221
  assert app is not None and mod is not None and act is not None, \
201
222
  'customized content error: %s, %s, %s, %s' % (msg_type, app, mod, act)
202
223
  if msg_type is None:
203
- msg_type = ContentType.CUSTOMIZED.value
224
+ msg_type = ContentType.APPLICATION
204
225
  super().__init__(None, msg_type)
205
226
  self['app'] = app
206
227
  self['mod'] = mod
@@ -230,5 +251,5 @@ class AppCustomizedContent(BaseContent, CustomizedContent):
230
251
 
231
252
  ----
232
253
 
233
- Copyright © 2018 Albert Moky
254
+ Copyright © 2018-2025 Albert Moky
234
255
  [![Followers](https://img.shields.io/github/followers/moky)](https://github.com/moky?tab=followers)
@@ -34,7 +34,7 @@ from mkm.crypto import *
34
34
  from mkm import *
35
35
  from dkd import *
36
36
 
37
- from .protocol.commands import CommandFactory
37
+ from .protocol.base import CommandFactory
38
38
  from .protocol import *
39
39
  from .crypto import *
40
40
  from .mkm import *
@@ -119,6 +119,8 @@ __all__ = [
119
119
  'Meta',
120
120
  'Document', 'Visa', 'Bulletin',
121
121
 
122
+ 'MetaType', 'DocumentType',
123
+
122
124
  'AddressFactory',
123
125
  'IDFactory',
124
126
  'MetaFactory',
@@ -132,8 +134,6 @@ __all__ = [
132
134
  'BaseMeta',
133
135
  'BaseDocument', 'BaseVisa', 'BaseBulletin',
134
136
 
135
- 'MetaType', 'DocumentType',
136
-
137
137
  #
138
138
  # DaoKeDao
139
139
  #
@@ -145,11 +145,12 @@ __all__ = [
145
145
  'InstantMessage', 'SecureMessage', 'ReliableMessage',
146
146
 
147
147
  # contents
148
- 'TextContent', 'ArrayContent', 'ForwardContent',
149
- 'PageContent', 'NameCard',
148
+ 'TextContent', 'PageContent', 'NameCard',
149
+ 'ForwardContent', 'CombineContent', 'ArrayContent',
150
150
  'FileContent', 'ImageContent', 'AudioContent', 'VideoContent',
151
151
  'MoneyContent', 'TransferContent',
152
- 'QuoteContent', 'CombineContent',
152
+ 'QuoteContent',
153
+ 'CustomizedContent',
153
154
 
154
155
  # commands
155
156
  'Command',
@@ -163,11 +164,12 @@ __all__ = [
163
164
 
164
165
  # extend contents
165
166
  'BaseContent',
166
- 'BaseTextContent', 'ListContent', 'SecretContent',
167
- 'WebPageContent', 'NameCardContent',
167
+ 'BaseTextContent', 'WebPageContent', 'NameCardContent',
168
+ 'SecretContent', 'CombineForwardContent', 'ListContent',
168
169
  'BaseFileContent', 'ImageFileContent', 'AudioFileContent', 'VideoFileContent',
169
170
  'BaseMoneyContent', 'TransferMoneyContent',
170
- 'BaseQuoteContent', 'CombineForwardContent',
171
+ 'BaseQuoteContent',
172
+ 'AppCustomizedContent',
171
173
 
172
174
  # extend commands
173
175
  'BaseCommand',
@@ -29,9 +29,9 @@
29
29
  # ==============================================================================
30
30
 
31
31
  from abc import ABC
32
- from typing import Any, Dict
32
+ from typing import Dict
33
33
 
34
- from mkm.types import Dictionary
34
+ from mkm.types import Mapper, Dictionary
35
35
  from mkm.crypto import CryptographyKey, EncryptKey, DecryptKey, SignKey, VerifyKey
36
36
  from mkm.crypto import SymmetricKey, AsymmetricKey, PublicKey, PrivateKey
37
37
  from mkm.plugins import SharedCryptoExtensions
@@ -46,7 +46,7 @@ from mkm.plugins import SharedCryptoExtensions
46
46
  # noinspection PyAbstractClass
47
47
  class BaseKey(Dictionary, CryptographyKey, ABC):
48
48
 
49
- def __init__(self, key: Dict[str, Any]):
49
+ def __init__(self, key: Dict):
50
50
  super().__init__(dictionary=key)
51
51
 
52
52
  @property # Override
@@ -58,9 +58,9 @@ class BaseKey(Dictionary, CryptographyKey, ABC):
58
58
  #
59
59
 
60
60
  @classmethod
61
- def get_key_algorithm(cls, key: Dict[str, Any]) -> str:
61
+ def get_key_algorithm(cls, key: Dict) -> str:
62
62
  ext = SharedCryptoExtensions()
63
- algorithm = ext.helper.get_key_algorithm(key=key, default=None)
63
+ algorithm = ext.helper.get_key_algorithm(key=key)
64
64
  return '' if algorithm is None else algorithm
65
65
 
66
66
  @classmethod
@@ -97,20 +97,32 @@ class BaseKey(Dictionary, CryptographyKey, ABC):
97
97
  # noinspection PyAbstractClass
98
98
  class BaseSymmetricKey(Dictionary, SymmetricKey, ABC):
99
99
 
100
- def __init__(self, key: Dict[str, Any]):
100
+ def __init__(self, key: Dict):
101
101
  super().__init__(dictionary=key)
102
102
 
103
103
  # Override
104
104
  def __eq__(self, other) -> bool:
105
- if isinstance(other, SymmetricKey):
106
- return BaseKey.symmetric_keys_equal(other, self)
105
+ if isinstance(other, Mapper):
106
+ if self is other:
107
+ # same object
108
+ return True
109
+ elif isinstance(other, SymmetricKey):
110
+ return BaseKey.symmetric_keys_equal(other, self)
111
+ # compare with inner map
112
+ other = other.dictionary
113
+ return self.dictionary.__eq__(other)
107
114
 
108
115
  # Override
109
116
  def __ne__(self, other) -> bool:
110
- if isinstance(other, SymmetricKey):
111
- return not BaseKey.symmetric_keys_equal(other, self)
112
- else:
113
- return True
117
+ if isinstance(other, Mapper):
118
+ if self is other:
119
+ # same object
120
+ return False
121
+ elif isinstance(other, SymmetricKey):
122
+ return not BaseKey.symmetric_keys_equal(other, self)
123
+ # compare with inner map
124
+ other = other.dictionary
125
+ return self.dictionary.__ne__(other)
114
126
 
115
127
  @property # Override
116
128
  def algorithm(self) -> str:
@@ -124,7 +136,7 @@ class BaseSymmetricKey(Dictionary, SymmetricKey, ABC):
124
136
  # noinspection PyAbstractClass
125
137
  class BaseAsymmetricKey(Dictionary, AsymmetricKey, ABC):
126
138
 
127
- def __init__(self, key: Dict[str, Any]):
139
+ def __init__(self, key: Dict):
128
140
  super().__init__(dictionary=key)
129
141
 
130
142
  @property # Override
@@ -135,7 +147,7 @@ class BaseAsymmetricKey(Dictionary, AsymmetricKey, ABC):
135
147
  # noinspection PyAbstractClass
136
148
  class BasePublicKey(Dictionary, PublicKey, ABC):
137
149
 
138
- def __init__(self, key: Dict[str, Any]):
150
+ def __init__(self, key: Dict):
139
151
  super().__init__(dictionary=key)
140
152
 
141
153
  @property # Override
@@ -150,20 +162,32 @@ class BasePublicKey(Dictionary, PublicKey, ABC):
150
162
  # noinspection PyAbstractClass
151
163
  class BasePrivateKey(Dictionary, PrivateKey, ABC):
152
164
 
153
- def __init__(self, key: Dict[str, Any]):
165
+ def __init__(self, key: Dict):
154
166
  super().__init__(dictionary=key)
155
167
 
156
168
  # Override
157
169
  def __eq__(self, other) -> bool:
158
- if isinstance(other, PrivateKey):
159
- return BaseKey.private_keys_equal(other, self)
170
+ if isinstance(other, Mapper):
171
+ if self is other:
172
+ # same object
173
+ return True
174
+ elif isinstance(other, PrivateKey):
175
+ return BaseKey.private_keys_equal(other, self)
176
+ # compare with inner map
177
+ other = other.dictionary
178
+ return self.dictionary.__eq__(other)
160
179
 
161
180
  # Override
162
181
  def __ne__(self, other) -> bool:
163
- if isinstance(other, PrivateKey):
164
- return not BaseKey.private_keys_equal(other, self)
165
- else:
166
- return True
182
+ if isinstance(other, Mapper):
183
+ if self is other:
184
+ # same object
185
+ return False
186
+ elif isinstance(other, PrivateKey):
187
+ return not BaseKey.private_keys_equal(other, self)
188
+ # compare with inner map
189
+ other = other.dictionary
190
+ return self.dictionary.__ne__(other)
167
191
 
168
192
  @property # Override
169
193
  def algorithm(self) -> str:
@@ -28,7 +28,7 @@
28
28
  # SOFTWARE.
29
29
  # ==============================================================================
30
30
 
31
- from typing import Optional, Any, Dict
31
+ from typing import Optional, Dict
32
32
 
33
33
  from mkm.format import TransportableData
34
34
  from mkm.types import URI
@@ -55,7 +55,7 @@ class BaseFileWrapper(Dictionary):
55
55
  }
56
56
  """
57
57
 
58
- def __init__(self, dictionary: Dict[str, Any]):
58
+ def __init__(self, dictionary: Dict):
59
59
  super().__init__(dictionary=dictionary)
60
60
  # file data (not encrypted)
61
61
  self.__attachment: Optional[TransportableData] = None
@@ -100,7 +100,7 @@ class BaseFileWrapper(Dictionary):
100
100
 
101
101
  @property
102
102
  def filename(self) -> Optional[str]:
103
- return self.get_str(key='filename', default=None)
103
+ return self.get_str(key='filename')
104
104
 
105
105
  @filename.setter
106
106
  def filename(self, name: Optional[str]):
@@ -115,11 +115,12 @@ class BaseFileWrapper(Dictionary):
115
115
 
116
116
  @property
117
117
  def url(self) -> Optional[URI]:
118
- if self.__url is None:
119
- remote = self.get_str(key='URL', default=None)
118
+ remote = self.__url
119
+ if remote is None:
120
+ remote = self.get_str(key='URL')
120
121
  # TODO: convert str to URI
121
122
  self.__url = remote
122
- return self.__url
123
+ return remote
123
124
 
124
125
  @url.setter
125
126
  def url(self, remote: Optional[URI]):
@@ -136,10 +137,12 @@ class BaseFileWrapper(Dictionary):
136
137
 
137
138
  @property
138
139
  def password(self) -> Optional[DecryptKey]:
139
- if self.__password is None:
140
+ key = self.__password
141
+ if key is None:
140
142
  info = self.get('key')
141
- self.__password = SymmetricKey.parse(key=info)
142
- return self.__password
143
+ key = SymmetricKey.parse(key=info)
144
+ self.__password = key
145
+ return key
143
146
 
144
147
  @password.setter
145
148
  def password(self, key: Optional[DecryptKey]):