mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-09 22:49:57 +09:00
Merge pull request #3351 from Snowapril/fix-generic-alias
Add missing methods on `GenericAlias`
This commit is contained in:
153
Lib/_collections_abc.py
vendored
153
Lib/_collections_abc.py
vendored
@@ -9,6 +9,12 @@ Unit tests are in test_collections.
|
||||
from abc import ABCMeta, abstractmethod
|
||||
import sys
|
||||
|
||||
GenericAlias = type(list[int])
|
||||
EllipsisType = type(...)
|
||||
def _f(): pass
|
||||
FunctionType = type(_f)
|
||||
del _f
|
||||
|
||||
__all__ = ["Awaitable", "Coroutine",
|
||||
"AsyncIterable", "AsyncIterator", "AsyncGenerator",
|
||||
"Hashable", "Iterable", "Iterator", "Generator", "Reversible",
|
||||
@@ -110,6 +116,8 @@ class Awaitable(metaclass=ABCMeta):
|
||||
return _check_methods(C, "__await__")
|
||||
return NotImplemented
|
||||
|
||||
__class_getitem__ = classmethod(GenericAlias)
|
||||
|
||||
|
||||
class Coroutine(Awaitable):
|
||||
|
||||
@@ -169,6 +177,8 @@ class AsyncIterable(metaclass=ABCMeta):
|
||||
return _check_methods(C, "__aiter__")
|
||||
return NotImplemented
|
||||
|
||||
__class_getitem__ = classmethod(GenericAlias)
|
||||
|
||||
|
||||
class AsyncIterator(AsyncIterable):
|
||||
|
||||
@@ -255,6 +265,8 @@ class Iterable(metaclass=ABCMeta):
|
||||
return _check_methods(C, "__iter__")
|
||||
return NotImplemented
|
||||
|
||||
__class_getitem__ = classmethod(GenericAlias)
|
||||
|
||||
|
||||
class Iterator(Iterable):
|
||||
|
||||
@@ -384,6 +396,8 @@ class Container(metaclass=ABCMeta):
|
||||
return _check_methods(C, "__contains__")
|
||||
return NotImplemented
|
||||
|
||||
__class_getitem__ = classmethod(GenericAlias)
|
||||
|
||||
class Collection(Sized, Iterable, Container):
|
||||
|
||||
__slots__ = ()
|
||||
@@ -394,6 +408,141 @@ class Collection(Sized, Iterable, Container):
|
||||
return _check_methods(C, "__len__", "__iter__", "__contains__")
|
||||
return NotImplemented
|
||||
|
||||
|
||||
class _CallableGenericAlias(GenericAlias):
|
||||
""" Represent `Callable[argtypes, resulttype]`.
|
||||
|
||||
This sets ``__args__`` to a tuple containing the flattened ``argtypes``
|
||||
followed by ``resulttype``.
|
||||
|
||||
Example: ``Callable[[int, str], float]`` sets ``__args__`` to
|
||||
``(int, str, float)``.
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __new__(cls, origin, args):
|
||||
if not (isinstance(args, tuple) and len(args) == 2):
|
||||
raise TypeError(
|
||||
"Callable must be used as Callable[[arg, ...], result].")
|
||||
t_args, t_result = args
|
||||
if isinstance(t_args, list):
|
||||
args = (*t_args, t_result)
|
||||
elif not _is_param_expr(t_args):
|
||||
raise TypeError(f"Expected a list of types, an ellipsis, "
|
||||
f"ParamSpec, or Concatenate. Got {t_args}")
|
||||
return super().__new__(cls, origin, args)
|
||||
|
||||
@property
|
||||
def __parameters__(self):
|
||||
params = []
|
||||
for arg in self.__args__:
|
||||
# Looks like a genericalias
|
||||
if hasattr(arg, "__parameters__") and isinstance(arg.__parameters__, tuple):
|
||||
params.extend(arg.__parameters__)
|
||||
else:
|
||||
if _is_typevarlike(arg):
|
||||
params.append(arg)
|
||||
return tuple(dict.fromkeys(params))
|
||||
|
||||
def __repr__(self):
|
||||
if len(self.__args__) == 2 and _is_param_expr(self.__args__[0]):
|
||||
return super().__repr__()
|
||||
return (f'collections.abc.Callable'
|
||||
f'[[{", ".join([_type_repr(a) for a in self.__args__[:-1]])}], '
|
||||
f'{_type_repr(self.__args__[-1])}]')
|
||||
|
||||
def __reduce__(self):
|
||||
args = self.__args__
|
||||
if not (len(args) == 2 and _is_param_expr(args[0])):
|
||||
args = list(args[:-1]), args[-1]
|
||||
return _CallableGenericAlias, (Callable, args)
|
||||
|
||||
def __getitem__(self, item):
|
||||
# Called during TypeVar substitution, returns the custom subclass
|
||||
# rather than the default types.GenericAlias object. Most of the
|
||||
# code is copied from typing's _GenericAlias and the builtin
|
||||
# types.GenericAlias.
|
||||
|
||||
# A special case in PEP 612 where if X = Callable[P, int],
|
||||
# then X[int, str] == X[[int, str]].
|
||||
param_len = len(self.__parameters__)
|
||||
if param_len == 0:
|
||||
raise TypeError(f'{self} is not a generic class')
|
||||
if not isinstance(item, tuple):
|
||||
item = (item,)
|
||||
if (param_len == 1 and _is_param_expr(self.__parameters__[0])
|
||||
and item and not _is_param_expr(item[0])):
|
||||
item = (list(item),)
|
||||
item_len = len(item)
|
||||
if item_len != param_len:
|
||||
raise TypeError(f'Too {"many" if item_len > param_len else "few"}'
|
||||
f' arguments for {self};'
|
||||
f' actual {item_len}, expected {param_len}')
|
||||
subst = dict(zip(self.__parameters__, item))
|
||||
new_args = []
|
||||
for arg in self.__args__:
|
||||
if _is_typevarlike(arg):
|
||||
if _is_param_expr(arg):
|
||||
arg = subst[arg]
|
||||
if not _is_param_expr(arg):
|
||||
raise TypeError(f"Expected a list of types, an ellipsis, "
|
||||
f"ParamSpec, or Concatenate. Got {arg}")
|
||||
else:
|
||||
arg = subst[arg]
|
||||
# Looks like a GenericAlias
|
||||
elif hasattr(arg, '__parameters__') and isinstance(arg.__parameters__, tuple):
|
||||
subparams = arg.__parameters__
|
||||
if subparams:
|
||||
subargs = tuple(subst[x] for x in subparams)
|
||||
arg = arg[subargs]
|
||||
new_args.append(arg)
|
||||
|
||||
# args[0] occurs due to things like Z[[int, str, bool]] from PEP 612
|
||||
if not isinstance(new_args[0], list):
|
||||
t_result = new_args[-1]
|
||||
t_args = new_args[:-1]
|
||||
new_args = (t_args, t_result)
|
||||
return _CallableGenericAlias(Callable, tuple(new_args))
|
||||
|
||||
|
||||
def _is_typevarlike(arg):
|
||||
obj = type(arg)
|
||||
# looks like a TypeVar/ParamSpec
|
||||
return (obj.__module__ == 'typing'
|
||||
and obj.__name__ in {'ParamSpec', 'TypeVar'})
|
||||
|
||||
def _is_param_expr(obj):
|
||||
"""Checks if obj matches either a list of types, ``...``, ``ParamSpec`` or
|
||||
``_ConcatenateGenericAlias`` from typing.py
|
||||
"""
|
||||
if obj is Ellipsis:
|
||||
return True
|
||||
if isinstance(obj, list):
|
||||
return True
|
||||
obj = type(obj)
|
||||
names = ('ParamSpec', '_ConcatenateGenericAlias')
|
||||
return obj.__module__ == 'typing' and any(obj.__name__ == name for name in names)
|
||||
|
||||
def _type_repr(obj):
|
||||
"""Return the repr() of an object, special-casing types (internal helper).
|
||||
|
||||
Copied from :mod:`typing` since collections.abc
|
||||
shouldn't depend on that module.
|
||||
"""
|
||||
if isinstance(obj, GenericAlias):
|
||||
return repr(obj)
|
||||
if isinstance(obj, type):
|
||||
if obj.__module__ == 'builtins':
|
||||
return obj.__qualname__
|
||||
return f'{obj.__module__}.{obj.__qualname__}'
|
||||
if obj is Ellipsis:
|
||||
return '...'
|
||||
if isinstance(obj, FunctionType):
|
||||
return obj.__name__
|
||||
return repr(obj)
|
||||
|
||||
|
||||
class Callable(metaclass=ABCMeta):
|
||||
|
||||
__slots__ = ()
|
||||
@@ -408,6 +557,8 @@ class Callable(metaclass=ABCMeta):
|
||||
return _check_methods(C, "__call__")
|
||||
return NotImplemented
|
||||
|
||||
__class_getitem__ = classmethod(_CallableGenericAlias)
|
||||
|
||||
|
||||
### SETS ###
|
||||
|
||||
@@ -703,6 +854,8 @@ class MappingView(Sized):
|
||||
def __repr__(self):
|
||||
return '{0.__class__.__name__}({0._mapping!r})'.format(self)
|
||||
|
||||
__class_getitem__ = classmethod(GenericAlias)
|
||||
|
||||
|
||||
class KeysView(MappingView, Set):
|
||||
|
||||
|
||||
3
Lib/_weakrefset.py
vendored
3
Lib/_weakrefset.py
vendored
@@ -3,6 +3,7 @@
|
||||
# by abc.py to load everything else at startup.
|
||||
|
||||
from _weakref import ref
|
||||
from types import GenericAlias
|
||||
|
||||
__all__ = ['WeakSet']
|
||||
|
||||
@@ -197,3 +198,5 @@ class WeakSet:
|
||||
|
||||
def __repr__(self):
|
||||
return repr(self.data)
|
||||
|
||||
__class_getitem__ = classmethod(GenericAlias)
|
||||
|
||||
3
Lib/asyncio/futures.py
vendored
3
Lib/asyncio/futures.py
vendored
@@ -184,6 +184,9 @@ class Future:
|
||||
context['source_traceback'] = self._source_traceback
|
||||
self._loop.call_exception_handler(context)
|
||||
|
||||
def __class_getitem__(cls, type):
|
||||
return cls
|
||||
|
||||
def cancel(self):
|
||||
"""Cancel the future and schedule callbacks.
|
||||
|
||||
|
||||
3
Lib/asyncio/queues.py
vendored
3
Lib/asyncio/queues.py
vendored
@@ -81,6 +81,9 @@ class Queue:
|
||||
def __str__(self):
|
||||
return '<{} {}>'.format(type(self).__name__, self._format())
|
||||
|
||||
def __class_getitem__(cls, type):
|
||||
return cls
|
||||
|
||||
def _format(self):
|
||||
result = 'maxsize={!r}'.format(self._maxsize)
|
||||
if getattr(self, '_queue', None):
|
||||
|
||||
3
Lib/concurrent/futures/_base.py
vendored
3
Lib/concurrent/futures/_base.py
vendored
@@ -7,6 +7,7 @@ import collections
|
||||
import logging
|
||||
import threading
|
||||
import time
|
||||
import types
|
||||
|
||||
FIRST_COMPLETED = 'FIRST_COMPLETED'
|
||||
FIRST_EXCEPTION = 'FIRST_EXCEPTION'
|
||||
@@ -544,6 +545,8 @@ class Future(object):
|
||||
self._condition.notify_all()
|
||||
self._invoke_callbacks()
|
||||
|
||||
__class_getitem__ = classmethod(types.GenericAlias)
|
||||
|
||||
class Executor(object):
|
||||
"""This is an abstract base class for concrete asynchronous executors."""
|
||||
|
||||
|
||||
3
Lib/concurrent/futures/thread.py
vendored
3
Lib/concurrent/futures/thread.py
vendored
@@ -10,6 +10,7 @@ from concurrent.futures import _base
|
||||
import itertools
|
||||
import queue
|
||||
import threading
|
||||
import types
|
||||
import weakref
|
||||
import os
|
||||
|
||||
@@ -62,6 +63,8 @@ class _WorkItem(object):
|
||||
else:
|
||||
self.future.set_result(result)
|
||||
|
||||
__class_getitem__ = classmethod(types.GenericAlias)
|
||||
|
||||
|
||||
def _worker(executor_reference, work_queue, initializer, initargs):
|
||||
if initializer is not None:
|
||||
|
||||
5
Lib/dataclasses.py
vendored
5
Lib/dataclasses.py
vendored
@@ -7,6 +7,7 @@ import keyword
|
||||
import builtins
|
||||
import functools
|
||||
import _thread
|
||||
from types import GenericAlias
|
||||
|
||||
|
||||
__all__ = ['dataclass',
|
||||
@@ -217,6 +218,8 @@ class InitVar(metaclass=_InitVarMeta):
|
||||
type_name = repr(self.type)
|
||||
return f'dataclasses.InitVar[{type_name}]'
|
||||
|
||||
def __class_getitem__(cls, type):
|
||||
return InitVar(type)
|
||||
|
||||
# Instances of Field are only ever created from within this module,
|
||||
# and only from the field() function, although Field instances are
|
||||
@@ -285,6 +288,8 @@ class Field:
|
||||
# it.
|
||||
func(self.default, owner, name)
|
||||
|
||||
__class_getitem__ = classmethod(GenericAlias)
|
||||
|
||||
|
||||
class _DataclassParams:
|
||||
__slots__ = ('init',
|
||||
|
||||
3
Lib/difflib.py
vendored
3
Lib/difflib.py
vendored
@@ -32,6 +32,7 @@ __all__ = ['get_close_matches', 'ndiff', 'restore', 'SequenceMatcher',
|
||||
|
||||
from heapq import nlargest as _nlargest
|
||||
from collections import namedtuple as _namedtuple
|
||||
from types import GenericAlias
|
||||
|
||||
Match = _namedtuple('Match', 'a b size')
|
||||
|
||||
@@ -685,6 +686,8 @@ class SequenceMatcher:
|
||||
# shorter sequence
|
||||
return _calculate_ratio(min(la, lb), la + lb)
|
||||
|
||||
__class_getitem__ = classmethod(GenericAlias)
|
||||
|
||||
def get_close_matches(word, possibilities, n=3, cutoff=0.6):
|
||||
"""Use SequenceMatcher to return list of the best "good enough" matches.
|
||||
|
||||
|
||||
5
Lib/functools.py
vendored
5
Lib/functools.py
vendored
@@ -22,6 +22,7 @@ try:
|
||||
from _thread import RLock
|
||||
except ModuleNotFoundError:
|
||||
from _dummy_thread import RLock
|
||||
from types import GenericAlias
|
||||
|
||||
|
||||
################################################################################
|
||||
@@ -427,6 +428,8 @@ class partialmethod(object):
|
||||
def __isabstractmethod__(self):
|
||||
return getattr(self.func, "__isabstractmethod__", False)
|
||||
|
||||
__class_getitem__ = classmethod(GenericAlias)
|
||||
|
||||
# Helper functions
|
||||
|
||||
def _unwrap_partial(func):
|
||||
@@ -977,3 +980,5 @@ class cached_property:
|
||||
)
|
||||
raise TypeError(msg) from None
|
||||
return val
|
||||
|
||||
__class_getitem__ = classmethod(GenericAlias)
|
||||
|
||||
3
Lib/multiprocessing/managers.py
vendored
3
Lib/multiprocessing/managers.py
vendored
@@ -18,6 +18,7 @@ import sys
|
||||
import threading
|
||||
import array
|
||||
import queue
|
||||
import types
|
||||
|
||||
from time import time as _time
|
||||
from traceback import format_exc
|
||||
@@ -1078,6 +1079,8 @@ class ValueProxy(BaseProxy):
|
||||
return self._callmethod('set', (value,))
|
||||
value = property(get, set)
|
||||
|
||||
__class_getitem__ = classmethod(types.GenericAlias)
|
||||
|
||||
|
||||
BaseListProxy = MakeProxyType('BaseListProxy', (
|
||||
'__add__', '__contains__', '__delitem__', '__getitem__', '__len__',
|
||||
|
||||
3
Lib/multiprocessing/pool.py
vendored
3
Lib/multiprocessing/pool.py
vendored
@@ -20,6 +20,7 @@ import collections
|
||||
import os
|
||||
import time
|
||||
import traceback
|
||||
import types
|
||||
|
||||
# If threading is available then ThreadPool should be provided. Therefore
|
||||
# we avoid top-level imports which are liable to fail on some systems.
|
||||
@@ -616,6 +617,8 @@ class ApplyResult(object):
|
||||
self._event.set()
|
||||
del self._cache[self._job]
|
||||
|
||||
__class_getitem__ = classmethod(types.GenericAlias)
|
||||
|
||||
AsyncResult = ApplyResult # create alias -- see #17805
|
||||
|
||||
#
|
||||
|
||||
3
Lib/multiprocessing/queues.py
vendored
3
Lib/multiprocessing/queues.py
vendored
@@ -14,6 +14,7 @@ import os
|
||||
import threading
|
||||
import collections
|
||||
import time
|
||||
import types
|
||||
import weakref
|
||||
import errno
|
||||
|
||||
@@ -353,3 +354,5 @@ class SimpleQueue(object):
|
||||
else:
|
||||
with self._wlock:
|
||||
self._writer.send_bytes(obj)
|
||||
|
||||
__class_getitem__ = classmethod(types.GenericAlias)
|
||||
|
||||
5
Lib/queue.py
vendored
5
Lib/queue.py
vendored
@@ -1,6 +1,7 @@
|
||||
'''A multi-producer, multi-consumer queue.'''
|
||||
|
||||
import threading
|
||||
import types
|
||||
from collections import deque
|
||||
from heapq import heappush, heappop
|
||||
from time import monotonic as time
|
||||
@@ -216,6 +217,8 @@ class Queue:
|
||||
def _get(self):
|
||||
return self.queue.popleft()
|
||||
|
||||
__class_getitem__ = classmethod(types.GenericAlias)
|
||||
|
||||
|
||||
class PriorityQueue(Queue):
|
||||
'''Variant of Queue that retrieves open entries in priority order (lowest first).
|
||||
@@ -316,6 +319,8 @@ class _PySimpleQueue:
|
||||
'''Return the approximate size of the queue (not reliable!).'''
|
||||
return len(self._queue)
|
||||
|
||||
__class_getitem__ = classmethod(types.GenericAlias)
|
||||
|
||||
|
||||
if SimpleQueue is None:
|
||||
SimpleQueue = _PySimpleQueue
|
||||
|
||||
5
Lib/tempfile.py
vendored
5
Lib/tempfile.py
vendored
@@ -44,6 +44,7 @@ import shutil as _shutil
|
||||
import errno as _errno
|
||||
from random import Random as _Random
|
||||
import sys as _sys
|
||||
import types as _types
|
||||
import weakref as _weakref
|
||||
|
||||
try:
|
||||
@@ -646,6 +647,8 @@ class SpooledTemporaryFile:
|
||||
'encoding': encoding, 'newline': newline,
|
||||
'dir': dir, 'errors': errors}
|
||||
|
||||
__class_getitem__ = classmethod(_types.GenericAlias)
|
||||
|
||||
def _check(self, file):
|
||||
if self._rolled: return
|
||||
max_size = self._max_size
|
||||
@@ -833,3 +836,5 @@ class TemporaryDirectory(object):
|
||||
def cleanup(self):
|
||||
if self._finalizer.detach():
|
||||
self._rmtree(self.name)
|
||||
|
||||
__class_getitem__ = classmethod(_types.GenericAlias)
|
||||
|
||||
350
Lib/test/test_genericalias.py
vendored
Normal file
350
Lib/test/test_genericalias.py
vendored
Normal file
@@ -0,0 +1,350 @@
|
||||
"""Tests for C-implemented GenericAlias."""
|
||||
|
||||
import unittest
|
||||
import pickle
|
||||
import copy
|
||||
from collections import (
|
||||
defaultdict, deque, OrderedDict, Counter, UserDict, UserList
|
||||
)
|
||||
from collections.abc import *
|
||||
from concurrent.futures import Future
|
||||
from concurrent.futures.thread import _WorkItem
|
||||
from contextlib import AbstractContextManager, AbstractAsyncContextManager
|
||||
# XXX RUSTPYTHON TODO: from contextvars import ContextVar, Token
|
||||
from dataclasses import Field
|
||||
from functools import partial, partialmethod, cached_property
|
||||
# XXX RUSTPYTHON TODO: from mailbox import Mailbox, _PartialFile
|
||||
try:
|
||||
import ctypes
|
||||
except ImportError:
|
||||
ctypes = None
|
||||
from difflib import SequenceMatcher
|
||||
# XXX RUSTPYTHON TODO: from filecmp import dircmp
|
||||
# XXX RUSTPYTHON TODO: from fileinput import FileInput
|
||||
from itertools import chain
|
||||
from http.cookies import Morsel
|
||||
from multiprocessing.managers import ValueProxy
|
||||
from multiprocessing.pool import ApplyResult
|
||||
try:
|
||||
from multiprocessing.shared_memory import ShareableList
|
||||
except ImportError:
|
||||
# multiprocessing.shared_memory is not available on e.g. Android
|
||||
ShareableList = None
|
||||
from multiprocessing.queues import SimpleQueue as MPSimpleQueue
|
||||
from os import DirEntry
|
||||
from re import Pattern, Match
|
||||
from types import GenericAlias, MappingProxyType, AsyncGeneratorType
|
||||
from tempfile import TemporaryDirectory, SpooledTemporaryFile
|
||||
from urllib.parse import SplitResult, ParseResult
|
||||
from unittest.case import _AssertRaisesContext
|
||||
from queue import Queue, SimpleQueue
|
||||
from weakref import WeakSet, ReferenceType, ref
|
||||
import typing
|
||||
|
||||
from typing import TypeVar
|
||||
T = TypeVar('T')
|
||||
K = TypeVar('K')
|
||||
V = TypeVar('V')
|
||||
|
||||
class BaseTest(unittest.TestCase):
|
||||
"""Test basics."""
|
||||
generic_types = [type, tuple, list, dict, set, frozenset, enumerate,
|
||||
defaultdict, deque,
|
||||
SequenceMatcher,
|
||||
# XXX RUSTPYTHON TODO: dircmp,
|
||||
# XXX RUSTPYTHON TODO: FileInput,
|
||||
OrderedDict, Counter, UserDict, UserList,
|
||||
Pattern, Match,
|
||||
partialmethod, cached_property, # XXX RUSTPYTHON TODO: partial
|
||||
# XXX RUSTPYTHON TODO: AbstractContextManager, AbstractAsyncContextManager,
|
||||
Awaitable, Coroutine,
|
||||
AsyncIterable, AsyncIterator,
|
||||
AsyncGenerator, Generator,
|
||||
Iterable, Iterator,
|
||||
Reversible,
|
||||
Container, Collection,
|
||||
# XXX RUSTPYTHON TODO: Mailbox, _PartialFile,
|
||||
# XXX RUSTPYTHON TODO: ContextVar, Token,
|
||||
Field,
|
||||
Set, MutableSet,
|
||||
Mapping, MutableMapping, MappingView,
|
||||
KeysView, ItemsView, ValuesView,
|
||||
Sequence, MutableSequence,
|
||||
MappingProxyType, AsyncGeneratorType,
|
||||
DirEntry,
|
||||
chain,
|
||||
TemporaryDirectory, SpooledTemporaryFile,
|
||||
Queue, SimpleQueue,
|
||||
_AssertRaisesContext,
|
||||
SplitResult, ParseResult,
|
||||
ValueProxy, ApplyResult,
|
||||
WeakSet, ReferenceType, ref,
|
||||
ShareableList, MPSimpleQueue,
|
||||
Future, _WorkItem,
|
||||
Morsel]
|
||||
if ctypes is not None:
|
||||
generic_types.extend((ctypes.Array, ctypes.LibraryLoader))
|
||||
|
||||
def test_subscriptable(self):
|
||||
for t in self.generic_types:
|
||||
if t is None:
|
||||
continue
|
||||
tname = t.__name__
|
||||
with self.subTest(f"Testing {tname}"):
|
||||
alias = t[int]
|
||||
self.assertIs(alias.__origin__, t)
|
||||
self.assertEqual(alias.__args__, (int,))
|
||||
self.assertEqual(alias.__parameters__, ())
|
||||
|
||||
def test_unsubscriptable(self):
|
||||
for t in int, str, float, Sized, Hashable:
|
||||
tname = t.__name__
|
||||
with self.subTest(f"Testing {tname}"):
|
||||
with self.assertRaises(TypeError):
|
||||
t[int]
|
||||
|
||||
def test_instantiate(self):
|
||||
for t in tuple, list, dict, set, frozenset, defaultdict, deque:
|
||||
tname = t.__name__
|
||||
with self.subTest(f"Testing {tname}"):
|
||||
alias = t[int]
|
||||
self.assertEqual(alias(), t())
|
||||
if t is dict:
|
||||
self.assertEqual(alias(iter([('a', 1), ('b', 2)])), dict(a=1, b=2))
|
||||
self.assertEqual(alias(a=1, b=2), dict(a=1, b=2))
|
||||
elif t is defaultdict:
|
||||
def default():
|
||||
return 'value'
|
||||
a = alias(default)
|
||||
d = defaultdict(default)
|
||||
self.assertEqual(a['test'], d['test'])
|
||||
else:
|
||||
self.assertEqual(alias(iter((1, 2, 3))), t((1, 2, 3)))
|
||||
|
||||
def test_unbound_methods(self):
|
||||
t = list[int]
|
||||
a = t()
|
||||
t.append(a, 'foo')
|
||||
self.assertEqual(a, ['foo'])
|
||||
x = t.__getitem__(a, 0)
|
||||
self.assertEqual(x, 'foo')
|
||||
self.assertEqual(t.__len__(a), 1)
|
||||
|
||||
def test_subclassing(self):
|
||||
class C(list[int]):
|
||||
pass
|
||||
self.assertEqual(C.__bases__, (list,))
|
||||
self.assertEqual(C.__class__, type)
|
||||
|
||||
def test_class_methods(self):
|
||||
t = dict[int, None]
|
||||
self.assertEqual(dict.fromkeys(range(2)), {0: None, 1: None}) # This works
|
||||
self.assertEqual(t.fromkeys(range(2)), {0: None, 1: None}) # Should be equivalent
|
||||
|
||||
def test_no_chaining(self):
|
||||
t = list[int]
|
||||
with self.assertRaises(TypeError):
|
||||
t[int]
|
||||
|
||||
def test_generic_subclass(self):
|
||||
class MyList(list):
|
||||
pass
|
||||
t = MyList[int]
|
||||
self.assertIs(t.__origin__, MyList)
|
||||
self.assertEqual(t.__args__, (int,))
|
||||
self.assertEqual(t.__parameters__, ())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_repr(self):
|
||||
class MyList(list):
|
||||
pass
|
||||
self.assertEqual(repr(list[str]), 'list[str]')
|
||||
self.assertEqual(repr(list[()]), 'list[()]')
|
||||
self.assertEqual(repr(tuple[int, ...]), 'tuple[int, ...]')
|
||||
self.assertTrue(repr(MyList[int]).endswith('.BaseTest.test_repr.<locals>.MyList[int]'))
|
||||
self.assertEqual(repr(list[str]()), '[]') # instances should keep their normal repr
|
||||
|
||||
def test_exposed_type(self):
|
||||
import types
|
||||
a = types.GenericAlias(list, int)
|
||||
self.assertEqual(str(a), 'list[int]')
|
||||
self.assertIs(a.__origin__, list)
|
||||
self.assertEqual(a.__args__, (int,))
|
||||
self.assertEqual(a.__parameters__, ())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_parameters(self):
|
||||
from typing import List, Dict, Callable
|
||||
D0 = dict[str, int]
|
||||
self.assertEqual(D0.__args__, (str, int))
|
||||
self.assertEqual(D0.__parameters__, ())
|
||||
D1a = dict[str, V]
|
||||
self.assertEqual(D1a.__args__, (str, V))
|
||||
self.assertEqual(D1a.__parameters__, (V,))
|
||||
D1b = dict[K, int]
|
||||
self.assertEqual(D1b.__args__, (K, int))
|
||||
self.assertEqual(D1b.__parameters__, (K,))
|
||||
D2a = dict[K, V]
|
||||
self.assertEqual(D2a.__args__, (K, V))
|
||||
self.assertEqual(D2a.__parameters__, (K, V))
|
||||
D2b = dict[T, T]
|
||||
self.assertEqual(D2b.__args__, (T, T))
|
||||
self.assertEqual(D2b.__parameters__, (T,))
|
||||
L0 = list[str]
|
||||
self.assertEqual(L0.__args__, (str,))
|
||||
self.assertEqual(L0.__parameters__, ())
|
||||
L1 = list[T]
|
||||
self.assertEqual(L1.__args__, (T,))
|
||||
self.assertEqual(L1.__parameters__, (T,))
|
||||
L2 = list[list[T]]
|
||||
self.assertEqual(L2.__args__, (list[T],))
|
||||
self.assertEqual(L2.__parameters__, (T,))
|
||||
L3 = list[List[T]]
|
||||
self.assertEqual(L3.__args__, (List[T],))
|
||||
self.assertEqual(L3.__parameters__, (T,))
|
||||
L4a = list[Dict[K, V]]
|
||||
self.assertEqual(L4a.__args__, (Dict[K, V],))
|
||||
self.assertEqual(L4a.__parameters__, (K, V))
|
||||
L4b = list[Dict[T, int]]
|
||||
self.assertEqual(L4b.__args__, (Dict[T, int],))
|
||||
self.assertEqual(L4b.__parameters__, (T,))
|
||||
L5 = list[Callable[[K, V], K]]
|
||||
self.assertEqual(L5.__args__, (Callable[[K, V], K],))
|
||||
self.assertEqual(L5.__parameters__, (K, V))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_parameter_chaining(self):
|
||||
from typing import List, Dict, Union, Callable
|
||||
self.assertEqual(list[T][int], list[int])
|
||||
self.assertEqual(dict[str, T][int], dict[str, int])
|
||||
self.assertEqual(dict[T, int][str], dict[str, int])
|
||||
self.assertEqual(dict[K, V][str, int], dict[str, int])
|
||||
self.assertEqual(dict[T, T][int], dict[int, int])
|
||||
|
||||
self.assertEqual(list[list[T]][int], list[list[int]])
|
||||
self.assertEqual(list[dict[T, int]][str], list[dict[str, int]])
|
||||
self.assertEqual(list[dict[str, T]][int], list[dict[str, int]])
|
||||
self.assertEqual(list[dict[K, V]][str, int], list[dict[str, int]])
|
||||
self.assertEqual(dict[T, list[int]][str], dict[str, list[int]])
|
||||
|
||||
self.assertEqual(list[List[T]][int], list[List[int]])
|
||||
self.assertEqual(list[Dict[K, V]][str, int], list[Dict[str, int]])
|
||||
self.assertEqual(list[Union[K, V]][str, int], list[Union[str, int]])
|
||||
self.assertEqual(list[Callable[[K, V], K]][str, int],
|
||||
list[Callable[[str, int], str]])
|
||||
self.assertEqual(dict[T, List[int]][str], dict[str, List[int]])
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
list[int][int]
|
||||
dict[T, int][str, int]
|
||||
dict[str, T][str, int]
|
||||
dict[T, T][str, int]
|
||||
|
||||
def test_equality(self):
|
||||
self.assertEqual(list[int], list[int])
|
||||
self.assertEqual(dict[str, int], dict[str, int])
|
||||
self.assertNotEqual(dict[str, int], dict[str, str])
|
||||
self.assertNotEqual(list, list[int])
|
||||
self.assertNotEqual(list[int], list)
|
||||
|
||||
def test_isinstance(self):
|
||||
self.assertTrue(isinstance([], list))
|
||||
with self.assertRaises(TypeError):
|
||||
isinstance([], list[str])
|
||||
|
||||
def test_issubclass(self):
|
||||
class L(list): ...
|
||||
self.assertTrue(issubclass(L, list))
|
||||
with self.assertRaises(TypeError):
|
||||
issubclass(L, list[str])
|
||||
|
||||
def test_type_generic(self):
|
||||
t = type[int]
|
||||
Test = t('Test', (), {})
|
||||
self.assertTrue(isinstance(Test, type))
|
||||
test = Test()
|
||||
self.assertEqual(t(test), Test)
|
||||
self.assertEqual(t(0), int)
|
||||
|
||||
def test_type_subclass_generic(self):
|
||||
class MyType(type):
|
||||
pass
|
||||
with self.assertRaises(TypeError):
|
||||
MyType[int]
|
||||
|
||||
def test_pickle(self):
|
||||
alias = GenericAlias(list, T)
|
||||
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
|
||||
s = pickle.dumps(alias, proto)
|
||||
loaded = pickle.loads(s)
|
||||
self.assertEqual(loaded.__origin__, alias.__origin__)
|
||||
self.assertEqual(loaded.__args__, alias.__args__)
|
||||
self.assertEqual(loaded.__parameters__, alias.__parameters__)
|
||||
|
||||
def test_copy(self):
|
||||
class X(list):
|
||||
def __copy__(self):
|
||||
return self
|
||||
def __deepcopy__(self, memo):
|
||||
return self
|
||||
|
||||
for origin in list, deque, X:
|
||||
alias = GenericAlias(origin, T)
|
||||
copied = copy.copy(alias)
|
||||
self.assertEqual(copied.__origin__, alias.__origin__)
|
||||
self.assertEqual(copied.__args__, alias.__args__)
|
||||
self.assertEqual(copied.__parameters__, alias.__parameters__)
|
||||
copied = copy.deepcopy(alias)
|
||||
self.assertEqual(copied.__origin__, alias.__origin__)
|
||||
self.assertEqual(copied.__args__, alias.__args__)
|
||||
self.assertEqual(copied.__parameters__, alias.__parameters__)
|
||||
|
||||
def test_union(self):
|
||||
a = typing.Union[list[int], list[str]]
|
||||
self.assertEqual(a.__args__, (list[int], list[str]))
|
||||
self.assertEqual(a.__parameters__, ())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_union_generic(self):
|
||||
a = typing.Union[list[T], tuple[T, ...]]
|
||||
self.assertEqual(a.__args__, (list[T], tuple[T, ...]))
|
||||
self.assertEqual(a.__parameters__, (T,))
|
||||
|
||||
def test_dir(self):
|
||||
dir_of_gen_alias = set(dir(list[int]))
|
||||
self.assertTrue(dir_of_gen_alias.issuperset(dir(list)))
|
||||
for generic_alias_property in ("__origin__", "__args__", "__parameters__"):
|
||||
self.assertIn(generic_alias_property, dir_of_gen_alias)
|
||||
|
||||
def test_weakref(self):
|
||||
for t in self.generic_types:
|
||||
if t is None:
|
||||
continue
|
||||
tname = t.__name__
|
||||
with self.subTest(f"Testing {tname}"):
|
||||
alias = t[int]
|
||||
self.assertEqual(ref(alias)(), alias)
|
||||
|
||||
def test_no_kwargs(self):
|
||||
# bpo-42576
|
||||
with self.assertRaises(TypeError):
|
||||
GenericAlias(bad=float)
|
||||
|
||||
def test_subclassing_types_genericalias(self):
|
||||
class SubClass(GenericAlias): ...
|
||||
alias = SubClass(list, int)
|
||||
class Bad(GenericAlias):
|
||||
def __new__(cls, *args, **kwargs):
|
||||
super().__new__(cls, *args, **kwargs)
|
||||
|
||||
self.assertEqual(alias, list[int])
|
||||
with self.assertRaises(TypeError):
|
||||
Bad(list, int, bad=int)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
35
Lib/test/test_typing.py
vendored
35
Lib/test/test_typing.py
vendored
@@ -1819,26 +1819,25 @@ class GenericTests(BaseTestCase):
|
||||
def test_extended_generic_rules_subclassing(self):
|
||||
class T1(Tuple[T, KT]): ...
|
||||
class T2(Tuple[T, ...]): ...
|
||||
class C1(Callable[[T], T]): ...
|
||||
class C2(Callable[..., int]):
|
||||
def __call__(self):
|
||||
return None
|
||||
class C1(typing.Container[T]):
|
||||
def __contains__(self, item):
|
||||
return False
|
||||
|
||||
self.assertEqual(T1.__parameters__, (T, KT))
|
||||
self.assertEqual(T1[int, str].__args__, (int, str))
|
||||
self.assertEqual(T1[int, T].__origin__, T1)
|
||||
|
||||
self.assertEqual(T2.__parameters__, (T,))
|
||||
with self.assertRaises(TypeError):
|
||||
T1[int]
|
||||
with self.assertRaises(TypeError):
|
||||
T2[int, str]
|
||||
# These don't work because of tuple.__class_item__
|
||||
## with self.assertRaises(TypeError):
|
||||
## T1[int]
|
||||
## with self.assertRaises(TypeError):
|
||||
## T2[int, str]
|
||||
|
||||
self.assertEqual(repr(C1[int]).split('.')[-1], 'C1[int]')
|
||||
self.assertEqual(C2.__parameters__, ())
|
||||
self.assertIsInstance(C2(), collections.abc.Callable)
|
||||
self.assertIsSubclass(C2, collections.abc.Callable)
|
||||
self.assertIsSubclass(C1, collections.abc.Callable)
|
||||
self.assertEqual(C1.__parameters__, (T,))
|
||||
self.assertIsInstance(C1(), collections.abc.Container)
|
||||
self.assertIsSubclass(C1, collections.abc.Container)
|
||||
self.assertIsInstance(T1(), tuple)
|
||||
self.assertIsSubclass(T2, tuple)
|
||||
with self.assertRaises(TypeError):
|
||||
@@ -1871,22 +1870,18 @@ class GenericTests(BaseTestCase):
|
||||
self.clear_caches()
|
||||
class MyTup(Tuple[T, T]): ...
|
||||
self.assertIs(MyTup[int]().__class__, MyTup)
|
||||
self.assertIs(MyTup[int]().__orig_class__, MyTup[int])
|
||||
class MyCall(Callable[..., T]):
|
||||
def __call__(self): return None
|
||||
self.assertIs(MyCall[T]().__class__, MyCall)
|
||||
self.assertIs(MyCall[T]().__orig_class__, MyCall[T])
|
||||
self.assertEqual(MyTup[int]().__orig_class__, MyTup[int])
|
||||
class MyDict(typing.Dict[T, T]): ...
|
||||
self.assertIs(MyDict[int]().__class__, MyDict)
|
||||
self.assertIs(MyDict[int]().__orig_class__, MyDict[int])
|
||||
self.assertEqual(MyDict[int]().__orig_class__, MyDict[int])
|
||||
class MyDef(typing.DefaultDict[str, T]): ...
|
||||
self.assertIs(MyDef[int]().__class__, MyDef)
|
||||
self.assertIs(MyDef[int]().__orig_class__, MyDef[int])
|
||||
self.assertEqual(MyDef[int]().__orig_class__, MyDef[int])
|
||||
# ChainMap was added in 3.3
|
||||
if sys.version_info >= (3, 3):
|
||||
class MyChain(typing.ChainMap[str, T]): ...
|
||||
self.assertIs(MyChain[int]().__class__, MyChain)
|
||||
self.assertIs(MyChain[int]().__orig_class__, MyChain[int])
|
||||
self.assertEqual(MyChain[int]().__orig_class__, MyChain[int])
|
||||
|
||||
def test_all_repr_eq_any(self):
|
||||
objs = (getattr(typing, el) for el in typing.__all__)
|
||||
|
||||
3
Lib/unittest/case.py
vendored
3
Lib/unittest/case.py
vendored
@@ -10,6 +10,7 @@ import warnings
|
||||
import collections
|
||||
import contextlib
|
||||
import traceback
|
||||
import types
|
||||
|
||||
from . import result
|
||||
from .util import (strclass, safe_repr, _count_diff_all_purpose,
|
||||
@@ -217,6 +218,8 @@ class _AssertRaisesContext(_AssertRaisesBaseContext):
|
||||
expected_regex.pattern, str(exc_value)))
|
||||
return True
|
||||
|
||||
__class_getitem__ = classmethod(types.GenericAlias)
|
||||
|
||||
|
||||
class _AssertWarnsContext(_AssertRaisesBaseContext):
|
||||
"""A context manager used to implement TestCase.assertWarns* methods."""
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::{PyCode, PyStrRef, PyTypeRef};
|
||||
use super::{PyCode, PyGenericAlias, PyStrRef, PyTypeRef};
|
||||
use crate::{
|
||||
builtins::PyBaseExceptionRef,
|
||||
coroutine::Coro,
|
||||
@@ -123,6 +123,11 @@ impl PyAsyncGen {
|
||||
fn ag_code(&self, _vm: &VirtualMachine) -> PyRef<PyCode> {
|
||||
self.inner.frame().code.clone()
|
||||
}
|
||||
|
||||
#[pyclassmethod(magic)]
|
||||
fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
|
||||
PyGenericAlias::new(cls, args, vm)
|
||||
}
|
||||
}
|
||||
impl Unconstructible for PyAsyncGen {}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use super::{
|
||||
set::PySetInner, IterStatus, PositionIterInternal, PyBaseExceptionRef, PySet, PyStrRef,
|
||||
PyTypeRef,
|
||||
set::PySetInner, IterStatus, PositionIterInternal, PyBaseExceptionRef, PyGenericAlias, PySet,
|
||||
PyStrRef, PyTypeRef,
|
||||
};
|
||||
use crate::{
|
||||
builtins::PyTuple,
|
||||
@@ -413,6 +413,11 @@ impl PyDict {
|
||||
fn reversed(zelf: PyRef<Self>) -> PyDictReverseKeyIterator {
|
||||
PyDictReverseKeyIterator::new(zelf)
|
||||
}
|
||||
|
||||
#[pyclassmethod(magic)]
|
||||
fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
|
||||
PyGenericAlias::new(cls, args, vm)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMapping for PyDict {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::{IterStatus, PositionIterInternal, PyIntRef, PyTupleRef, PyTypeRef};
|
||||
use super::{IterStatus, PositionIterInternal, PyGenericAlias, PyIntRef, PyTupleRef, PyTypeRef};
|
||||
use crate::common::lock::{PyMutex, PyRwLock};
|
||||
use crate::{
|
||||
function::{IntoPyObject, OptionalArg},
|
||||
@@ -47,7 +47,12 @@ impl Constructor for PyEnumerate {
|
||||
}
|
||||
|
||||
#[pyimpl(with(IterNext, Constructor), flags(BASETYPE))]
|
||||
impl PyEnumerate {}
|
||||
impl PyEnumerate {
|
||||
#[pyclassmethod(magic)]
|
||||
fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
|
||||
PyGenericAlias::new(cls, args, vm)
|
||||
}
|
||||
}
|
||||
|
||||
impl IterNextIterable for PyEnumerate {}
|
||||
impl IterNext for PyEnumerate {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use crate::{
|
||||
builtins::{PyList, PyStr, PyStrRef, PyTuple, PyTupleRef, PyTypeRef},
|
||||
builtins::{PyList, PyStr, PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef},
|
||||
common::hash,
|
||||
function::IntoPyObject,
|
||||
types::{Constructor, GetAttr, Hashable},
|
||||
IdProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject,
|
||||
TypeProtocol, VirtualMachine,
|
||||
function::{FuncArgs, IntoPyObject},
|
||||
types::{Callable, Comparable, Constructor, GetAttr, Hashable, PyComparisonOp},
|
||||
IdProtocol, PyClassImpl, PyComparisonValue, PyContext, PyObject, PyObjectRef, PyRef, PyResult,
|
||||
PyValue, TryFromObject, TypeProtocol, VirtualMachine,
|
||||
};
|
||||
use std::fmt;
|
||||
|
||||
@@ -52,7 +52,10 @@ impl Constructor for PyGenericAlias {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl(with(Hashable, Constructor, GetAttr), flags(BASETYPE))]
|
||||
#[pyimpl(
|
||||
with(Callable, Comparable, Constructor, GetAttr, Hashable),
|
||||
flags(BASETYPE)
|
||||
)]
|
||||
impl PyGenericAlias {
|
||||
pub fn new(origin: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> Self {
|
||||
let args: PyTupleRef = if let Ok(tuple) = PyTupleRef::try_from_object(vm, args.clone()) {
|
||||
@@ -134,9 +137,34 @@ impl PyGenericAlias {
|
||||
}
|
||||
Ok(dir)
|
||||
}
|
||||
|
||||
#[pymethod(magic)]
|
||||
fn reduce(zelf: PyRef<Self>, vm: &VirtualMachine) -> (PyTypeRef, (PyTypeRef, PyTupleRef)) {
|
||||
(
|
||||
vm.ctx.types.generic_alias_type.clone(),
|
||||
(zelf.origin.clone(), zelf.args.clone()),
|
||||
)
|
||||
}
|
||||
|
||||
#[pymethod(magic)]
|
||||
fn mro_entries(&self, _bases: PyObjectRef, vm: &VirtualMachine) -> PyTupleRef {
|
||||
PyTuple::new_ref(vec![self.origin()], &vm.ctx)
|
||||
}
|
||||
|
||||
#[pymethod(magic)]
|
||||
fn instancecheck(_zelf: PyRef<Self>, _obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
Err(vm
|
||||
.new_type_error("isinstance() argument 2 cannot be a parameterized generic".to_owned()))
|
||||
}
|
||||
|
||||
#[pymethod(magic)]
|
||||
fn subclasscheck(_zelf: PyRef<Self>, _obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
Err(vm
|
||||
.new_type_error("issubclass() argument 2 cannot be a parameterized generic".to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
fn is_typevar(obj: PyObjectRef) -> bool {
|
||||
fn is_typevar(obj: &PyObjectRef) -> bool {
|
||||
let class = obj.class();
|
||||
class.slot_name() == "TypeVar"
|
||||
&& class
|
||||
@@ -148,7 +176,7 @@ fn is_typevar(obj: PyObjectRef) -> bool {
|
||||
fn make_parameters(args: &PyTupleRef, vm: &VirtualMachine) -> PyTupleRef {
|
||||
let mut parameters: Vec<PyObjectRef> = vec![];
|
||||
for arg in args.as_slice() {
|
||||
if is_typevar(arg.clone()) {
|
||||
if is_typevar(arg) {
|
||||
parameters.push(arg.clone());
|
||||
} else if let Ok(tuple) = arg
|
||||
.clone()
|
||||
@@ -164,6 +192,46 @@ fn make_parameters(args: &PyTupleRef, vm: &VirtualMachine) -> PyTupleRef {
|
||||
PyTuple::new_ref(parameters, &vm.ctx)
|
||||
}
|
||||
|
||||
impl Callable for PyGenericAlias {
|
||||
type Args = FuncArgs;
|
||||
fn call(zelf: &crate::PyObjectView<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
|
||||
PyType::call(&zelf.origin, args, vm).map(|obj| {
|
||||
if let Err(exc) = obj.set_attr("__orig_class__", zelf.to_owned(), vm) {
|
||||
if !exc.isinstance(&vm.ctx.exceptions.attribute_error)
|
||||
&& !exc.isinstance(&vm.ctx.exceptions.type_error)
|
||||
{
|
||||
return Err(exc);
|
||||
}
|
||||
}
|
||||
Ok(obj)
|
||||
})?
|
||||
}
|
||||
}
|
||||
|
||||
impl Comparable for PyGenericAlias {
|
||||
fn cmp(
|
||||
zelf: &crate::PyObjectView<Self>,
|
||||
other: &PyObject,
|
||||
op: PyComparisonOp,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyComparisonValue> {
|
||||
op.eq_only(|| {
|
||||
let other = class_or_notimplemented!(Self, other);
|
||||
Ok(PyComparisonValue::Implemented(
|
||||
if !zelf
|
||||
.origin()
|
||||
.rich_compare_bool(&other.origin(), PyComparisonOp::Eq, vm)?
|
||||
{
|
||||
false
|
||||
} else {
|
||||
zelf.args()
|
||||
.rich_compare_bool(&other.args(), PyComparisonOp::Eq, vm)?
|
||||
},
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Hashable for PyGenericAlias {
|
||||
#[inline]
|
||||
fn hash(zelf: &crate::PyObjectView<Self>, vm: &VirtualMachine) -> PyResult<hash::PyHash> {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::{PyDict, PyList, PyStrRef, PyTuple, PyTypeRef};
|
||||
use super::{PyDict, PyGenericAlias, PyList, PyStrRef, PyTuple, PyTypeRef};
|
||||
use crate::{
|
||||
function::{IntoPyObject, OptionalArg},
|
||||
protocol::{PyMapping, PyMappingMethods},
|
||||
@@ -148,6 +148,11 @@ impl PyMappingProxy {
|
||||
};
|
||||
Ok(format!("mappingproxy({})", vm.to_repr(&obj)?))
|
||||
}
|
||||
|
||||
#[pyclassmethod(magic)]
|
||||
fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
|
||||
PyGenericAlias::new(cls, args, vm)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMapping for PyMappingProxy {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
/*
|
||||
* Builtin set type with a sequence of unique items.
|
||||
*/
|
||||
use super::{builtins_iter, IterStatus, PositionIterInternal, PyDictRef, PyTupleRef, PyTypeRef};
|
||||
use super::{
|
||||
builtins_iter, IterStatus, PositionIterInternal, PyDictRef, PyGenericAlias, PyTupleRef,
|
||||
PyTypeRef,
|
||||
};
|
||||
use crate::common::{ascii, hash::PyHash, lock::PyMutex, rc::PyRc};
|
||||
use crate::{
|
||||
dictdatatype::{self, DictSize},
|
||||
@@ -619,6 +622,11 @@ impl PySet {
|
||||
) -> PyResult<(PyTypeRef, PyTupleRef, Option<PyDictRef>)> {
|
||||
reduce_set(zelf.as_ref(), vm)
|
||||
}
|
||||
|
||||
#[pyclassmethod(magic)]
|
||||
fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
|
||||
PyGenericAlias::new(cls, args, vm)
|
||||
}
|
||||
}
|
||||
|
||||
impl Comparable for PySet {
|
||||
@@ -810,6 +818,11 @@ impl PyFrozenSet {
|
||||
) -> PyResult<(PyTypeRef, PyTupleRef, Option<PyDictRef>)> {
|
||||
reduce_set(zelf.as_ref(), vm)
|
||||
}
|
||||
|
||||
#[pyclassmethod(magic)]
|
||||
fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
|
||||
PyGenericAlias::new(cls, args, vm)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hashable for PyFrozenSet {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::{PositionIterInternal, PyTypeRef};
|
||||
use super::{PositionIterInternal, PyGenericAlias, PyTypeRef};
|
||||
use crate::common::hash::PyHash;
|
||||
use crate::{
|
||||
function::{IntoPyObject, OptionalArg},
|
||||
@@ -303,6 +303,11 @@ impl PyTuple {
|
||||
};
|
||||
(tup_arg,)
|
||||
}
|
||||
|
||||
#[pyclassmethod(magic)]
|
||||
fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
|
||||
PyGenericAlias::new(cls, args, vm)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMapping for PyTuple {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::PyTypeRef;
|
||||
use super::{PyGenericAlias, PyTypeRef};
|
||||
use crate::common::hash::PyHash;
|
||||
use crate::{
|
||||
function::OptionalArg,
|
||||
@@ -83,6 +83,11 @@ impl PyWeak {
|
||||
format!("<weakref at {:#x}; dead>", id)
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclassmethod(magic)]
|
||||
fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
|
||||
PyGenericAlias::new(cls, args, vm)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hashable for PyWeak {
|
||||
|
||||
@@ -13,8 +13,8 @@ use crate::{
|
||||
bytes,
|
||||
getset::{IntoPyGetterFunc, IntoPySetterFunc, PyGetSet},
|
||||
object, pystr, PyBaseExceptionRef, PyBoundMethod, PyDict, PyDictRef, PyEllipsis, PyFloat,
|
||||
PyFrozenSet, PyInt, PyIntRef, PyList, PyListRef, PyNone, PyNotImplemented, PyStr, PyTuple,
|
||||
PyTupleRef, PyType, PyTypeRef,
|
||||
PyFrozenSet, PyGenericAlias, PyInt, PyIntRef, PyList, PyListRef, PyNone, PyNotImplemented,
|
||||
PyStr, PyTuple, PyTupleRef, PyType, PyTypeRef,
|
||||
},
|
||||
dictdatatype::Dict,
|
||||
exceptions,
|
||||
@@ -584,7 +584,12 @@ where
|
||||
match vm.get_special_method(self.to_owned(), "__getitem__")? {
|
||||
Ok(special_method) => return special_method.invoke((key,), vm),
|
||||
Err(obj) => {
|
||||
if obj.isinstance(&vm.ctx.types.type_type) {
|
||||
if obj.class().issubclass(&vm.ctx.types.type_type) {
|
||||
if obj.is(&vm.ctx.types.type_type) {
|
||||
return PyGenericAlias::new(obj.clone_class(), key.into_pyobject(vm), vm)
|
||||
.into_pyresult(vm);
|
||||
}
|
||||
|
||||
if let Some(class_getitem) = vm.get_attribute_opt(obj, "__class_getitem__")? {
|
||||
return vm.invoke(&class_getitem, (key,));
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ pub(crate) use _collections::make_module;
|
||||
|
||||
#[pymodule]
|
||||
mod _collections {
|
||||
use crate::builtins::PositionIterInternal;
|
||||
use crate::builtins::{PositionIterInternal, PyGenericAlias};
|
||||
use crate::common::lock::{PyMutex, PyRwLock, PyRwLockReadGuard, PyRwLockWriteGuard};
|
||||
use crate::{
|
||||
builtins::{
|
||||
@@ -529,6 +529,11 @@ mod _collections {
|
||||
};
|
||||
Ok(vm.new_pyobj((cls, value, vm.ctx.none(), PyDequeIterator::new(zelf))))
|
||||
}
|
||||
|
||||
#[pyclassmethod(magic)]
|
||||
fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
|
||||
PyGenericAlias::new(cls, args, vm)
|
||||
}
|
||||
}
|
||||
|
||||
impl Comparable for PyDeque {
|
||||
|
||||
@@ -7,7 +7,7 @@ mod decl {
|
||||
rc::PyRc,
|
||||
};
|
||||
use crate::{
|
||||
builtins::{int, PyInt, PyIntRef, PyTuple, PyTupleRef, PyTypeRef},
|
||||
builtins::{int, PyGenericAlias, PyInt, PyIntRef, PyTuple, PyTupleRef, PyTypeRef},
|
||||
function::{ArgCallable, FuncArgs, IntoPyObject, OptionalArg, OptionalOption, PosArgs},
|
||||
protocol::{PyIter, PyIterReturn},
|
||||
stdlib::sys,
|
||||
@@ -54,6 +54,11 @@ mod decl {
|
||||
}
|
||||
.into_ref_with_type(vm, cls)
|
||||
}
|
||||
|
||||
#[pyclassmethod(magic)]
|
||||
fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
|
||||
PyGenericAlias::new(cls, args, vm)
|
||||
}
|
||||
}
|
||||
impl IterNextIterable for PyItertoolsChain {}
|
||||
impl IterNext for PyItertoolsChain {
|
||||
|
||||
@@ -429,7 +429,9 @@ pub(super) mod _os {
|
||||
};
|
||||
use crate::common::lock::{OnceCell, PyRwLock};
|
||||
use crate::{
|
||||
builtins::{PyBytesRef, PyIntRef, PyStrRef, PyTuple, PyTupleRef, PyTypeRef},
|
||||
builtins::{
|
||||
PyBytesRef, PyGenericAlias, PyIntRef, PyStrRef, PyTuple, PyTupleRef, PyTypeRef,
|
||||
},
|
||||
crt_fd::{Fd, Offset},
|
||||
function::{ArgBytesLike, FuncArgs, IntoPyException, IntoPyObject, OptionalArg},
|
||||
protocol::PyIterReturn,
|
||||
@@ -890,6 +892,11 @@ pub(super) mod _os {
|
||||
Ok(format!("<{}>", zelf.class()))
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclassmethod(magic)]
|
||||
fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
|
||||
PyGenericAlias::new(cls, args, vm)
|
||||
}
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
|
||||
@@ -4,7 +4,8 @@ pub(crate) use _sre::make_module;
|
||||
mod _sre {
|
||||
use crate::{
|
||||
builtins::{
|
||||
PyCallableIterator, PyDictRef, PyInt, PyList, PyStr, PyStrRef, PyTuple, PyTupleRef,
|
||||
PyCallableIterator, PyDictRef, PyGenericAlias, PyInt, PyList, PyStr, PyStrRef, PyTuple,
|
||||
PyTupleRef, PyTypeRef,
|
||||
},
|
||||
common::{ascii, hash::PyHash},
|
||||
function::{ArgCallable, IntoPyObject, OptionalArg, PosArgs},
|
||||
@@ -513,6 +514,11 @@ mod _sre {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#[pyclassmethod(magic)]
|
||||
fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
|
||||
PyGenericAlias::new(cls, args, vm)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hashable for Pattern {
|
||||
@@ -787,6 +793,11 @@ mod _sre {
|
||||
}
|
||||
Some(slice_drive(&str_drive, start as usize, end as usize, vm))
|
||||
}
|
||||
|
||||
#[pyclassmethod(magic)]
|
||||
fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
|
||||
PyGenericAlias::new(cls, args, vm)
|
||||
}
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
|
||||
Reference in New Issue
Block a user