@@ -155,9 +155,11 @@ def __init__(self, *, loop=None):
155155 self ._loop = events .get_event_loop ()
156156
157157 def __repr__ (self ):
158- # TODO: add waiters:N if > 0.
159158 res = super ().__repr__ ()
160- return '<{} [{}]>' .format (res [1 :- 1 ], 'set' if self ._value else 'unset' )
159+ extra = 'set' if self ._value else 'unset'
160+ if self ._waiters :
161+ extra = '{},waiters:{}' .format (extra , len (self ._waiters ))
162+ return '<{} [{}]>' .format (res [1 :- 1 ], extra )
161163
162164 def is_set (self ):
163165 """Return true if and only if the internal flag is true."""
@@ -201,20 +203,38 @@ def wait(self):
201203 self ._waiters .remove (fut )
202204
203205
204- # TODO: Why is this a Lock subclass? threading.Condition *has* a lock.
205- class Condition (Lock ):
206- """A Condition implementation.
206+ class Condition :
207+ """A Condition implementation, our equivalent to threading.Condition.
207208
208209 This class implements condition variable objects. A condition variable
209210 allows one or more coroutines to wait until they are notified by another
210211 coroutine.
212+
213+ A new Lock object is created and used as the underlying lock.
211214 """
212215
213216 def __init__ (self , * , loop = None ):
214- super ().__init__ (loop = loop )
215- self ._condition_waiters = collections .deque ()
217+ if loop is not None :
218+ self ._loop = loop
219+ else :
220+ self ._loop = events .get_event_loop ()
216221
217- # TODO: Add __repr__() with len(_condition_waiters).
222+ # Lock as an attribute as in threading.Condition.
223+ lock = Lock (loop = self ._loop )
224+ self ._lock = lock
225+ # Export the lock's locked(), acquire() and release() methods.
226+ self .locked = lock .locked
227+ self .acquire = lock .acquire
228+ self .release = lock .release
229+
230+ self ._waiters = collections .deque ()
231+
232+ def __repr__ (self ):
233+ res = super ().__repr__ ()
234+ extra = 'locked' if self .locked () else 'unlocked'
235+ if self ._waiters :
236+ extra = '{},waiters:{}' .format (extra , len (self ._waiters ))
237+ return '<{} [{}]>' .format (res [1 :- 1 ], extra )
218238
219239 @tasks .coroutine
220240 def wait (self ):
@@ -228,19 +248,19 @@ def wait(self):
228248 the same condition variable in another coroutine. Once
229249 awakened, it re-acquires the lock and returns True.
230250 """
231- if not self ._locked :
251+ if not self .locked () :
232252 raise RuntimeError ('cannot wait on un-acquired lock' )
233253
234254 keep_lock = True
235255 self .release ()
236256 try :
237257 fut = futures .Future (loop = self ._loop )
238- self ._condition_waiters .append (fut )
258+ self ._waiters .append (fut )
239259 try :
240260 yield from fut
241261 return True
242262 finally :
243- self ._condition_waiters .remove (fut )
263+ self ._waiters .remove (fut )
244264
245265 except GeneratorExit :
246266 keep_lock = False # Prevent yield in finally clause.
@@ -275,11 +295,11 @@ def notify(self, n=1):
275295 wait() call until it can reacquire the lock. Since notify() does
276296 not release the lock, its caller should.
277297 """
278- if not self ._locked :
298+ if not self .locked () :
279299 raise RuntimeError ('cannot notify on un-acquired lock' )
280300
281301 idx = 0
282- for fut in self ._condition_waiters :
302+ for fut in self ._waiters :
283303 if idx >= n :
284304 break
285305
@@ -293,7 +313,17 @@ def notify_all(self):
293313 calling thread has not acquired the lock when this method is called,
294314 a RuntimeError is raised.
295315 """
296- self .notify (len (self ._condition_waiters ))
316+ self .notify (len (self ._waiters ))
317+
318+ def __enter__ (self ):
319+ return self ._lock .__enter__ ()
320+
321+ def __exit__ (self , * args ):
322+ return self ._lock .__exit__ (* args )
323+
324+ def __iter__ (self ):
325+ yield from self .acquire ()
326+ return self
297327
298328
299329class Semaphore :
@@ -310,10 +340,10 @@ class Semaphore:
310340 counter; it defaults to 1. If the value given is less than 0,
311341 ValueError is raised.
312342
313- The second optional argument determins can semophore be released more than
314- initial internal counter value; it defaults to False. If the value given
315- is True and number of release() is more than number of successfull
316- acquire() calls ValueError is raised.
343+ The second optional argument determines if the semaphore can be released
344+ more than initial internal counter value; it defaults to False. If the
345+ value given is True and number of release() is more than number of
346+ successful acquire() calls ValueError is raised.
317347 """
318348
319349 def __init__ (self , value = 1 , bound = False , * , loop = None ):
@@ -330,12 +360,12 @@ def __init__(self, value=1, bound=False, *, loop=None):
330360 self ._loop = events .get_event_loop ()
331361
332362 def __repr__ (self ):
333- # TODO: add waiters:N if > 0.
334363 res = super ().__repr__ ()
335- return '<{} [{}]>' .format (
336- res [1 :- 1 ],
337- 'locked' if self ._locked else 'unlocked,value:{}' .format (
338- self ._value ))
364+ extra = 'locked' if self ._locked else 'unlocked,value:{}' .format (
365+ self ._value )
366+ if self ._waiters :
367+ extra = '{},waiters:{}' .format (extra , len (self ._waiters ))
368+ return '<{} [{}]>' .format (res [1 :- 1 ], extra )
339369
340370 def locked (self ):
341371 """Returns True if semaphore can not be acquired immediately."""
@@ -373,7 +403,7 @@ def release(self):
373403 When it was zero on entry and another coroutine is waiting for it to
374404 become larger than zero again, wake up that coroutine.
375405
376- If Semaphore is create with "bound" paramter equals true, then
406+ If Semaphore is created with "bound" parameter equals true, then
377407 release() method checks to make sure its current value doesn't exceed
378408 its initial value. If it does, ValueError is raised.
379409 """
0 commit comments