source: src/mlx/rpc.py@ 923:fe2a902681bc

python3
Last change on this file since 923:fe2a902681bc was 923:fe2a902681bc, checked in by István Váradi <ivaradi@…>, 5 years ago

Eliminated calls to cmp() (re #347).

File size: 25.9 KB
RevLine 
[919]1from . import const
2from . import rpccommon
[745]3
[919]4from .common import MAVA_BASE_URL
[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
195 TYPECODE2TYPE = { "736" : const.AIRCRAFT_B736,
196 "73G" : const.AIRCRAFT_B737,
197 "738" : const.AIRCRAFT_B738,
198 "73H" : const.AIRCRAFT_B738C,
[790]199 "732" : const.AIRCRAFT_B732,
[745]200 "733" : const.AIRCRAFT_B733,
201 "734" : const.AIRCRAFT_B734,
202 "735" : const.AIRCRAFT_B735,
203 "DH4" : const.AIRCRAFT_DH8D,
204 "762" : const.AIRCRAFT_B762,
205 "763" : const.AIRCRAFT_B763,
206 "CR2" : const.AIRCRAFT_CRJ2,
207 "F70" : const.AIRCRAFT_F70,
208 "LI2" : const.AIRCRAFT_DC3,
209 "TU3" : const.AIRCRAFT_T134,
210 "TU5" : const.AIRCRAFT_T154,
211 "YK4" : const.AIRCRAFT_YK40,
212 "146" : const.AIRCRAFT_B462 }
213
214 # FIXME: copied from web.BookedFlight
[858]215 TYPE2TYPECODE = { const.AIRCRAFT_B736 : "736",
216 const.AIRCRAFT_B737 : "73G",
217 const.AIRCRAFT_B738 : "738",
218 const.AIRCRAFT_B738C : "73H",
219 const.AIRCRAFT_B732 : "732",
220 const.AIRCRAFT_B733 : "733",
221 const.AIRCRAFT_B734 : "734",
222 const.AIRCRAFT_B735 : "735",
223 const.AIRCRAFT_DH8D : "DH4",
224 const.AIRCRAFT_B762 : "762",
225 const.AIRCRAFT_B763 : "763",
226 const.AIRCRAFT_CRJ2 : "CR2",
227 const.AIRCRAFT_F70 : "F70",
228 const.AIRCRAFT_DC3 : "LI2",
229 const.AIRCRAFT_T134 : "TU3",
230 const.AIRCRAFT_T154 : "TU5",
231 const.AIRCRAFT_YK40 : "YK4",
232 const.AIRCRAFT_B462 : "146" }
233
234 # FIXME: copied from web.BookedFlight
[745]235 @staticmethod
236 def _decodeAircraftType(typeCode):
237 """Decode the aircraft type from the given typeCode."""
238 if typeCode in BookedFlight.TYPECODE2TYPE:
239 return BookedFlight.TYPECODE2TYPE[typeCode]
240 else:
241 raise Exception("Invalid aircraft type code: '" + typeCode + "'")
242
[809]243 @staticmethod
244 def _decodeStatus(status):
245 """Decode the status from the status string."""
246 if status=="booked":
247 return BookedFlight.STATUS_BOOKED
248 elif status=="reported":
249 return BookedFlight.STATUS_REPORTED
250 elif status=="accepted":
251 return BookedFlight.STATUS_ACCEPTED
252 elif status=="rejected":
253 return BookedFlight.STATUS_REJECTED
254 else:
255 raise Exception("Invalid flight status code: '" + status + "'")
256
[745]257 # FIXME: copied from web.BookedFlight
258 @staticmethod
259 def getDateTime(date, time):
260 """Get a datetime object from the given textual date and time."""
261 return datetime.datetime.strptime(date + " " + time,
262 "%Y-%m-%d %H:%M:%S")
263
[809]264 # FIXME: copied from web.BookedFlight
265 STATUS_BOOKED = 1
266
267 # FIXME: copied from web.BookedFlight
268 STATUS_REPORTED = 2
269
270 # FIXME: copied from web.BookedFlight
271 STATUS_ACCEPTED = 3
272
273 # FIXME: copied from web.BookedFlight
274 STATUS_REJECTED = 4
275
[745]276 # The instructions for the construction
277 _instructions = {
278 "numPassengers" : int,
279 "numCrew" : int,
280 "bagWeight" : int,
281 "cargoWeight" : int,
282 "mailWeight" : int,
[809]283 "aircraftType" : lambda value: BookedFlight._decodeAircraftType(value),
284 "status" : lambda value: BookedFlight._decodeStatus(value)
[745]285 }
286
287 def __init__(self, value):
288 """Construct the booked flight object from the given RPC result
289 value."""
[820]290 self.status = BookedFlight.STATUS_BOOKED
[745]291 super(BookedFlight, self).__init__(value, BookedFlight._instructions)
292 self.departureTime = \
293 BookedFlight.getDateTime(self.date, self.departureTime)
294 self.arrivalTime = \
295 BookedFlight.getDateTime(self.date, self.arrivalTime)
296 if self.arrivalTime<self.departureTime:
297 self.arrivalTime += datetime.timedelta(days = 1)
298
[889]299 def writeIntoFile(self, f):
300 """Write the flight into a file."""
[919]301 print("callsign=%s" % (self.callsign,), file=f)
[889]302 date = self.departureTime.date()
[919]303 print("date=%04d-%02d-%0d" % (date.year, date.month, date.day), file=f)
304 print("dep_airport=%s" % (self.departureICAO,), file=f)
305 print("dest_airport=%s" % (self.arrivalICAO,), file=f)
306 print("planecode=%s" % \
307 (BookedFlight.TYPE2TYPECODE[self.aircraftType],), file=f)
308 print("planetype=%s" % (self.aircraftTypeName,), file=f)
309 print("tail_nr=%s" % (self.tailNumber,), file=f)
310 print("passenger=%d" % (self.numPassengers,), file=f)
311 print("crew=%d" % (self.numCrew,), file=f)
312 print("bag=%d" % (self.bagWeight,), file=f)
313 print("cargo=%d" % (self.cargoWeight,), file=f)
314 print("mail=%d" % (self.mailWeight,), file=f)
315 print("flight_route=%s" % (self.route,), file=f)
[889]316 departureTime = self.departureTime
[919]317 print("departure_time=%02d\\:%02d\\:%02d" % \
318 (departureTime.hour, departureTime.minute, departureTime.second), file=f)
[889]319 arrivalTime = self.arrivalTime
[919]320 print("arrival_time=%02d\\:%02d\\:%02d" % \
321 (arrivalTime.hour, arrivalTime.minute, arrivalTime.second), file=f)
322 print("foglalas_id=%s" % ("0" if self.id is None else self.id,), file=f)
[889]323
[745]324#---------------------------------------------------------------------------------------
325
[854]326class AcceptedFlight(RPCObject):
327 """A flight that has been already accepted."""
328 # The instructions for the construction
329 @staticmethod
330 def parseTimestamp(s):
331 """Parse the given RPC timestamp."""
332 dt = datetime.datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
333 return calendar.timegm(dt.utctimetuple())
334
335 _instructions = {
336 "bookedFlight" : lambda value: BookedFlight(value),
337 "numPassengers" : int,
338 "fuelUsed" : int,
339 "rating" : lambda value: float(value) if value else 0.0
340 }
341
342 def __init__(self, value):
343 """Construct the booked flight object from the given RPC result
344 value."""
345 super(AcceptedFlight, self).__init__(value, AcceptedFlight._instructions)
346 self.flightTimeStart = \
347 AcceptedFlight.parseTimestamp(self.flightDate + " " +
348 self.flightTimeStart)
349 self.flightTimeEnd = \
350 AcceptedFlight.parseTimestamp(self.flightDate + " " +
351 self.flightTimeEnd)
352 if self.flightTimeEnd<self.flightTimeStart:
353 self.flightTimeEnd += 24*60*60
354
355#---------------------------------------------------------------------------------------
356
[745]357class Plane(rpccommon.Plane, RPCObject):
358 """An airplane in the fleet."""
359 _instructions = {
360 "status" : lambda value: rpccommon.Plane.str2status(value),
[859]361 "gateNumber" : lambda value: value if value else None,
362 "typeCode": lambda value: BookedFlight._decodeAircraftType(value)
[745]363 }
364
365 def __init__(self, value):
366 """Construct the plane."""
367 RPCObject.__init__(self, value, instructions = Plane._instructions)
[859]368 self.aircraftType = self.typeCode
369 del self.typeCode
[745]370
371#---------------------------------------------------------------------------------------
372
373class Fleet(rpccommon.Fleet):
374 """The fleet."""
375 def __init__(self, value):
376 """Construct the fleet."""
377 super(Fleet, self).__init__()
378 for planeValue in value:
379 self._addPlane(Plane(planeValue))
380
381#---------------------------------------------------------------------------------------
382
[756]383class Registration(object):
384 """Data for registration."""
[759]385 def __init__(self, surName, firstName, nameOrder,
386 yearOfBirth, emailAddress, emailAddressPublic,
[756]387 vatsimID, ivaoID, phoneNumber, nationality, password):
388 """Construct the registration data."""
[759]389 self.surName = surName
390 self.firstName = firstName
391 self.nameOrder = nameOrder
[756]392 self.yearOfBirth = yearOfBirth
393 self.emailAddress = emailAddress
394 self.emailAddressPublic = 1 if emailAddressPublic is True else \
395 0 if emailAddressPublic is False else emailAddressPublic
396 self.vatsimID = "" if vatsimID is None else vatsimID
397 self.ivaoID = "" if ivaoID is None else ivaoID
398 self.phoneNumber = phoneNumber
399 self.nationality = nationality
400 self.password = password
401
402#---------------------------------------------------------------------------------------
403
[745]404class RPCException(Exception):
405 """An exception thrown by RPC operations."""
406 def __init__(self, result, message = None):
407 """Construct the exception."""
408 self._result = result
409 if message is None:
410 message = "RPC call failed with result code: %d" % (result,)
411 super(RPCException, self).__init__(message)
412
413 @property
414 def result(self):
415 """Get the result code."""
416 return self._result
417
418#---------------------------------------------------------------------------------------
419
420class Client(object):
421 """The RPC client interface."""
[820]422 # The client protocol version
423 VERSION = 2
424
[745]425 # Result code: OK
426 RESULT_OK = 0
427
428 # Result code: the login has failed
429 RESULT_LOGIN_FAILED = 1
430
431 # Result code: the given session ID is unknown (it might have expired).
432 RESULT_SESSION_INVALID = 2
433
434 # Result code: some database error
435 RESULT_DATABASE_ERROR = 3
436
[756]437 # Result code: invalid data
438 RESULT_INVALID_DATA = 4
439
[745]440 # Result code: the flight does not exist
441 RESULT_FLIGHT_NOT_EXISTS = 101
442
443 # Result code: the flight has already been reported.
444 RESULT_FLIGHT_ALREADY_REPORTED = 102
445
[756]446 # Result code: a user with the given e-mail address already exists
447 RESULT_EMAIL_ALREADY_REGISTERED = 103
448
[745]449 def __init__(self, getCredentialsFn):
450 """Construct the client."""
451 self._getCredentialsFn = getCredentialsFn
452
453 self._server = jsonrpclib.Server(MAVA_BASE_URL + "/jsonrpc.php")
454
455 self._userName = None
456 self._passwordHash = None
457 self._sessionID = None
458 self._loginCount = 0
459
460 @property
461 def valid(self):
462 """Determine if the client is valid, i.e. there is a session ID
463 stored."""
464 return self._sessionID is not None
465
466 def setCredentials(self, userName, password):
467 """Set the credentials for future logins."""
468
469 self._userName = userName
470
471 md5 = hashlib.md5()
472 md5.update(password)
473 self._passwordHash = md5.hexdigest()
474
475 self._sessionID = None
476
[756]477 def register(self, registrationData):
478 """Register with the given data.
479
480 Returns a tuple of:
481 - the error code,
482 - the PID if there is no error."""
483 reply = Reply(self._server.register(registrationData))
484
485 return (reply.result,
486 reply.value["pid"] if reply.result==Client.RESULT_OK else None)
487
[745]488 def login(self):
489 """Login using the given previously set credentials.
490
491 The session ID is stored in the object and used for later calls.
492
[756]493 Returns the name of the pilot on success, or None on error."""
[745]494 self._sessionID = None
495
[820]496 reply = Reply(self._server.login(self._userName, self._passwordHash,
497 Client.VERSION))
[745]498 if reply.result == Client.RESULT_OK:
499 self._loginCount += 1
500 self._sessionID = reply.value["sessionID"]
[858]501
502 types = [BookedFlight.TYPECODE2TYPE[typeCode]
503 for typeCode in reply.value["typeCodes"]]
504
505 return (reply.value["name"], reply.value["rank"], types)
[745]506 else:
507 return None
508
509 def getFlights(self):
510 """Get the flights available for performing."""
[809]511 bookedFlights = []
512 reportedFlights = []
513 rejectedFlights = []
[745]514
515 value = self._performCall(lambda sessionID:
516 self._server.getFlights(sessionID))
517 for flightData in value:
[809]518 flight = BookedFlight(flightData)
519 if flight.status == BookedFlight.STATUS_BOOKED:
520 bookedFlights.append(flight)
521 elif flight.status == BookedFlight.STATUS_REPORTED:
522 reportedFlights.append(flight)
523 elif flight.status == BookedFlight.STATUS_REJECTED:
524 rejectedFlights.append(flight)
[745]525
[809]526 for flights in [bookedFlights, reportedFlights, rejectedFlights]:
[923]527 flights.sort(key = lambda flight: flight.departureTime)
[745]528
[809]529 return (bookedFlights, reportedFlights, rejectedFlights)
[745]530
[854]531 def getAcceptedFlights(self):
532 """Get the flights that are already accepted."""
533 value = self._performCall(lambda sessionID:
534 self._server.getAcceptedFlights(sessionID))
535 flights = []
536 for flight in value:
537 flights.append(AcceptedFlight(flight))
538 return flights
539
[761]540 def getEntryExamStatus(self):
541 """Get the status of the exams needed for joining MAVA."""
542 value = self._performCall(lambda sessionID:
543 self._server.getEntryExamStatus(sessionID))
[764]544 return (value["entryExamPassed"], value["entryExamLink"],
[769]545 value["checkFlightStatus"], value["madeFO"])
[761]546
[745]547 def getFleet(self):
548 """Query and return the fleet."""
549 value = self._performCall(lambda sessionID:
550 self._server.getFleet(sessionID))
551
552 return Fleet(value)
553
554 def updatePlane(self, tailNumber, status, gateNumber):
555 """Update the state and position of the plane with the given tail
556 number."""
557 status = rpccommon.Plane.status2str(status)
558 self._performCall(lambda sessionID:
559 self._server.updatePlane(sessionID, tailNumber,
560 status, gateNumber))
561
[853]562 def addPIREP(self, flightID, pirep, update = False):
[745]563 """Add the PIREP for the given flight."""
564 (result, _value) = \
565 self._performCall(lambda sessionID:
[853]566 self._server.addPIREP(sessionID, flightID, pirep,
567 update),
[745]568 acceptResults = [Client.RESULT_FLIGHT_ALREADY_REPORTED,
569 Client.RESULT_FLIGHT_NOT_EXISTS])
570 return result
571
572 def updateOnlineACARS(self, acars):
573 """Update the online ACARS from the given data."""
574 self._performCall(lambda sessionID:
575 self._server.updateOnlineACARS(sessionID, acars))
576
[769]577 def setCheckFlightPassed(self, type):
578 """Mark the check flight of the user passed with the given type."""
579 self._performCall(lambda sessionID:
580 self._server.setCheckFlightPassed(sessionID, type))
581
[829]582 def getPIREP(self, flightID):
583 """Get the PIREP data for the flight with the given ID."""
584 value = self._performCall(lambda sessionID:
585 self._server.getPIREP(sessionID, flightID))
586 return value
587
[821]588 def reflyFlights(self, flightIDs):
589 """Mark the flights with the given IDs for reflying."""
590 self._performCall(lambda sessionID:
591 self._server.reflyFlights(sessionID, flightIDs))
592
[824]593 def deleteFlights(self, flightIDs):
594 """Delete the flights with the given IDs."""
595 self._performCall(lambda sessionID:
596 self._server.deleteFlights(sessionID, flightIDs))
597
[858]598 def getTimetable(self, date, types = None):
599 """Get the time table for the given date restricted to the given list
600 of type codes, if any."""
601 typeCodes = None if types is None else \
602 [BookedFlight.TYPE2TYPECODE[type] for type in types]
603
604 values = self._performCall(lambda sessionID:
605 self._server.getTimetable(sessionID,
606 date.strftime("%Y-%m-%d"),
[859]607 date.weekday()+1,
[858]608 typeCodes))
609 return ScheduledFlightPair.scheduledFlights2Pairs([ScheduledFlight(value)
[859]610 for value in values],
611 date)
612
613 def bookFlights(self, flightIDs, date, tailNumber):
614 """Book the flights with the given IDs on the given date to be flown
615 with the plane of the given tail number."""
616 values = self._performCall(lambda sessionID:
617 self._server.bookFlights(sessionID,
618 flightIDs,
619 date.strftime("%Y-%m-%d"),
620 tailNumber))
621 return [BookedFlight(value) for value in values]
[858]622
[745]623 def _performCall(self, callFn, acceptResults = []):
624 """Perform a call using the given call function.
625
626 acceptResults should be a list of result codes that should be accepted
627 besides RESULT_OK. If this list is not empty, the returned value is a
628 tuple of the result code and the corresponding value. Otherwise only
629 RESULT_OK is accepted, and the value is returned.
630
631 All other error codes are converted to exceptions."""
632 numAttempts = 0
633 while True:
634 reply = Reply(callFn(self._ensureSession()))
635 numAttempts += 1
636 result = reply.result
637 if result==Client.RESULT_SESSION_INVALID:
638 self._sessionID = None
639 if numAttempts==3:
640 raise RPCException(result)
641 elif result!=Client.RESULT_OK and result not in acceptResults:
642 raise RPCException(result)
643 elif acceptResults:
644 return (result, reply.value)
645 else:
646 return reply.value
647
648 def _ensureSession(self):
649 """Ensure that there is a valid session ID."""
650 while self._sessionID is None:
651 if self._userName is not None and self._passwordHash is not None:
652 if not self.login():
653 self._userName = self._passwordHash = None
654
655 if self._userName is None or self._passwordHash is None:
656 (self._userName, password) = self._getCredentialsFn()
657 if self._userName is None:
658 raise RPCException(Client.RESULT_LOGIN_FAILED)
659
660 md5 = hashlib.md5()
661 md5.update(password)
662 self._passwordHash = md5.hexdigest()
663
664 return self._sessionID
665
666#---------------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.