source: src/mlx/rpc.py@ 1032:36f152c70cd7

python3
Last change on this file since 1032:36f152c70cd7 was 1032:36f152c70cd7, checked in by István Váradi <ivaradi@…>, 2 years ago

Updated aircraft type constants (re #357).

File size: 26.2 KB
RevLine 
[919]1from . import const
2from . import rpccommon
[745]3
[955]4from .common import MAVA_BASE_URL, fixUnpickled
[745]5
6import jsonrpclib
7import hashlib
8import datetime
[854]9import calendar
10import sys
[745]11
12#---------------------------------------------------------------------------------------
13
14class RPCObject(object):
15 """Base class for objects read from RPC calls.
16
17 It is possible to construct it from a dictionary."""
18 def __init__(self, value, instructions = {}):
19 """Construct the object.
20
21 value is the dictionary returned by the call.
22
23 info is a mapping from names to 'instructions' on what to do with the
24 corresponding values. If the instruction is None, it will be ignored.
25 If the instruction is a function, the value will be passed to it and
26 the return value will be stored in the object.
27
28 For all other names, the value will be stored as the same-named
29 attribute."""
[919]30 for (key, value) in value.items():
[745]31 if key in instructions:
32 instruction = instructions[key]
33 if instruction is None:
34 continue
35
[854]36 try:
37 value = instruction(value)
38 except:
[919]39 print("Failed to convert value '%s' of attribute '%s':" % \
40 (value, key), file=sys.stderr)
[854]41 import traceback
42 traceback.print_exc()
[745]43 setattr(self, key, value)
44
45#---------------------------------------------------------------------------------------
46
47class Reply(RPCObject):
48 """The generic reply structure."""
49
50#---------------------------------------------------------------------------------------
51
[858]52class ScheduledFlight(RPCObject):
53 """A scheduled flight in the time table."""
54 # The instructions for the construction
55 # Type: normal flight
56 TYPE_NORMAL = 0
57
58 # Type: VIP flight
59 TYPE_VIP = 1
60
61 _instructions = {
62 "id" : int,
63 "pairID": int,
64 "typeCode": lambda value: BookedFlight._decodeAircraftType(value),
65 "departureTime": lambda value: ScheduledFlight._decodeTime(value),
66 "arrivalTime": lambda value: ScheduledFlight._decodeTime(value),
67 "duration": lambda value: ScheduledFlight._decodeDuration(value),
[859]68 "spec": int,
69 "validFrom": lambda value: ScheduledFlight._decodeDate(value),
70 "validTo": lambda value: ScheduledFlight._decodeDate(value),
71 "date": lambda value: ScheduledFlight._decodeDate(value)
[858]72 }
73
74 @staticmethod
75 def _decodeTime(value):
76 """Decode the given value as a time value."""
77 return datetime.datetime.strptime(value, "%H:%M:%S").time()
78
79 @staticmethod
[859]80 def _decodeDate(value):
81 """Decode the given value as a date value."""
82 if not value or value=="0000-00-00":
83 return const.defaultDate
84 else:
85 return datetime.datetime.strptime(value, "%Y-%m-%d").date()
86
87 @staticmethod
[858]88 def _decodeDuration(value):
89 """Decode the given value as a duration.
90
91 A number of seconds will be returned."""
92 t = datetime.datetime.strptime(value, "%H:%M:%S")
93 return (t.hour*60 + t.minute) * 60 + t.second
94
95 def __init__(self, value):
96 """Construct the scheduled flight object from the given JSON value."""
97 super(ScheduledFlight, self).__init__(value,
98 ScheduledFlight._instructions)
99 self.aircraftType = self.typeCode
100 del self.typeCode
101
[859]102 self.type = ScheduledFlight.TYPE_VIP if self.spec==1 \
103 else ScheduledFlight.TYPE_NORMAL
104 del self.spec
105
106 def compareBy(self, other, name):
107 """Compare this flight with the other one according to the given
108 attribute name."""
109 if name=="callsign":
110 try:
111 cs1 = int(self.callsign[2:])
112 cs2 = int(other.callsign[2:])
[923]113 return 0 if cs1==cs2 else -1 if cs1<cs2 else 1
[859]114 except:
[923]115 return 0 if self.callsign==other.callsign \
116 else -1 if self.callsign<other.callsign else 1
[859]117 else:
[923]118 v1 = getattr(self, name)
119 v2 = getattr(other, name)
120 return 0 if v1==v2 else -1 if v1<v2 else 1
[859]121
[858]122 def __repr__(self):
123 return "ScheduledFlight<%d, %d, %s, %s (%s) - %s (%s) -> %d, %d>" % \
124 (self.id, self.pairID, BookedFlight.TYPE2TYPECODE[self.aircraftType],
125 self.departureICAO, str(self.departureTime),
126 self.arrivalICAO, str(self.arrivalTime),
[859]127 self.duration, self.type)
[858]128
129#---------------------------------------------------------------------------------------
130
131class ScheduledFlightPair(object):
132 """A pair of scheduled flights.
133
134 Occasionally, one of the flights may be missing."""
135 @staticmethod
[859]136 def scheduledFlights2Pairs(scheduledFlights, date):
[858]137 """Convert the given list of scheduled flights into a list of flight
138 pairs."""
[859]139 weekday = str(date.weekday()+1)
140
[858]141 flights = {}
[859]142 weekdayFlights = {}
[858]143 for flight in scheduledFlights:
144 flights[flight.id] = flight
[859]145 if (flight.type==ScheduledFlight.TYPE_NORMAL and
146 flight.arrivalICAO!="LHBP" and weekday in flight.days and
147 flight.validFrom<=date and flight.validTo>=date) or \
148 flight.type==ScheduledFlight.TYPE_VIP:
149 weekdayFlights[flight.id] = flight
[858]150
151 flightPairs = []
152
[859]153 while weekdayFlights:
154 (id, flight) = weekdayFlights.popitem()
155 if flight.type==ScheduledFlight.TYPE_NORMAL:
156 pairID = flight.pairID
157 if pairID in flights:
158 pairFlight = flights[pairID]
159 if flight.departureICAO=="LHBP" or \
160 (pairFlight.departureICAO!="LHBP" and
161 flight.callsign<pairFlight.callsign):
162 flightPairs.append(ScheduledFlightPair(flight, pairFlight))
163 else:
164 flightPairs.append(ScheduledFlightPair(pairFlight, flight))
165 del flights[pairID]
166 if pairID in weekdayFlights:
167 del weekdayFlights[pairID]
168 elif flight.type==ScheduledFlight.TYPE_VIP:
[858]169 flightPairs.append(ScheduledFlightPair(flight))
170
[923]171 flightPairs.sort(key = lambda pair: pair.flight0.date)
[859]172
[858]173 return flightPairs
174
175 def __init__(self, flight0, flight1 = None):
176 """Construct the pair with the given flights."""
177 self.flight0 = flight0
178 self.flight1 = flight1
179
[859]180 def compareBy(self, other, name):
181 """Compare this flight pair with the other one according to the given
182 attribute name, considering the first flights."""
183 return self.flight0.compareBy(other.flight0, name)
184
185 def __repr__(self):
186 return "ScheduledFlightPair<%s, %s, %s>" % \
187 (self.flight0.callsign, self.flight0.departureICAO,
188 self.flight0.arrivalICAO)
189
[858]190#---------------------------------------------------------------------------------------
191
[745]192class BookedFlight(RPCObject):
193 """A booked flight."""
194 # FIXME: copied from web.BookedFlight
[1032]195 TYPECODE2TYPE = { "B736" : const.AIRCRAFT_B736,
196 "B737" : const.AIRCRAFT_B737,
197 "B738" : const.AIRCRAFT_B738,
198 "B73H" : const.AIRCRAFT_B738C,
199 "B732" : const.AIRCRAFT_B732,
200 "B733" : const.AIRCRAFT_B733,
201 "B734" : const.AIRCRAFT_B734,
202 "B735" : const.AIRCRAFT_B735,
203 "DH8D" : const.AIRCRAFT_DH8D,
204 "B762" : const.AIRCRAFT_B762,
205 "B763" : const.AIRCRAFT_B763,
206 "CRJ2" : const.AIRCRAFT_CRJ2,
[745]207 "F70" : const.AIRCRAFT_F70,
208 "LI2" : const.AIRCRAFT_DC3,
[1032]209 "T134" : const.AIRCRAFT_T134,
210 "T154" : const.AIRCRAFT_T154,
211 "YK40" : const.AIRCRAFT_YK40,
212 "B462" : const.AIRCRAFT_B462,
213 "IL62" : const.AIRCRAFT_IL62 }
[745]214
215 # FIXME: copied from web.BookedFlight
[1032]216 TYPE2TYPECODE = { const.AIRCRAFT_B736 : "B736",
217 const.AIRCRAFT_B737 : "B737",
218 const.AIRCRAFT_B738 : "B738",
219 const.AIRCRAFT_B738C : "B73H",
220 const.AIRCRAFT_B732 : "B732",
221 const.AIRCRAFT_B733 : "B733",
222 const.AIRCRAFT_B734 : "B734",
223 const.AIRCRAFT_B735 : "B735",
224 const.AIRCRAFT_DH8D : "DH8D",
225 const.AIRCRAFT_B762 : "B762",
226 const.AIRCRAFT_B763 : "B763",
227 const.AIRCRAFT_CRJ2 : "CRJ2",
[858]228 const.AIRCRAFT_F70 : "F70",
229 const.AIRCRAFT_DC3 : "LI2",
[1032]230 const.AIRCRAFT_T134 : "T134",
231 const.AIRCRAFT_T154 : "T155",
232 const.AIRCRAFT_YK40 : "YK40",
233 const.AIRCRAFT_B462 : "B462",
234 const.AIRCRAFT_IL62 : "IL62" }
[858]235
236 # FIXME: copied from web.BookedFlight
[745]237 @staticmethod
238 def _decodeAircraftType(typeCode):
239 """Decode the aircraft type from the given typeCode."""
240 if typeCode in BookedFlight.TYPECODE2TYPE:
241 return BookedFlight.TYPECODE2TYPE[typeCode]
242 else:
243 raise Exception("Invalid aircraft type code: '" + typeCode + "'")
244
[809]245 @staticmethod
246 def _decodeStatus(status):
247 """Decode the status from the status string."""
248 if status=="booked":
249 return BookedFlight.STATUS_BOOKED
250 elif status=="reported":
251 return BookedFlight.STATUS_REPORTED
252 elif status=="accepted":
253 return BookedFlight.STATUS_ACCEPTED
254 elif status=="rejected":
255 return BookedFlight.STATUS_REJECTED
256 else:
257 raise Exception("Invalid flight status code: '" + status + "'")
258
[745]259 # FIXME: copied from web.BookedFlight
260 @staticmethod
261 def getDateTime(date, time):
262 """Get a datetime object from the given textual date and time."""
263 return datetime.datetime.strptime(date + " " + time,
264 "%Y-%m-%d %H:%M:%S")
265
[809]266 # FIXME: copied from web.BookedFlight
267 STATUS_BOOKED = 1
268
269 # FIXME: copied from web.BookedFlight
270 STATUS_REPORTED = 2
271
272 # FIXME: copied from web.BookedFlight
273 STATUS_ACCEPTED = 3
274
275 # FIXME: copied from web.BookedFlight
276 STATUS_REJECTED = 4
277
[745]278 # The instructions for the construction
279 _instructions = {
280 "numPassengers" : int,
281 "numCrew" : int,
282 "bagWeight" : int,
283 "cargoWeight" : int,
284 "mailWeight" : int,
[809]285 "aircraftType" : lambda value: BookedFlight._decodeAircraftType(value),
286 "status" : lambda value: BookedFlight._decodeStatus(value)
[745]287 }
288
289 def __init__(self, value):
290 """Construct the booked flight object from the given RPC result
291 value."""
[820]292 self.status = BookedFlight.STATUS_BOOKED
[745]293 super(BookedFlight, self).__init__(value, BookedFlight._instructions)
294 self.departureTime = \
295 BookedFlight.getDateTime(self.date, self.departureTime)
296 self.arrivalTime = \
297 BookedFlight.getDateTime(self.date, self.arrivalTime)
298 if self.arrivalTime<self.departureTime:
299 self.arrivalTime += datetime.timedelta(days = 1)
300
[889]301 def writeIntoFile(self, f):
302 """Write the flight into a file."""
[919]303 print("callsign=%s" % (self.callsign,), file=f)
[889]304 date = self.departureTime.date()
[919]305 print("date=%04d-%02d-%0d" % (date.year, date.month, date.day), file=f)
306 print("dep_airport=%s" % (self.departureICAO,), file=f)
307 print("dest_airport=%s" % (self.arrivalICAO,), file=f)
308 print("planecode=%s" % \
309 (BookedFlight.TYPE2TYPECODE[self.aircraftType],), file=f)
310 print("planetype=%s" % (self.aircraftTypeName,), file=f)
311 print("tail_nr=%s" % (self.tailNumber,), file=f)
312 print("passenger=%d" % (self.numPassengers,), file=f)
313 print("crew=%d" % (self.numCrew,), file=f)
314 print("bag=%d" % (self.bagWeight,), file=f)
315 print("cargo=%d" % (self.cargoWeight,), file=f)
316 print("mail=%d" % (self.mailWeight,), file=f)
317 print("flight_route=%s" % (self.route,), file=f)
[889]318 departureTime = self.departureTime
[919]319 print("departure_time=%02d\\:%02d\\:%02d" % \
320 (departureTime.hour, departureTime.minute, departureTime.second), file=f)
[889]321 arrivalTime = self.arrivalTime
[919]322 print("arrival_time=%02d\\:%02d\\:%02d" % \
323 (arrivalTime.hour, arrivalTime.minute, arrivalTime.second), file=f)
324 print("foglalas_id=%s" % ("0" if self.id is None else self.id,), file=f)
[889]325
[955]326 def __setstate__(self, state):
327 """Set the state from the given unpickled dictionary."""
328 self.__dict__.update(fixUnpickled(state))
329
[745]330#---------------------------------------------------------------------------------------
331
[854]332class AcceptedFlight(RPCObject):
333 """A flight that has been already accepted."""
334 # The instructions for the construction
335 @staticmethod
336 def parseTimestamp(s):
337 """Parse the given RPC timestamp."""
338 dt = datetime.datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
339 return calendar.timegm(dt.utctimetuple())
340
341 _instructions = {
342 "bookedFlight" : lambda value: BookedFlight(value),
343 "numPassengers" : int,
344 "fuelUsed" : int,
345 "rating" : lambda value: float(value) if value else 0.0
346 }
347
348 def __init__(self, value):
349 """Construct the booked flight object from the given RPC result
350 value."""
351 super(AcceptedFlight, self).__init__(value, AcceptedFlight._instructions)
352 self.flightTimeStart = \
353 AcceptedFlight.parseTimestamp(self.flightDate + " " +
354 self.flightTimeStart)
355 self.flightTimeEnd = \
356 AcceptedFlight.parseTimestamp(self.flightDate + " " +
357 self.flightTimeEnd)
358 if self.flightTimeEnd<self.flightTimeStart:
359 self.flightTimeEnd += 24*60*60
360
361#---------------------------------------------------------------------------------------
362
[745]363class Plane(rpccommon.Plane, RPCObject):
364 """An airplane in the fleet."""
365 _instructions = {
366 "status" : lambda value: rpccommon.Plane.str2status(value),
[859]367 "gateNumber" : lambda value: value if value else None,
368 "typeCode": lambda value: BookedFlight._decodeAircraftType(value)
[745]369 }
370
371 def __init__(self, value):
372 """Construct the plane."""
373 RPCObject.__init__(self, value, instructions = Plane._instructions)
[859]374 self.aircraftType = self.typeCode
375 del self.typeCode
[745]376
377#---------------------------------------------------------------------------------------
378
379class Fleet(rpccommon.Fleet):
380 """The fleet."""
381 def __init__(self, value):
382 """Construct the fleet."""
383 super(Fleet, self).__init__()
384 for planeValue in value:
385 self._addPlane(Plane(planeValue))
386
387#---------------------------------------------------------------------------------------
388
[756]389class Registration(object):
390 """Data for registration."""
[759]391 def __init__(self, surName, firstName, nameOrder,
392 yearOfBirth, emailAddress, emailAddressPublic,
[756]393 vatsimID, ivaoID, phoneNumber, nationality, password):
394 """Construct the registration data."""
[759]395 self.surName = surName
396 self.firstName = firstName
397 self.nameOrder = nameOrder
[756]398 self.yearOfBirth = yearOfBirth
399 self.emailAddress = emailAddress
400 self.emailAddressPublic = 1 if emailAddressPublic is True else \
401 0 if emailAddressPublic is False else emailAddressPublic
402 self.vatsimID = "" if vatsimID is None else vatsimID
403 self.ivaoID = "" if ivaoID is None else ivaoID
404 self.phoneNumber = phoneNumber
405 self.nationality = nationality
406 self.password = password
407
408#---------------------------------------------------------------------------------------
409
[745]410class RPCException(Exception):
411 """An exception thrown by RPC operations."""
412 def __init__(self, result, message = None):
413 """Construct the exception."""
414 self._result = result
415 if message is None:
416 message = "RPC call failed with result code: %d" % (result,)
417 super(RPCException, self).__init__(message)
418
419 @property
420 def result(self):
421 """Get the result code."""
422 return self._result
423
424#---------------------------------------------------------------------------------------
425
426class Client(object):
427 """The RPC client interface."""
[820]428 # The client protocol version
429 VERSION = 2
430
[745]431 # Result code: OK
432 RESULT_OK = 0
433
434 # Result code: the login has failed
435 RESULT_LOGIN_FAILED = 1
436
437 # Result code: the given session ID is unknown (it might have expired).
438 RESULT_SESSION_INVALID = 2
439
440 # Result code: some database error
441 RESULT_DATABASE_ERROR = 3
442
[756]443 # Result code: invalid data
444 RESULT_INVALID_DATA = 4
445
[745]446 # Result code: the flight does not exist
447 RESULT_FLIGHT_NOT_EXISTS = 101
448
449 # Result code: the flight has already been reported.
450 RESULT_FLIGHT_ALREADY_REPORTED = 102
451
[756]452 # Result code: a user with the given e-mail address already exists
453 RESULT_EMAIL_ALREADY_REGISTERED = 103
454
[745]455 def __init__(self, getCredentialsFn):
456 """Construct the client."""
457 self._getCredentialsFn = getCredentialsFn
458
459 self._server = jsonrpclib.Server(MAVA_BASE_URL + "/jsonrpc.php")
460
461 self._userName = None
462 self._passwordHash = None
463 self._sessionID = None
464 self._loginCount = 0
465
466 @property
467 def valid(self):
468 """Determine if the client is valid, i.e. there is a session ID
469 stored."""
470 return self._sessionID is not None
471
472 def setCredentials(self, userName, password):
473 """Set the credentials for future logins."""
474
475 self._userName = userName
476
477 md5 = hashlib.md5()
[928]478 md5.update(password.encode("utf-8"))
[745]479 self._passwordHash = md5.hexdigest()
480
481 self._sessionID = None
482
[756]483 def register(self, registrationData):
484 """Register with the given data.
485
486 Returns a tuple of:
487 - the error code,
488 - the PID if there is no error."""
489 reply = Reply(self._server.register(registrationData))
490
491 return (reply.result,
492 reply.value["pid"] if reply.result==Client.RESULT_OK else None)
493
[745]494 def login(self):
495 """Login using the given previously set credentials.
496
497 The session ID is stored in the object and used for later calls.
498
[756]499 Returns the name of the pilot on success, or None on error."""
[745]500 self._sessionID = None
501
[820]502 reply = Reply(self._server.login(self._userName, self._passwordHash,
503 Client.VERSION))
[745]504 if reply.result == Client.RESULT_OK:
505 self._loginCount += 1
506 self._sessionID = reply.value["sessionID"]
[858]507
508 types = [BookedFlight.TYPECODE2TYPE[typeCode]
509 for typeCode in reply.value["typeCodes"]]
510
511 return (reply.value["name"], reply.value["rank"], types)
[745]512 else:
513 return None
514
515 def getFlights(self):
516 """Get the flights available for performing."""
[809]517 bookedFlights = []
518 reportedFlights = []
519 rejectedFlights = []
[745]520
521 value = self._performCall(lambda sessionID:
522 self._server.getFlights(sessionID))
523 for flightData in value:
[809]524 flight = BookedFlight(flightData)
525 if flight.status == BookedFlight.STATUS_BOOKED:
526 bookedFlights.append(flight)
527 elif flight.status == BookedFlight.STATUS_REPORTED:
528 reportedFlights.append(flight)
529 elif flight.status == BookedFlight.STATUS_REJECTED:
530 rejectedFlights.append(flight)
[745]531
[809]532 for flights in [bookedFlights, reportedFlights, rejectedFlights]:
[923]533 flights.sort(key = lambda flight: flight.departureTime)
[745]534
[809]535 return (bookedFlights, reportedFlights, rejectedFlights)
[745]536
[854]537 def getAcceptedFlights(self):
538 """Get the flights that are already accepted."""
539 value = self._performCall(lambda sessionID:
540 self._server.getAcceptedFlights(sessionID))
541 flights = []
542 for flight in value:
543 flights.append(AcceptedFlight(flight))
544 return flights
545
[761]546 def getEntryExamStatus(self):
547 """Get the status of the exams needed for joining MAVA."""
548 value = self._performCall(lambda sessionID:
549 self._server.getEntryExamStatus(sessionID))
[764]550 return (value["entryExamPassed"], value["entryExamLink"],
[769]551 value["checkFlightStatus"], value["madeFO"])
[761]552
[745]553 def getFleet(self):
554 """Query and return the fleet."""
555 value = self._performCall(lambda sessionID:
556 self._server.getFleet(sessionID))
557
558 return Fleet(value)
559
560 def updatePlane(self, tailNumber, status, gateNumber):
561 """Update the state and position of the plane with the given tail
562 number."""
563 status = rpccommon.Plane.status2str(status)
564 self._performCall(lambda sessionID:
565 self._server.updatePlane(sessionID, tailNumber,
566 status, gateNumber))
567
[853]568 def addPIREP(self, flightID, pirep, update = False):
[745]569 """Add the PIREP for the given flight."""
570 (result, _value) = \
571 self._performCall(lambda sessionID:
[853]572 self._server.addPIREP(sessionID, flightID, pirep,
573 update),
[745]574 acceptResults = [Client.RESULT_FLIGHT_ALREADY_REPORTED,
575 Client.RESULT_FLIGHT_NOT_EXISTS])
576 return result
577
578 def updateOnlineACARS(self, acars):
579 """Update the online ACARS from the given data."""
580 self._performCall(lambda sessionID:
581 self._server.updateOnlineACARS(sessionID, acars))
582
[769]583 def setCheckFlightPassed(self, type):
584 """Mark the check flight of the user passed with the given type."""
585 self._performCall(lambda sessionID:
586 self._server.setCheckFlightPassed(sessionID, type))
587
[829]588 def getPIREP(self, flightID):
589 """Get the PIREP data for the flight with the given ID."""
590 value = self._performCall(lambda sessionID:
591 self._server.getPIREP(sessionID, flightID))
592 return value
593
[821]594 def reflyFlights(self, flightIDs):
595 """Mark the flights with the given IDs for reflying."""
596 self._performCall(lambda sessionID:
597 self._server.reflyFlights(sessionID, flightIDs))
598
[824]599 def deleteFlights(self, flightIDs):
600 """Delete the flights with the given IDs."""
601 self._performCall(lambda sessionID:
602 self._server.deleteFlights(sessionID, flightIDs))
603
[858]604 def getTimetable(self, date, types = None):
605 """Get the time table for the given date restricted to the given list
606 of type codes, if any."""
607 typeCodes = None if types is None else \
608 [BookedFlight.TYPE2TYPECODE[type] for type in types]
609
610 values = self._performCall(lambda sessionID:
611 self._server.getTimetable(sessionID,
612 date.strftime("%Y-%m-%d"),
[859]613 date.weekday()+1,
[858]614 typeCodes))
615 return ScheduledFlightPair.scheduledFlights2Pairs([ScheduledFlight(value)
[859]616 for value in values],
617 date)
618
619 def bookFlights(self, flightIDs, date, tailNumber):
620 """Book the flights with the given IDs on the given date to be flown
621 with the plane of the given tail number."""
622 values = self._performCall(lambda sessionID:
623 self._server.bookFlights(sessionID,
624 flightIDs,
625 date.strftime("%Y-%m-%d"),
626 tailNumber))
627 return [BookedFlight(value) for value in values]
[858]628
[745]629 def _performCall(self, callFn, acceptResults = []):
630 """Perform a call using the given call function.
631
632 acceptResults should be a list of result codes that should be accepted
633 besides RESULT_OK. If this list is not empty, the returned value is a
634 tuple of the result code and the corresponding value. Otherwise only
635 RESULT_OK is accepted, and the value is returned.
636
637 All other error codes are converted to exceptions."""
638 numAttempts = 0
639 while True:
640 reply = Reply(callFn(self._ensureSession()))
641 numAttempts += 1
642 result = reply.result
643 if result==Client.RESULT_SESSION_INVALID:
644 self._sessionID = None
645 if numAttempts==3:
646 raise RPCException(result)
647 elif result!=Client.RESULT_OK and result not in acceptResults:
648 raise RPCException(result)
649 elif acceptResults:
650 return (result, reply.value)
651 else:
652 return reply.value
653
654 def _ensureSession(self):
655 """Ensure that there is a valid session ID."""
656 while self._sessionID is None:
657 if self._userName is not None and self._passwordHash is not None:
658 if not self.login():
659 self._userName = self._passwordHash = None
660
661 if self._userName is None or self._passwordHash is None:
662 (self._userName, password) = self._getCredentialsFn()
663 if self._userName is None:
664 raise RPCException(Client.RESULT_LOGIN_FAILED)
665
666 md5 = hashlib.md5()
667 md5.update(password)
668 self._passwordHash = md5.hexdigest()
669
670 return self._sessionID
671
672#---------------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.