2      """Holds the state of a list of selected Yadis services, managing 
  3      storing it in a session and iterating over the services in order.""" 
  4   
  5 -    def __init__(self, starting_url, yadis_url, services, session_key): 
   6           
  7          self.starting_url = starting_url 
  8   
  9           
 10          self.yadis_url = yadis_url 
 11   
 12           
 13          self.services = list(services) 
 14   
 15          self.session_key = session_key 
 16   
 17           
 18          self._current = None 
  19   
 21          """How many untried services remain?""" 
 22          return len(self.services) 
  23   
 26   
 28          """Return the next service 
 29   
 30          self.current() will continue to return that service until the 
 31          next call to this method.""" 
 32          try: 
 33              self._current = self.services.pop(0) 
 34          except IndexError: 
 35              raise StopIteration 
 36          else: 
 37              return self._current 
  38   
 40          """Return the current service. 
 41   
 42          Returns None if there are no services left. 
 43          """ 
 44          return self._current 
  45   
 47          return url in [self.starting_url, self.yadis_url] 
  48   
 50          """Has the first service been returned?""" 
 51          return self._current is not None 
  52   
 53 -    def store(self, session): 
  54          """Store this object in the session, by its session key.""" 
 55          session[self.session_key] = self 
   56   
 58      """State management for discovery. 
 59   
 60      High-level usage pattern is to call .getNextService(discover) in 
 61      order to find the next available service for this user for this 
 62      session. Once a request completes, call .finish() to clean up the 
 63      session state. 
 64   
 65      @ivar session: a dict-like object that stores state unique to the 
 66          requesting user-agent. This object must be able to store 
 67          serializable objects. 
 68   
 69      @ivar url: the URL that is used to make the discovery request 
 70   
 71      @ivar session_key_suffix: The suffix that will be used to identify 
 72          this object in the session object. 
 73      """ 
 74   
 75      DEFAULT_SUFFIX = 'auth' 
 76      PREFIX = '_yadis_services_' 
 77   
 78 -    def __init__(self, session, url, session_key_suffix=None): 
  79          """Initialize a discovery object""" 
 80          self.session = session 
 81          self.url = url 
 82          if session_key_suffix is None: 
 83              session_key_suffix = self.DEFAULT_SUFFIX 
 84   
 85          self.session_key_suffix = session_key_suffix 
  86   
 88          """Return the next authentication service for the pair of 
 89          user_input and session.  This function handles fallback. 
 90   
 91   
 92          @param discover: a callable that takes a URL and returns a 
 93              list of services 
 94   
 95          @type discover: str -> [service] 
 96   
 97   
 98          @return: the next available service 
 99          """ 
100          manager = self.getManager() 
101          if manager is not None and not manager: 
102              self.destroyManager() 
103   
104          if not manager: 
105              yadis_url, services = discover(self.url) 
106              manager = self.createManager(services, yadis_url) 
107   
108          if manager: 
109              service = manager.next() 
110              manager.store(self.session) 
111          else: 
112              service = None 
113   
114          return service 
 115   
117          """Clean up Yadis-related services in the session and return 
118          the most-recently-attempted service from the manager, if one 
119          exists. 
120   
121          @param force: True if the manager should be deleted regardless 
122          of whether it's a manager for self.url. 
123   
124          @return: current service endpoint object or None if there is 
125              no current service 
126          """ 
127          manager = self.getManager(force=force) 
128          if manager is not None: 
129              service = manager.current() 
130              self.destroyManager(force=force) 
131          else: 
132              service = None 
133   
134          return service 
 135   
136       
137   
139          """Get the session key for this starting URL and suffix 
140   
141          @return: The session key 
142          @rtype: str 
143          """ 
144          return self.PREFIX + self.session_key_suffix 
 145   
147          """Extract the YadisServiceManager for this object's URL and 
148          suffix from the session. 
149   
150          @param force: True if the manager should be returned 
151          regardless of whether it's a manager for self.url. 
152   
153          @return: The current YadisServiceManager, if it's for this 
154              URL, or else None 
155          """ 
156          manager = self.session.get(self.getSessionKey()) 
157          if (manager is not None and (manager.forURL(self.url) or force)): 
158              return manager 
159          else: 
160              return None 
 161   
163          """Create a new YadisService Manager for this starting URL and 
164          suffix, and store it in the session. 
165   
166          @raises KeyError: When I already have a manager. 
167   
168          @return: A new YadisServiceManager or None 
169          """ 
170          key = self.getSessionKey() 
171          if self.getManager(): 
172              raise KeyError('There is already a %r manager for %r' % 
173                             (key, self.url)) 
174   
175          if not services: 
176              return None 
177   
178          manager = YadisServiceManager(self.url, yadis_url, services, key) 
179          manager.store(self.session) 
180          return manager 
 181   
183          """Delete any YadisServiceManager with this starting URL and 
184          suffix from the session. 
185   
186          If there is no service manager or the service manager is for a 
187          different URL, it silently does nothing. 
188   
189          @param force: True if the manager should be deleted regardless 
190          of whether it's a manager for self.url. 
191          """ 
192          if self.getManager(force=force) is not None: 
193              key = self.getSessionKey() 
194              del self.session[key] 
  195