A `semaphore `__. A semaphore holds an integer value, which can be incremented by calling :meth:`release` and decremented by calling :meth:`acquire` – but the value is never allowed to drop below zero. If the value is zero, then
| 439 | |
| 440 | @final |
| 441 | class Semaphore(AsyncContextManagerMixin): |
| 442 | """A `semaphore <https://en.wikipedia.org/wiki/Semaphore_(programming)>`__. |
| 443 | |
| 444 | A semaphore holds an integer value, which can be incremented by |
| 445 | calling :meth:`release` and decremented by calling :meth:`acquire` – but |
| 446 | the value is never allowed to drop below zero. If the value is zero, then |
| 447 | :meth:`acquire` will block until someone calls :meth:`release`. |
| 448 | |
| 449 | If you're looking for a :class:`Semaphore` to limit the number of tasks |
| 450 | that can access some resource simultaneously, then consider using a |
| 451 | :class:`CapacityLimiter` instead. |
| 452 | |
| 453 | This object's interface is similar to, but different from, that of |
| 454 | :class:`threading.Semaphore`. |
| 455 | |
| 456 | A :class:`Semaphore` object can be used as an async context manager; it |
| 457 | blocks on entry but not on exit. |
| 458 | |
| 459 | Args: |
| 460 | initial_value (int): A non-negative integer giving semaphore's initial |
| 461 | value. |
| 462 | max_value (int or None): If given, makes this a "bounded" semaphore that |
| 463 | raises an error if the value is about to exceed the given |
| 464 | ``max_value``. |
| 465 | |
| 466 | """ |
| 467 | |
| 468 | def __init__(self, initial_value: int, *, max_value: int | None = None) -> None: |
| 469 | if not isinstance(initial_value, int): |
| 470 | raise TypeError("initial_value must be an int") |
| 471 | if initial_value < 0: |
| 472 | raise ValueError("initial value must be >= 0") |
| 473 | if max_value is not None: |
| 474 | if not isinstance(max_value, int): |
| 475 | raise TypeError("max_value must be None or an int") |
| 476 | if max_value < initial_value: |
| 477 | raise ValueError("max_values must be >= initial_value") |
| 478 | |
| 479 | # Invariants: |
| 480 | # bool(self._lot) implies self._value == 0 |
| 481 | # (or equivalently: self._value > 0 implies not self._lot) |
| 482 | self._lot = trio.lowlevel.ParkingLot() |
| 483 | self._value = initial_value |
| 484 | self._max_value = max_value |
| 485 | |
| 486 | def __repr__(self) -> str: |
| 487 | if self._max_value is None: |
| 488 | max_value_str = "" |
| 489 | else: |
| 490 | max_value_str = f", max_value={self._max_value}" |
| 491 | return f"<trio.Semaphore({self._value}{max_value_str}) at {id(self):#x}>" |
| 492 | |
| 493 | @property |
| 494 | def value(self) -> int: |
| 495 | """The current value of the semaphore.""" |
| 496 | return self._value |
| 497 | |
| 498 | @property |
no outgoing calls
searching dependent graphs…