# We maintain our own collections.pytd, rather than using collections.pyi from
# typeshed, because this file makes use of mutable type parameters.
import sys
from typing import Any, Callable, DefaultDict, Dict, Generic, List, Mapping, MutableMapping, Optional, Protocol, Reversible, SupportsIndex, Text, Tuple, TypeAlias, Union, overload

K = TypeVar("K")
V = TypeVar("V")
K2 = TypeVar("K2")
V2 = TypeVar("V2")
DV = TypeVar("DV")
T = TypeVar("T")
T2 = TypeVar("T2")

__all__ = ...  # type: list[Any]
_abcoll = ...  # type: Any
_chain = ...  # type: type
_class_template = ...  # type: str
_eq = ...  # type: Any
_field_template = ...  # type: str
_get_ident = ...  # type: Any
_heapq = ...  # type: Any
_imap = ...  # type: type
_iskeyword = ...  # type: Any
_itemgetter = ...  # type: Callable
_repeat = ...  # type: type
_repr_template = ...  # type: str
_starmap = ...  # type: type
_sys = ...  # type: module
dumps = ...  # type: Callable
loads = ...  # type: Callable

class deque(typing.Deque[T]): ...

class SupportsDunderLT(Protocol[T]):
    def __lt__(self, __other: T) -> bool: ...

class SupportsDunderGT(Protocol[T]):
    def __gt__(self, __other: T) -> bool: ...

SupportsRichComparison: TypeAlias = SupportsDunderLT[Any] | SupportsDunderGT[Any]
SupportsRichComparisonT = TypeVar("SupportsRichComparisonT", bound=SupportsRichComparison)

AsyncGenerator = typing.AsyncGenerator
AsyncIterable = typing.AsyncIterable
AsyncIterator = typing.AsyncIterator
Awaitable = typing.Awaitable
ByteString = typing.ByteString
Callable = typing.Callable
ChainMap = typing.ChainMap
Collection = typing.Collection
Container = typing.Container
Coroutine = typing.Coroutine
Counter = typing.Counter
Generator = typing.Generator
Hashable = typing.Hashable
ItemsView = typing.ItemsView
Iterable = typing.Iterable
Iterator = typing.Iterator
KeysView = typing.KeysView
Mapping = typing.Mapping
MappingView = typing.MappingView
MutableMapping = typing.MutableMapping
MutableSequence = typing.MutableSequence
MutableSet = typing.MutableSet
OrderedDict = typing.OrderedDict
Reversible = typing.Reversible
Sequence = typing.Sequence
Set = typing.Set
Sized = typing.Sized
ValuesView = typing.ValuesView

def namedtuple(typename: Text,
  field_names: Union[Text, Iterable[Text], Iterable[nothing]],
  *,
  rename: bool = ..., defaults: List[Any] = ..., module = ...) -> type: ...

class defaultdict(DefaultDict[K, V]):
  default_factory = ...  # type: Optional[Callable[[], V]]
  def __init__(self):
    self = defaultdict[nothing, nothing]
  def __init__(self, default_factory: None):
    self = defaultdict[nothing, nothing]
  def __init__(self, default_factory: Callable[[], DV]) -> None:
    self = defaultdict[nothing, DV]
  def __init__(self, **kwargs: V2):
    self = defaultdict[str, V2]
  def __init__(self, default_factory: None, **kwargs: V2) -> None:
    self = defaultdict[str, V2]
  def __init__(self, default_factory: None, iter: Iterable[Tuple[K, V]], **kwargs: V2) -> None:
    self = defaultdict[K, Union[V, V2]]
  def __init__(self, default_factory: None, map: Mapping[K, V], **kwargs: V2) -> None:
    self = defaultdict[K, Union[V, V2]]
  def __init__(self, default_factory: Callable[[], DV], **kwargs: V2) -> None:
    self = defaultdict[str, Union[DV, V2]]
  def __init__(self, default_factory: Callable[[], DV], map: Mapping[K, V], **kwargs: V2) -> None:
    self = defaultdict[K, Union[DV, V, V2]]
  def __init__(self, default_factory: Callable[[], DV], iter: Iterable[Tuple[K, V]], **kwargs: V2) -> None:
    self = defaultdict[K, Union[DV, V, V2]]
  def __missing__(self, key: K) -> V: ...
  def __getitem__(self, key: K2) -> Any:
    self = defaultdict[Union[K, K2], Any]

class UserDict(MutableMapping[K, V]):
  data: dict[K, V]

  # __init__ should be kept roughly in line with `dict.__init__`, which has the same semantics
  @overload
  def __init__(self) -> NoneType:
    self = UserDict[nothing, nothing]
  @overload
  def __init__(self, list: list[nothing]) -> NoneType:
    self = UserDict[nothing, nothing]
  @overload
  def __init__(self, mapping: Mapping[K2, V2]) -> NoneType:
    self = UserDict[K2, V2]
  @overload
  def __init__(self, iterable: Iterable[tuple[K2, V2]]) -> NoneType:
    self = UserDict[K2, V2]
  @overload
  def __init__(self, *args, **kwargs) -> NoneType:
    self = UserDict[Any, Any]
  def __len__(self) -> int: ...
  def __getitem__(self, key: K) -> V: ...
  def __setitem__(self, key: K2, item: V2) -> None:
    self = UserDict[K | K2, V | V2]
  def __delitem__(self, key: K) -> None: ...
  def __iter__(self) -> Iterator[K]: ...
  def __contains__(self, key: object) -> bool: ...
  def copy(self: T) -> T: ...
  def __copy__(self: T) -> T: ...

  # `UserDict.fromkeys` has the same semantics as `dict.fromkeys`, so should be kept in line with `dict.fromkeys`.
  # TODO: Much like `dict.fromkeys`, the true signature of `UserDict.fromkeys` is inexpressible in the current type system.
  # See #3800 & https://github.com/python/typing/issues/548#issuecomment-683336963.
  @staticmethod
  @overload
  def fromkeys(keys: Iterable[K]) -> UserDict[K, None]: ...
  @staticmethod
  @overload
  def fromkeys(keys: Iterable[K], value: V) -> UserDict[K, V]: ...
  if sys.version_info >= (3, 9):
    def __or__(self, other: UserDict[K2, V2] | dict[K2, V2]) -> UserDict[K | K2, V | V2]: ...
    def __ror__(self, other: UserDict[K2, V2] | dict[K2, V2]) -> UserDict[K | K2, V | V2]: ...
    # UserDict.__ior__ should be kept roughly in line with MutableMapping.update()
    @overload
    def __ior__(self, other: Mapping[K2, V2]) -> None:
      self = UserDict[K | K2, V | V2]
    @overload
    def __ior__(self, other: Iterable[tuple[K2, V2]]) -> None:
      self = UserDict[K | K2, V | V2]

class UserList(MutableSequence[T]):
  data: list[T]
  @overload
  def __init__(self) -> NoneType:
    self = UserList[nothing]
  @overload
  def __init__(self, list: list[nothing]) -> NoneType:
    self = UserList[nothing]
  @overload
  def __init__(self, iterable: Iterable[T]) -> NoneType:
    self = UserList[T]
  def __lt__(self, other: list[T] | UserList[T] ) -> bool: ...
  def __le__(self, other: list[T] | UserList[T] ) -> bool: ...
  def __gt__(self, other: list[T] | UserList[T] ) -> bool: ...
  def __ge__(self, other: list[T] | UserList[T] ) -> bool: ...
  def __eq__(self, other: object ) -> bool: ...
  def __contains__(self, item: object) -> bool: ...
  def __len__(self) -> int: ...
  @overload
  def __getitem__(self, i: SupportsIndex) -> T: ...
  @overload
  def __getitem__(self, i: slice) -> UserList[T]: ...
  @overload
  def __setitem__(self, i: SupportsIndex, item: T2) -> None:
    self = UserList[T | T2]
  @overload
  def __setitem__(self, i: slice, item: Iterable[T2]) -> None:
    self = UserList[T | T2]
  def __delitem__(self, i: SupportsIndex | slice) -> None: ...
  def __add__(self, other: Iterable[T2]) -> UserList[T | T2]: ...
  def __radd__(self, other: Iterable[T2]) -> UserList[T | T2]: ...
  def __iadd__(self, other: Iterable[T2]) -> UserList[T | T2]:
    self = UserList[T | T2]
  def __mul__(self, n: int) -> UserList[T]: ...
  def __rmul__(self, n: int) -> UserList[T]: ...
  def __imul__(self, n: int) -> UserList[T]: ...
  def append(self, item: T) -> None: ...
  def insert(self, i: int, item: T) -> None: ...
  def pop(self, i: int = -1) -> T: ...
  def remove(self, item: T) -> None: ...
  def copy(self: T) -> T: ...
  def __copy__(self: T) -> T: ...
  def count(self, item: T) -> int: ...
  # All arguments are passed to `list.index` at runtime, so the signature should be kept in line with `list.index`.
  def index(self, item: T, __start: SupportsIndex = 0, __stop: SupportsIndex = sys.maxsize) -> int: ...
  # All arguments are passed to `list.sort` at runtime, so the signature should be kept in line with `list.sort`.
  @overload
  def sort(self: UserList[SupportsRichComparisonT], *, key: None = None, reverse: bool = False) -> None: ...
  @overload
  def sort(self, *, key: Callable[[T], SupportsRichComparison], reverse: bool = False) -> None: ...
  def extend(self, other: Iterable[T]) -> None: ...

class UserString(Sequence[UserString]):
  data: str
  def __init__(self, seq: object) -> None: ...
  def __int__(self) -> int: ...
  def __float__(self) -> float: ...
  def __complex__(self) -> complex: ...
  def __getnewargs__(self) -> tuple[str]: ...
  def __lt__(self, string: str | UserString) -> bool: ...
  def __le__(self, string: str | UserString) -> bool: ...
  def __gt__(self, string: str | UserString) -> bool: ...
  def __ge__(self, string: str | UserString) -> bool: ...
  def __eq__(self, string: object) -> bool: ...
  def __hash__(self) -> int: ...
  def __contains__(self, char: object) -> bool: ...
  def __len__(self) -> int: ...
  def __getitem__(self, index: SupportsIndex | slice)  -> UserString: ...
  def __iter__(self) -> Iterator[UserString]: ...
  def __reversed__(self) -> Iterator[UserString]: ...
  def __add__(self, other: object) -> UserString: ...
  def __radd__(self, other: object) -> UserString: ...
  def __mul__(self, n: int) -> UserString: ...
  def __rmul__(self, n: int) -> UserString: ...
  def __mod__(self, args: Any) -> UserString: ...
  def __rmod__(self, template: object) -> UserString: ...

  def capitalize(self) -> UserString: ...
  def casefold(self) -> UserString: ...
  def center(self, width: int, *args: Any) -> UserString: ...
  def count(self, sub: str | UserString, start: int = 0, end: int = sys.maxsize) -> int: ...
  def encode(self: UserString, encoding: str | None = "utf-8", errors: str | None = "strict") -> bytes: ...

  def endswith(self, suffix: str | tuple[str, ...], start: int | None = 0, end: int | None = sys.maxsize) -> bool: ...
  def expandtabs(self, tabsize: int = 8) -> UserString: ...
  def find(self, sub: str | UserString, start: int = 0, end: int = sys.maxsize) -> int: ...
  def format(self, *args: Any, **kwds: Any) -> str: ...
  def format_map(self, mapping: Mapping[str, Any]) -> str: ...
  def index(self, sub: str, start: int = 0, end: int = sys.maxsize) -> int: ...
  def isalpha(self) -> bool: ...
  def isalnum(self) -> bool: ...
  def isdecimal(self) -> bool: ...
  def isdigit(self) -> bool: ...
  def isidentifier(self) -> bool: ...
  def islower(self) -> bool: ...
  def isnumeric(self) -> bool: ...
  def isprintable(self) -> bool: ...
  def isspace(self) -> bool: ...
  def istitle(self) -> bool: ...
  def isupper(self) -> bool: ...
  def isascii(self) -> bool: ...
  def join(self, seq: Iterable[str]) -> str: ...
  def ljust(self, width: int, *args: Any) -> UserString: ...
  def lower(self) -> UserString: ...
  def lstrip(self, chars: str | None = None) -> UserString: ...
  maketrans = str.maketrans
  def partition(self, sep: str) -> tuple[str, str, str]: ...
  if sys.version_info >= (3, 9):
    def removeprefix(self, __prefix: str | UserString) -> UserString: ...
    def removesuffix(self, __suffix: str | UserString) -> UserString: ...

  def replace(self, old: str | UserString, new: str | UserString, maxsplit: int = -1) -> UserString: ...
  def rfind(self, sub: str | UserString, start: int = 0, end: int = sys.maxsize) -> int: ...
  def rindex(self, sub: str | UserString, start: int = 0, end: int = sys.maxsize) -> int: ...
  def rjust(self, width: int, *args: Any) -> UserString: ...
  def rpartition(self, sep: str) -> tuple[str, str, str]: ...
  def rstrip(self, chars: str | None = None) -> UserString: ...
  def split(self, sep: str | None = None, maxsplit: int = -1) -> list[str]: ...
  def rsplit(self, sep: str | None = None, maxsplit: int = -1) -> list[str]: ...
  def splitlines(self, keepends: bool = False) -> list[str]: ...
  def startswith(self, prefix: str | tuple[str, ...], start: int | None = 0, end: int | None = sys.maxsize) -> bool: ...
  def strip(self, chars: str | None = None) -> UserString: ...
  def swapcase(self) -> UserString: ...
  def title(self) -> UserString: ...
  def translate(self, *args: Any) -> UserString: ...
  def upper(self) -> UserString: ...
  def zfill(self, width: int) -> UserString: ...
