source: geniuswebcore/geniusweb/protocol/session/DefaultSessionState.py@ 78

Last change on this file since 78 was 73, checked in by Bart Vastenhouw, 3 years ago

Fix for IssueValue hashcode.

File size: 8.4 KB
Line 
1from __future__ import annotations
2from abc import abstractmethod
3from typing import List, Dict, Optional, TypeVar, Generic
4
5from geniusweb.actions.Action import Action
6from geniusweb.actions.PartyId import PartyId
7from geniusweb.progress.Progress import Progress
8from geniusweb.protocol.ProtocolException import ProtocolException
9from geniusweb.protocol.session.SessionSettings import SessionSettings
10from geniusweb.protocol.session.SessionState import SessionState
11from geniusweb.references.PartyWithProfile import PartyWithProfile
12from geniusweb.utils import toTuple, val
13
14
15S = TypeVar('S',bound=SessionSettings)
16P = TypeVar('P',bound="DefaultSessionState")
17
18
19class DefaultSessionState (SessionState, Generic[P,S]):
20 '''
21 The default current state of the session. immutable.
22
23 @param <P> the actual SessionState object
24 @param <S> the actual SessionSettings object
25 '''
26
27 # error: Optional[ProtocolException]
28 # progress: Progress
29 # partyprofiles: Optional[Dict[PartyId, PartyWithProfile]]
30 # settings: S
31
32
33 def __init__(self, actions:List[Action], connections:List[PartyId],
34 progress:Optional[Progress], settings:S,
35 partyprofiles:Optional[Dict[PartyId, PartyWithProfile]]=None,
36 error:Optional[ProtocolException]=None):
37 '''
38 @param actions value for actions done so far. None equals to empty
39 list
40 @param conns the currently existing connections. Can be empty. If
41 None it is assumed to be empty. Each connection
42 represents another party. Normally there is exactly
43 1 connection for every party. The protocol should
44 check this.
45 @param progr the {@link Progress} that governs this session. Can
46 be null if session did not yet start.
47 @param settings the settings used for the session
48 @param partyprofiles map with the {@link PartyWithProfile} for connected
49 parties. None is equivalent to an empty map.
50 @param e the exception that occured, usually None. All errors
51 occuring due to faulty {@link Action}s translate to
52 {@link ProtocolException}s. All errors in our own
53 code are bugs (not ProtocolExceptions) and should
54 result in a throw and terminate the session.
55 '''
56
57 self._partyprofiles = dict(partyprofiles) if partyprofiles else {}
58
59 self._connections = list(connections) if connections else []
60
61 self._actions = list(actions) if actions else []
62
63 if len(self._connections) != len(set(self._connections)):
64 raise ValueError("There can not be multiple connections for a party:"
65 + str(self._connections))
66
67 if not settings:
68 raise ValueError("Settings must be not null");
69 self._progress = progress;
70 self._settings = settings;
71 self._error = error;
72
73 @abstractmethod
74 def With(self,actions1:List[Action] , conns:List[PartyId],
75 progress1:Optional[Progress] , settings1:S,
76 partyprofiles1:Dict[PartyId, PartyWithProfile] , e: Optional[ProtocolException]) -> P :
77 '''
78 Construct a new session state, where the DefaultSessionState changes
79 while the rest of the state remains unchanged. Notice the return type P.
80
81 @param actions1 the new {@link Action}s
82 @param conns the new connected {@link PartyId}s
83 @param progress1 the new {@link Progress}, can be None still
84 @param settings1 the new {@link SessionSettings}. Normally this is
85 constant during a session.
86 @param partyprofiles1 the new {@link PartyWithProfile}s for all parties.
87 Normally this remains constant during a session.
88 @param e an error that occured that caused the session to
89 reach its terminated/final state. If an error does
90 not cause termination, only a warning should be
91 logged.
92 @return the new state of the derived sessionstate.
93 '''
94
95 def getConnections(self)->List[PartyId] :
96 '''
97 @return existing connections.
98 '''
99 return list(self._connections)
100
101 def getPartyProfiles(self)->Dict[PartyId, PartyWithProfile] :
102 '''
103 @return map with {@link PartyWithProfile} for the parties. May be an
104 incomplete map, as more parties with their profiles may be set
105 only later.
106 '''
107 return dict(self._partyprofiles)
108
109 def getActions(self)->List[Action] :
110 '''
111 @return unmodifyable list of actions done so far.
112 '''
113 return list(self._actions)
114
115 def getProgress(self)->Optional[Progress] :
116 return self._progress
117
118 def getSettings(self)->S :
119 return self._settings
120
121 def isFinal(self,currentTimeMs:int) ->bool :
122 return self._error != None or \
123 (self._progress!=None and self._progress.isPastDeadline(currentTimeMs)) #type:ignore
124
125 def getError(self)-> Optional[ProtocolException]:
126 '''
127 @return an error that occured that caused the session to
128 reach its terminated/final state. If an error does
129 not cause termination, only a warning should be
130 logged. None if no error occured.
131 '''
132 return self._error
133
134 def WithDeadlineReached(self)->P :
135 return self.With(self._actions, self._connections, val(self._progress), self._settings,
136 self._partyprofiles, self._error)
137
138 def WithoutParty(self, party:PartyId )->P :
139 assert isinstance(party, PartyId)
140 newconn = list(self._connections)
141 newconn.remove(party);
142 return self.With(self._actions, newconn, val(self._progress), \
143 self._settings, self._partyprofiles, self._error)
144
145 def WithException(self, e:ProtocolException)-> P:
146 '''
147 @param e the error that occured
148 @return a new state with the error set/updated.
149 '''
150 assert isinstance(e, ProtocolException)
151 return self.With(self._actions, self._connections, val(self._progress),
152 self._settings, self._partyprofiles, e)
153
154 def WithParty(self, connection:PartyId, partyprofile:PartyWithProfile) -> P :
155 assert isinstance(connection, PartyId)
156 assert isinstance(partyprofile, PartyWithProfile)
157 newconns = list(self.getConnections())
158 newconns.append(connection)
159 newprofiles = dict(self.getPartyProfiles())
160 newprofiles[connection]= partyprofile
161 return self.With(self.getActions(), newconns, self._progress, self.getSettings(),
162 newprofiles, None)
163
164 def WithProgress(self,newprogress:Progress )->P :
165 '''
166 Sets the new progress for this session.
167
168 @param newprogress the new progress
169 @return new SAOPState with the progress set to new value
170 '''
171 assert isinstance(newprogress, Progress)
172 if not newprogress:
173 raise ValueError("newprogress must be not null");
174 return self.With(self._actions, self._connections, newprogress, self._settings,
175 self._partyprofiles,self._error)
176
177 def WithAction(self,actor:PartyId , action:Action )->P :
178 '''
179 @param actor the actor that did this action. Can be used to check if
180 action is valid. NOTICE caller has to make sure the current
181 state is not final.
182 @param action the action that was proposed by actor.
183 @return new SAOPState with the action added as last action.
184 '''
185 msg = self.checkAction(actor, action)
186 if msg:
187 raise ValueError(msg)
188
189 newactions = list(self.getActions())
190 newactions.append(action)
191 return self.With(newactions, self._connections, val(self._progress),
192 self._settings, self._partyprofiles,self._error)
193
194 def checkAction(self, actor:PartyId , action:Action) -> Optional[str]:
195 '''
196 @param actor the known real actor that did this action
197 @param action an {@link Action}
198 @return null if action seems ok, or message explaining why not.
199 '''
200 if not actor :
201 return "actor must not be null"
202 if not action:
203 return "action is null"
204 if actor != action.getActor():
205 return "act contains wrong credentials: " + str(action)
206 return None
207
208 def __repr__(self)->str:
209 return type(self).__name__ + "[" + str(self._actions) + ","\
210 + str(self._connections) + "," + str(self._progress) + "," + \
211 str(self._settings) + "," + str(self._error);
212
213 def __hash__(self):
214 return hash((tuple(self._actions), tuple(self._connections),
215 toTuple(self._partyprofiles),
216 self._progress, self._settings, self._error))
217
218 def __eq__(self, other) -> bool:
219 return isinstance(other, self.__class__) and \
220 self._partyprofiles == other._partyprofiles and \
221 self._actions==other._actions and \
222 self._connections == other._connections and \
223 self._progress == other._progress and \
224 self._settings == other._settings and \
225 self._error == other._error
226
Note: See TracBrowser for help on using the repository browser.