diff options
author | robot-piglet <robot-piglet@yandex-team.com> | 2024-05-08 11:10:05 +0300 |
---|---|---|
committer | robot-piglet <robot-piglet@yandex-team.com> | 2024-05-08 11:21:05 +0300 |
commit | 4196c19bc0753c1c2fe4d29f231bb49039430f9f (patch) | |
tree | f02ac799e7260353047c65d46fa5ce206f2e9303 | |
parent | c3d51e170ca39637234e72ae19dcbc53b737a83e (diff) | |
download | ydb-4196c19bc0753c1c2fe4d29f231bb49039430f9f.tar.gz |
Intermediate changes
32 files changed, 439 insertions, 410 deletions
diff --git a/contrib/python/freezegun/py3/.dist-info/METADATA b/contrib/python/freezegun/py3/.dist-info/METADATA index d5971e9f78..339ddf25b0 100644 --- a/contrib/python/freezegun/py3/.dist-info/METADATA +++ b/contrib/python/freezegun/py3/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: freezegun -Version: 1.4.0 +Version: 1.5.0 Summary: Let your Python tests travel through time Home-page: https://github.com/spulec/freezegun Author: Steve Pulec diff --git a/contrib/python/freezegun/py3/AUTHORS.rst b/contrib/python/freezegun/py3/AUTHORS.rst index 62e4962735..36afa4276c 100644 --- a/contrib/python/freezegun/py3/AUTHORS.rst +++ b/contrib/python/freezegun/py3/AUTHORS.rst @@ -21,3 +21,5 @@ Patches and Suggestions - `Hannes Ljungberg <hannes@5monkeys.se>`_ - `staticdev <staticdev-support@proton.me>`_ - `Marcin Sulikowski <https://github.com/marcinsulikowski>`_ +- `Ashish Patil <https://github.com/ashishnitinpatil>`_ +- `Victor Ferrer <https://github.com/vicfergar>`_ diff --git a/contrib/python/freezegun/py3/freezegun/__init__.py b/contrib/python/freezegun/py3/freezegun/__init__.py index 8fcb18ddfe..c2996b734e 100644 --- a/contrib/python/freezegun/py3/freezegun/__init__.py +++ b/contrib/python/freezegun/py3/freezegun/__init__.py @@ -9,7 +9,7 @@ from .api import freeze_time from .config import configure __title__ = 'freezegun' -__version__ = '1.4.0' +__version__ = '1.5.0' __author__ = 'Steve Pulec' __license__ = 'Apache License 2.0' __copyright__ = 'Copyright 2012 Steve Pulec' diff --git a/contrib/python/freezegun/py3/freezegun/__init__.pyi b/contrib/python/freezegun/py3/freezegun/__init__.pyi deleted file mode 100644 index 17dd86ac05..0000000000 --- a/contrib/python/freezegun/py3/freezegun/__init__.pyi +++ /dev/null @@ -1,2 +0,0 @@ -from .api import freeze_time as freeze_time - diff --git a/contrib/python/freezegun/py3/freezegun/api.py b/contrib/python/freezegun/py3/freezegun/api.py index 2917fa1f75..0f11e0c113 100644 --- a/contrib/python/freezegun/py3/freezegun/api.py +++ b/contrib/python/freezegun/py3/freezegun/api.py @@ -15,21 +15,33 @@ import warnings import types import numbers import inspect +from typing import TYPE_CHECKING, overload +from typing import Any, Awaitable, Callable, Dict, Iterator, List, Optional, Set, Type, TypeVar, Tuple, Union from dateutil import parser from dateutil.tz import tzlocal try: - from maya import MayaDT + from maya import MayaDT # type: ignore except ImportError: MayaDT = None +if TYPE_CHECKING: + from typing_extensions import ParamSpec + + P = ParamSpec("P") + +T = TypeVar("T") + _TIME_NS_PRESENT = hasattr(time, 'time_ns') _MONOTONIC_NS_PRESENT = hasattr(time, 'monotonic_ns') _PERF_COUNTER_NS_PRESENT = hasattr(time, 'perf_counter_ns') _EPOCH = datetime.datetime(1970, 1, 1) _EPOCHTZ = datetime.datetime(1970, 1, 1, tzinfo=dateutil.tz.UTC) +T2 = TypeVar("T2") +_Freezable = Union[str, datetime.datetime, datetime.date, datetime.timedelta, types.FunctionType, Callable[[], Union[str, datetime.datetime, datetime.date, datetime.timedelta]], Iterator[datetime.datetime]] + real_time = time.time real_localtime = time.localtime real_gmtime = time.gmtime @@ -57,38 +69,40 @@ _real_time_object_ids = {id(obj) for obj in real_date_objects} # time.clock is deprecated and was removed in Python 3.8 real_clock = getattr(time, 'clock', None) -freeze_factories = [] -tz_offsets = [] -ignore_lists = [] -tick_flags = [] +freeze_factories: List[Union["StepTickTimeFactory", "TickingDateTimeFactory", "FrozenDateTimeFactory"]] = [] +tz_offsets: List[datetime.timedelta] = [] +ignore_lists: List[Tuple[str, ...]] = [] +tick_flags: List[bool] = [] try: # noinspection PyUnresolvedReferences - real_uuid_generate_time = uuid._uuid_generate_time + real_uuid_generate_time = uuid._uuid_generate_time # type: ignore uuid_generate_time_attr = '_uuid_generate_time' except AttributeError: # noinspection PyUnresolvedReferences - uuid._load_system_functions() + if hasattr(uuid, '_load_system_functions'): + # A no-op after Python ~3.9, being removed in 3.13. + uuid._load_system_functions() # noinspection PyUnresolvedReferences - real_uuid_generate_time = uuid._generate_time_safe + real_uuid_generate_time = uuid._generate_time_safe # type: ignore uuid_generate_time_attr = '_generate_time_safe' except ImportError: real_uuid_generate_time = None - uuid_generate_time_attr = None + uuid_generate_time_attr = None # type: ignore try: # noinspection PyUnresolvedReferences - real_uuid_create = uuid._UuidCreate + real_uuid_create = uuid._UuidCreate # type: ignore except (AttributeError, ImportError): real_uuid_create = None # keep a cache of module attributes otherwise freezegun will need to analyze too many modules all the time -_GLOBAL_MODULES_CACHE = {} +_GLOBAL_MODULES_CACHE: Dict[str, Tuple[str, List[Tuple[str, Any]]]] = {} -def _get_module_attributes(module): - result = [] +def _get_module_attributes(module: types.ModuleType) -> List[Tuple[str, Any]]: + result: List[Tuple[str, Any]] = [] try: module_attributes = dir(module) except (ImportError, TypeError): @@ -104,7 +118,7 @@ def _get_module_attributes(module): return result -def _setup_module_cache(module): +def _setup_module_cache(module: types.ModuleType) -> None: date_attrs = [] all_module_attributes = _get_module_attributes(module) for attribute_name, attribute_value in all_module_attributes: @@ -113,7 +127,7 @@ def _setup_module_cache(module): _GLOBAL_MODULES_CACHE[module.__name__] = (_get_module_attributes_hash(module), date_attrs) -def _get_module_attributes_hash(module): +def _get_module_attributes_hash(module: types.ModuleType) -> str: try: module_dir = dir(module) except (ImportError, TypeError): @@ -121,7 +135,7 @@ def _get_module_attributes_hash(module): return f'{id(module)}-{hash(frozenset(module_dir))}' -def _get_cached_module_attributes(module): +def _get_cached_module_attributes(module: types.ModuleType) -> List[Tuple[str, Any]]: module_hash, cached_attrs = _GLOBAL_MODULES_CACHE.get(module.__name__, ('0', [])) if _get_module_attributes_hash(module) == module_hash: return cached_attrs @@ -142,7 +156,7 @@ _is_cpython = ( call_stack_inspection_limit = 5 -def _should_use_real_time(): +def _should_use_real_time() -> bool: if not call_stack_inspection_limit: return False @@ -153,38 +167,38 @@ def _should_use_real_time(): if not ignore_lists[-1]: return False - frame = inspect.currentframe().f_back.f_back + frame = inspect.currentframe().f_back.f_back # type: ignore for _ in range(call_stack_inspection_limit): - module_name = frame.f_globals.get('__name__') + module_name = frame.f_globals.get('__name__') # type: ignore if module_name and module_name.startswith(ignore_lists[-1]): return True - frame = frame.f_back + frame = frame.f_back # type: ignore if frame is None: break return False -def get_current_time(): +def get_current_time() -> datetime.datetime: return freeze_factories[-1]() -def fake_time(): +def fake_time() -> float: if _should_use_real_time(): return real_time() current_time = get_current_time() return calendar.timegm(current_time.timetuple()) + current_time.microsecond / 1000000.0 if _TIME_NS_PRESENT: - def fake_time_ns(): + def fake_time_ns() -> int: if _should_use_real_time(): return real_time_ns() - return int(int(fake_time()) * 1e9) + return int(fake_time() * 1e9) -def fake_localtime(t=None): +def fake_localtime(t: Optional[float]=None) -> time.struct_time: if t is not None: return real_localtime(t) if _should_use_real_time(): @@ -193,7 +207,7 @@ def fake_localtime(t=None): return shifted_time.timetuple() -def fake_gmtime(t=None): +def fake_gmtime(t: Optional[float]=None) -> time.struct_time: if t is not None: return real_gmtime(t) if _should_use_real_time(): @@ -201,7 +215,7 @@ def fake_gmtime(t=None): return get_current_time().timetuple() -def _get_fake_monotonic(): +def _get_fake_monotonic() -> float: # For monotonic timers like .monotonic(), .perf_counter(), etc current_time = get_current_time() return ( @@ -210,7 +224,7 @@ def _get_fake_monotonic(): ) -def _get_fake_monotonic_ns(): +def _get_fake_monotonic_ns() -> int: # For monotonic timers like .monotonic(), .perf_counter(), etc current_time = get_current_time() return ( @@ -219,14 +233,14 @@ def _get_fake_monotonic_ns(): ) * 1000 -def fake_monotonic(): +def fake_monotonic() -> float: if _should_use_real_time(): return real_monotonic() return _get_fake_monotonic() -def fake_perf_counter(): +def fake_perf_counter() -> float: if _should_use_real_time(): return real_perf_counter() @@ -234,7 +248,7 @@ def fake_perf_counter(): if _MONOTONIC_NS_PRESENT: - def fake_monotonic_ns(): + def fake_monotonic_ns() -> int: if _should_use_real_time(): return real_monotonic_ns() @@ -242,13 +256,13 @@ if _MONOTONIC_NS_PRESENT: if _PERF_COUNTER_NS_PRESENT: - def fake_perf_counter_ns(): + def fake_perf_counter_ns() -> int: if _should_use_real_time(): return real_perf_counter_ns() return _get_fake_monotonic_ns() -def fake_strftime(format, time_to_format=None): +def fake_strftime(format: Any, time_to_format: Any=None) -> str: if time_to_format is None: if not _should_use_real_time(): time_to_format = fake_localtime() @@ -259,12 +273,12 @@ def fake_strftime(format, time_to_format=None): return real_strftime(format, time_to_format) if real_clock is not None: - def fake_clock(): + def fake_clock() -> Any: if _should_use_real_time(): - return real_clock() + return real_clock() # type: ignore if len(freeze_factories) == 1: - return 0.0 if not tick_flags[-1] else real_clock() + return 0.0 if not tick_flags[-1] else real_clock() # type: ignore first_frozen_time = freeze_factories[0]() last_frozen_time = get_current_time() @@ -273,22 +287,22 @@ if real_clock is not None: total_seconds = timedelta.total_seconds() if tick_flags[-1]: - total_seconds += real_clock() + total_seconds += real_clock() # type: ignore return total_seconds class FakeDateMeta(type): @classmethod - def __instancecheck__(self, obj): + def __instancecheck__(self, obj: Any) -> bool: return isinstance(obj, real_date) @classmethod - def __subclasscheck__(cls, subclass): + def __subclasscheck__(cls, subclass: Any) -> bool: return issubclass(subclass, real_date) -def datetime_to_fakedatetime(datetime): +def datetime_to_fakedatetime(datetime: datetime.datetime) -> "FakeDatetime": return FakeDatetime(datetime.year, datetime.month, datetime.day, @@ -299,39 +313,39 @@ def datetime_to_fakedatetime(datetime): datetime.tzinfo) -def date_to_fakedate(date): +def date_to_fakedate(date: datetime.date) -> "FakeDate": return FakeDate(date.year, date.month, date.day) class FakeDate(real_date, metaclass=FakeDateMeta): - def __add__(self, other): + def __add__(self, other: Any) -> "FakeDate": result = real_date.__add__(self, other) if result is NotImplemented: return result return date_to_fakedate(result) - def __sub__(self, other): + def __sub__(self, other: Any) -> "FakeDate": # type: ignore result = real_date.__sub__(self, other) if result is NotImplemented: - return result + return result # type: ignore if isinstance(result, real_date): return date_to_fakedate(result) else: - return result + return result # type: ignore @classmethod - def today(cls): + def today(cls: Type["FakeDate"]) -> "FakeDate": result = cls._date_to_freeze() + cls._tz_offset() return date_to_fakedate(result) @staticmethod - def _date_to_freeze(): + def _date_to_freeze() -> datetime.datetime: return get_current_time() @classmethod - def _tz_offset(cls): + def _tz_offset(cls) -> datetime.timedelta: return tz_offsets[-1] FakeDate.min = date_to_fakedate(real_date.min) @@ -340,50 +354,51 @@ FakeDate.max = date_to_fakedate(real_date.max) class FakeDatetimeMeta(FakeDateMeta): @classmethod - def __instancecheck__(self, obj): + def __instancecheck__(self, obj: Any) -> bool: return isinstance(obj, real_datetime) @classmethod - def __subclasscheck__(cls, subclass): + def __subclasscheck__(cls, subclass: Any) -> bool: return issubclass(subclass, real_datetime) class FakeDatetime(real_datetime, FakeDate, metaclass=FakeDatetimeMeta): - def __add__(self, other): + def __add__(self, other: Any) -> "FakeDatetime": # type: ignore result = real_datetime.__add__(self, other) if result is NotImplemented: return result return datetime_to_fakedatetime(result) - def __sub__(self, other): + def __sub__(self, other: Any) -> "FakeDatetime": # type: ignore result = real_datetime.__sub__(self, other) if result is NotImplemented: - return result + return result # type: ignore if isinstance(result, real_datetime): return datetime_to_fakedatetime(result) else: - return result + return result # type: ignore - def astimezone(self, tz=None): + def astimezone(self, tz: Optional[datetime.tzinfo]=None) -> "FakeDatetime": if tz is None: tz = tzlocal() return datetime_to_fakedatetime(real_datetime.astimezone(self, tz)) @classmethod - def fromtimestamp(cls, t, tz=None): + def fromtimestamp(cls, t: float, tz: Optional[datetime.tzinfo]=None) -> "FakeDatetime": if tz is None: - return real_datetime.fromtimestamp( - t, tz=dateutil.tz.tzoffset("freezegun", cls._tz_offset()) - ).replace(tzinfo=None) - return datetime_to_fakedatetime(real_datetime.fromtimestamp(t, tz)) + tz = dateutil.tz.tzoffset("freezegun", cls._tz_offset()) + result = real_datetime.fromtimestamp(t, tz=tz).replace(tzinfo=None) + else: + result = real_datetime.fromtimestamp(t, tz) + return datetime_to_fakedatetime(result) - def timestamp(self): + def timestamp(self) -> float: if self.tzinfo is None: - return (self - _EPOCH - self._tz_offset()).total_seconds() - return (self - _EPOCHTZ).total_seconds() + return (self - _EPOCH - self._tz_offset()).total_seconds() # type: ignore + return (self - _EPOCHTZ).total_seconds() # type: ignore @classmethod - def now(cls, tz=None): + def now(cls, tz: Optional[datetime.tzinfo] = None) -> "FakeDatetime": now = cls._time_to_freeze() or real_datetime.now() if tz: result = tz.fromutc(now.replace(tzinfo=tz)) + cls._tz_offset() @@ -391,33 +406,34 @@ class FakeDatetime(real_datetime, FakeDate, metaclass=FakeDatetimeMeta): result = now + cls._tz_offset() return datetime_to_fakedatetime(result) - def date(self): + def date(self) -> "FakeDate": return date_to_fakedate(self) @property - def nanosecond(self): + def nanosecond(self) -> int: try: # noinspection PyUnresolvedReferences - return real_datetime.nanosecond + return real_datetime.nanosecond # type: ignore except AttributeError: return 0 @classmethod - def today(cls): + def today(cls) -> "FakeDatetime": return cls.now(tz=None) @classmethod - def utcnow(cls): + def utcnow(cls) -> "FakeDatetime": result = cls._time_to_freeze() or real_datetime.now(datetime.timezone.utc) return datetime_to_fakedatetime(result) @staticmethod - def _time_to_freeze(): + def _time_to_freeze() -> Optional[datetime.datetime]: if freeze_factories: return get_current_time() + return None @classmethod - def _tz_offset(cls): + def _tz_offset(cls) -> datetime.timedelta: return tz_offsets[-1] @@ -425,17 +441,17 @@ FakeDatetime.min = datetime_to_fakedatetime(real_datetime.min) FakeDatetime.max = datetime_to_fakedatetime(real_datetime.max) -def convert_to_timezone_naive(time_to_freeze): +def convert_to_timezone_naive(time_to_freeze: datetime.datetime) -> datetime.datetime: """ Converts a potentially timezone-aware datetime to be a naive UTC datetime """ if time_to_freeze.tzinfo: - time_to_freeze -= time_to_freeze.utcoffset() + time_to_freeze -= time_to_freeze.utcoffset() # type: ignore time_to_freeze = time_to_freeze.replace(tzinfo=None) return time_to_freeze -def pickle_fake_date(datetime_): +def pickle_fake_date(datetime_: datetime.date) -> Tuple[Type[FakeDate], Tuple[int, int, int]]: # A pickle function for FakeDate return FakeDate, ( datetime_.year, @@ -444,7 +460,7 @@ def pickle_fake_date(datetime_): ) -def pickle_fake_datetime(datetime_): +def pickle_fake_datetime(datetime_: datetime.datetime) -> Tuple[Type[FakeDatetime], Tuple[int, int, int, int, int, int, int, Optional[datetime.tzinfo]]]: # A pickle function for FakeDatetime return FakeDatetime, ( datetime_.year, @@ -458,7 +474,7 @@ def pickle_fake_datetime(datetime_): ) -def _parse_time_to_freeze(time_to_freeze_str): +def _parse_time_to_freeze(time_to_freeze_str: Optional[_Freezable]) -> datetime.datetime: """Parses all the possible inputs for freeze_time :returns: a naive ``datetime.datetime`` object """ @@ -472,12 +488,12 @@ def _parse_time_to_freeze(time_to_freeze_str): elif isinstance(time_to_freeze_str, datetime.timedelta): time_to_freeze = datetime.datetime.now(datetime.timezone.utc) + time_to_freeze_str else: - time_to_freeze = parser.parse(time_to_freeze_str) + time_to_freeze = parser.parse(time_to_freeze_str) # type: ignore return convert_to_timezone_naive(time_to_freeze) -def _parse_tz_offset(tz_offset): +def _parse_tz_offset(tz_offset: Union[datetime.timedelta, float]) -> datetime.timedelta: if isinstance(tz_offset, datetime.timedelta): return tz_offset else: @@ -486,30 +502,44 @@ def _parse_tz_offset(tz_offset): class TickingDateTimeFactory: - def __init__(self, time_to_freeze, start): + def __init__(self, time_to_freeze: datetime.datetime, start: datetime.datetime): self.time_to_freeze = time_to_freeze self.start = start - def __call__(self): + def __call__(self) -> datetime.datetime: return self.time_to_freeze + (real_datetime.now() - self.start) + def tick(self, delta: Union[datetime.timedelta, int]=datetime.timedelta(seconds=1)) -> datetime.datetime: + if isinstance(delta, numbers.Real): + # noinspection PyTypeChecker + self.move_to(self.time_to_freeze + datetime.timedelta(seconds=delta)) + else: + self.move_to(self.time_to_freeze + delta) # type: ignore + return self.time_to_freeze + + def move_to(self, target_datetime: _Freezable) -> None: + """Moves frozen date to the given ``target_datetime``""" + self.start = real_datetime.now() + self.time_to_freeze = _parse_time_to_freeze(target_datetime) + class FrozenDateTimeFactory: - def __init__(self, time_to_freeze): + def __init__(self, time_to_freeze: datetime.datetime): self.time_to_freeze = time_to_freeze - def __call__(self): + def __call__(self) -> datetime.datetime: return self.time_to_freeze - def tick(self, delta=datetime.timedelta(seconds=1)): + def tick(self, delta: Union[datetime.timedelta, int]=datetime.timedelta(seconds=1)) -> datetime.datetime: if isinstance(delta, numbers.Real): # noinspection PyTypeChecker self.time_to_freeze += datetime.timedelta(seconds=delta) else: - self.time_to_freeze += delta + self.time_to_freeze += delta # type: ignore + return self.time_to_freeze - def move_to(self, target_datetime): + def move_to(self, target_datetime: _Freezable) -> None: """Moves frozen date to the given ``target_datetime``""" target_datetime = _parse_time_to_freeze(target_datetime) delta = target_datetime - self.time_to_freeze @@ -518,24 +548,25 @@ class FrozenDateTimeFactory: class StepTickTimeFactory: - def __init__(self, time_to_freeze, step_width): + def __init__(self, time_to_freeze: datetime.datetime, step_width: float): self.time_to_freeze = time_to_freeze self.step_width = step_width - def __call__(self): + def __call__(self) -> datetime.datetime: return_time = self.time_to_freeze self.tick() return return_time - def tick(self, delta=None): + def tick(self, delta: Union[datetime.timedelta, int, None]=None) -> datetime.datetime: if not delta: delta = datetime.timedelta(seconds=self.step_width) - self.time_to_freeze += delta + self.time_to_freeze += delta # type: ignore + return self.time_to_freeze - def update_step_width(self, step_width): + def update_step_width(self, step_width: float) -> None: self.step_width = step_width - def move_to(self, target_datetime): + def move_to(self, target_datetime: _Freezable) -> None: """Moves frozen date to the given ``target_datetime``""" target_datetime = _parse_time_to_freeze(target_datetime) delta = target_datetime - self.time_to_freeze @@ -544,26 +575,48 @@ class StepTickTimeFactory: class _freeze_time: - def __init__(self, time_to_freeze_str, tz_offset, ignore, tick, as_arg, as_kwarg, auto_tick_seconds, real_asyncio): + def __init__( + self, + time_to_freeze_str: Optional[_Freezable], + tz_offset: Union[int, datetime.timedelta], + ignore: List[str], + tick: bool, + as_arg: bool, + as_kwarg: str, + auto_tick_seconds: float, + real_asyncio: Optional[bool], + ): self.time_to_freeze = _parse_time_to_freeze(time_to_freeze_str) self.tz_offset = _parse_tz_offset(tz_offset) self.ignore = tuple(ignore) self.tick = tick self.auto_tick_seconds = auto_tick_seconds - self.undo_changes = [] - self.modules_at_start = set() + self.undo_changes: List[Tuple[types.ModuleType, str, Any]] = [] + self.modules_at_start: Set[str] = set() self.as_arg = as_arg self.as_kwarg = as_kwarg self.real_asyncio = real_asyncio - def __call__(self, func): + @overload + def __call__(self, func: Type[T2]) -> Type[T2]: + ... + + @overload + def __call__(self, func: "Callable[P, Awaitable[Any]]") -> "Callable[P, Awaitable[Any]]": + ... + + @overload + def __call__(self, func: "Callable[P, T]") -> "Callable[P, T]": + ... + + def __call__(self, func: Union[Type[T2], "Callable[P, Awaitable[Any]]", "Callable[P, T]"]) -> Union[Type[T2], "Callable[P, Awaitable[Any]]", "Callable[P, T]"]: # type: ignore if inspect.isclass(func): return self.decorate_class(func) elif inspect.iscoroutinefunction(func): return self.decorate_coroutine(func) - return self.decorate_callable(func) + return self.decorate_callable(func) # type: ignore - def decorate_class(self, klass): + def decorate_class(self, klass: Type[T2]) -> Type[T2]: if issubclass(klass, unittest.TestCase): # If it's a TestCase, we freeze time around setup and teardown, as well # as for every test case. This requires some care to avoid freezing @@ -574,41 +627,39 @@ class _freeze_time: orig_tearDownClass = klass.tearDownClass # noinspection PyDecorator - @classmethod - def setUpClass(cls): + @classmethod # type: ignore + def setUpClass(cls: type) -> None: self.start() if orig_setUpClass is not None: orig_setUpClass() self.stop() # noinspection PyDecorator - @classmethod - def tearDownClass(cls): + @classmethod # type: ignore + def tearDownClass(cls: type) -> None: self.start() if orig_tearDownClass is not None: orig_tearDownClass() self.stop() - klass.setUpClass = setUpClass - klass.tearDownClass = tearDownClass + klass.setUpClass = setUpClass # type: ignore + klass.tearDownClass = tearDownClass # type: ignore orig_setUp = klass.setUp orig_tearDown = klass.tearDown - def setUp(*args, **kwargs): + def setUp(*args: Any, **kwargs: Any) -> None: self.start() if orig_setUp is not None: orig_setUp(*args, **kwargs) - def tearDown(*args, **kwargs): + def tearDown(*args: Any, **kwargs: Any) -> None: if orig_tearDown is not None: orig_tearDown(*args, **kwargs) self.stop() - klass.setUp = setUp - klass.tearDown = tearDown - - return klass + klass.setUp = setUp # type: ignore[method-assign] + klass.tearDown = tearDown # type: ignore[method-assign] else: @@ -629,18 +680,18 @@ class _freeze_time: except (AttributeError, TypeError): # Sometimes we can't set this for built-in types and custom callables continue - return klass + return klass - def __enter__(self): + def __enter__(self) -> Union[StepTickTimeFactory, TickingDateTimeFactory, FrozenDateTimeFactory]: return self.start() - def __exit__(self, *args): + def __exit__(self, *args: Any) -> None: self.stop() - def start(self): + def start(self) -> Union[StepTickTimeFactory, TickingDateTimeFactory, FrozenDateTimeFactory]: if self.auto_tick_seconds: - freeze_factory = StepTickTimeFactory(self.time_to_freeze, self.auto_tick_seconds) + freeze_factory: Union[StepTickTimeFactory, TickingDateTimeFactory, FrozenDateTimeFactory] = StepTickTimeFactory(self.time_to_freeze, self.auto_tick_seconds) elif self.tick: freeze_factory = TickingDateTimeFactory(self.time_to_freeze, real_datetime.now()) else: @@ -656,19 +707,19 @@ class _freeze_time: return freeze_factory # Change the modules - datetime.datetime = FakeDatetime - datetime.date = FakeDate + datetime.datetime = FakeDatetime # type: ignore[misc] + datetime.date = FakeDate # type: ignore[misc] time.time = fake_time time.monotonic = fake_monotonic time.perf_counter = fake_perf_counter - time.localtime = fake_localtime - time.gmtime = fake_gmtime - time.strftime = fake_strftime + time.localtime = fake_localtime # type: ignore + time.gmtime = fake_gmtime # type: ignore + time.strftime = fake_strftime # type: ignore if uuid_generate_time_attr: setattr(uuid, uuid_generate_time_attr, None) - uuid._UuidCreate = None - uuid._last_timestamp = None + uuid._UuidCreate = None # type: ignore[attr-defined] + uuid._last_timestamp = None # type: ignore[attr-defined] copyreg.dispatch_table[real_datetime] = pickle_fake_datetime copyreg.dispatch_table[real_date] = pickle_fake_date @@ -699,10 +750,10 @@ class _freeze_time: if real_clock is not None: # time.clock is deprecated and was removed in Python 3.8 - time.clock = fake_clock + time.clock = fake_clock # type: ignore[attr-defined] to_patch.append(('real_clock', real_clock, fake_clock)) - self.fake_names = tuple(fake.__name__ for real_name, real, fake in to_patch) + self.fake_names = tuple(fake.__name__ for real_name, real, fake in to_patch) # type: ignore self.reals = {id(fake): real for real_name, real, fake in to_patch} fakes = {id(real): fake for real_name, real, fake in to_patch} add_change = self.undo_changes.append @@ -741,20 +792,20 @@ class _freeze_time: event_loop = asyncio.new_event_loop() event_loop.close() EventLoopClass = type(event_loop) - add_change((EventLoopClass, "time", EventLoopClass.time)) - EventLoopClass.time = lambda self: real_monotonic() + add_change((EventLoopClass, "time", EventLoopClass.time)) # type: ignore + EventLoopClass.time = lambda self: real_monotonic() # type: ignore[method-assign] return freeze_factory - def stop(self): + def stop(self) -> None: freeze_factories.pop() ignore_lists.pop() tick_flags.pop() tz_offsets.pop() if not freeze_factories: - datetime.datetime = real_datetime - datetime.date = real_date + datetime.datetime = real_datetime # type: ignore[misc] + datetime.date = real_date # type: ignore[misc] copyreg.dispatch_table.pop(real_datetime) copyreg.dispatch_table.pop(real_date) for module_or_object, attribute, original_value in self.undo_changes: @@ -794,7 +845,7 @@ class _freeze_time: time.gmtime = real_gmtime time.localtime = real_localtime time.strftime = real_strftime - time.clock = real_clock + time.clock = real_clock # type: ignore[attr-defined] if _TIME_NS_PRESENT: time.time_ns = real_time_ns @@ -807,33 +858,33 @@ class _freeze_time: if uuid_generate_time_attr: setattr(uuid, uuid_generate_time_attr, real_uuid_generate_time) - uuid._UuidCreate = real_uuid_create - uuid._last_timestamp = None + uuid._UuidCreate = real_uuid_create # type: ignore[attr-defined] + uuid._last_timestamp = None # type: ignore[attr-defined] - def decorate_coroutine(self, coroutine): + def decorate_coroutine(self, coroutine: "Callable[P, Awaitable[T]]") -> "Callable[P, Awaitable[T]]": return wrap_coroutine(self, coroutine) - def decorate_callable(self, func): - def wrapper(*args, **kwargs): + def decorate_callable(self, func: "Callable[P, T]") -> "Callable[P, T]": + @functools.wraps(func) + def wrapper(*args: "P.args", **kwargs: "P.kwargs") -> T: with self as time_factory: if self.as_arg and self.as_kwarg: assert False, "You can't specify both as_arg and as_kwarg at the same time. Pick one." elif self.as_arg: - result = func(time_factory, *args, **kwargs) + result = func(time_factory, *args, **kwargs) # type: ignore elif self.as_kwarg: kwargs[self.as_kwarg] = time_factory result = func(*args, **kwargs) else: result = func(*args, **kwargs) return result - functools.update_wrapper(wrapper, func) return wrapper -def freeze_time(time_to_freeze=None, tz_offset=0, ignore=None, tick=False, as_arg=False, as_kwarg='', - auto_tick_seconds=0, real_asyncio=False): - acceptable_times = (type(None), str, datetime.date, datetime.timedelta, +def freeze_time(time_to_freeze: Optional[_Freezable]=None, tz_offset: Union[int, datetime.timedelta]=0, ignore: Optional[List[str]]=None, tick: bool=False, as_arg: bool=False, as_kwarg: str='', + auto_tick_seconds: float=0, real_asyncio: bool=False) -> _freeze_time: + acceptable_times: Any = (type(None), str, datetime.date, datetime.timedelta, types.FunctionType, types.GeneratorType) if MayaDT is not None: @@ -883,10 +934,10 @@ except ImportError: pass else: # These are copied from Python sqlite3.dbapi2 - def adapt_date(val): + def adapt_date(val: datetime.date) -> str: return val.isoformat() - def adapt_datetime(val): + def adapt_datetime(val: datetime.datetime) -> str: return val.isoformat(" ") sqlite3.register_adapter(FakeDate, adapt_date) diff --git a/contrib/python/freezegun/py3/freezegun/api.pyi b/contrib/python/freezegun/py3/freezegun/api.pyi deleted file mode 100644 index 2ff9bdc5da..0000000000 --- a/contrib/python/freezegun/py3/freezegun/api.pyi +++ /dev/null @@ -1,62 +0,0 @@ -from collections.abc import Awaitable, Callable, Iterator, Sequence -from datetime import date, datetime, timedelta -from numbers import Real -from typing import Any, TypeVar, overload -from typing_extensions import TypeAlias - -_T = TypeVar("_T") -_Freezable: TypeAlias = str | datetime | date | timedelta - -class TickingDateTimeFactory: - def __init__(self, time_to_freeze: datetime, start: datetime) -> None: ... - def __call__(self) -> datetime: ... - -class FrozenDateTimeFactory: - def __init__(self, time_to_freeze: datetime) -> None: ... - def __call__(self) -> datetime: ... - def tick(self, delta: float | Real | timedelta = ...) -> None: ... - def move_to(self, target_datetime: _Freezable | None) -> None: ... - -class StepTickTimeFactory: - def __init__(self, time_to_freeze: datetime, step_width: float) -> None: ... - def __call__(self) -> datetime: ... - def tick(self, delta: timedelta | None = ...) -> None: ... - def update_step_width(self, step_width: float) -> None: ... - def move_to(self, target_datetime: _Freezable | None) -> None: ... - -class _freeze_time: - def __init__( - self, - time_to_freeze_str: _Freezable | None, - tz_offset: int | timedelta, - ignore: Sequence[str], - tick: bool, - as_arg: bool, - as_kwarg: str, - auto_tick_seconds: float, - real_asyncio: bool, - ) -> None: ... - @overload - def __call__(self, func: type[_T]) -> type[_T]: ... - @overload - def __call__(self, func: Callable[..., Awaitable[_T]]) -> Callable[..., Awaitable[_T]]: ... - @overload - def __call__(self, func: Callable[..., _T]) -> Callable[..., _T]: ... - def __enter__(self) -> FrozenDateTimeFactory | StepTickTimeFactory: ... - def __exit__(self, *args: object) -> None: ... - def start(self) -> Any: ... - def stop(self) -> None: ... - def decorate_class(self, klass: type[_T]) -> _T: ... - def decorate_coroutine(self, coroutine: _T) -> _T: ... - def decorate_callable(self, func: Callable[..., _T]) -> Callable[..., _T]: ... - -def freeze_time( - time_to_freeze: _Freezable | Callable[..., _Freezable] | Iterator[_Freezable] | None = ..., - tz_offset: int | timedelta | None = ..., - ignore: Sequence[str] | None = ..., - tick: bool | None = ..., - as_arg: bool | None = ..., - as_kwarg: str | None = ..., - auto_tick_seconds: float | None = ..., - real_asyncio: bool | None = ... -) -> _freeze_time: ... diff --git a/contrib/python/freezegun/py3/freezegun/config.py b/contrib/python/freezegun/py3/freezegun/config.py index cbbd8ae6a4..fc2cf98b23 100644 --- a/contrib/python/freezegun/py3/freezegun/config.py +++ b/contrib/python/freezegun/py3/freezegun/config.py @@ -8,7 +8,7 @@ DEFAULT_IGNORE_LIST = [ 'google.gax', 'threading', 'multiprocessing', - 'Queue', + 'queue', 'selenium', '_pytest.terminal.', '_pytest.runner.', @@ -32,10 +32,10 @@ class ConfigurationError(Exception): def configure(default_ignore_list: Optional[List[str]]=None, extend_ignore_list: Optional[List[str]]=None) -> None: if default_ignore_list is not None and extend_ignore_list is not None: raise ConfigurationError("Either default_ignore_list or extend_ignore_list might be given, not both") - if default_ignore_list: + if default_ignore_list is not None: settings.default_ignore_list = default_ignore_list if extend_ignore_list: - settings.default_ignore_list = [*settings.default_ignore_list, *extend_ignore_list] + settings.default_ignore_list = list(dict.fromkeys([*settings.default_ignore_list, *extend_ignore_list])) def reset_config() -> None: diff --git a/contrib/python/freezegun/py3/ya.make b/contrib/python/freezegun/py3/ya.make index d6c429a154..124a48d771 100644 --- a/contrib/python/freezegun/py3/ya.make +++ b/contrib/python/freezegun/py3/ya.make @@ -4,7 +4,7 @@ PY3_LIBRARY() PROVIDES(freezegun) -VERSION(1.4.0) +VERSION(1.5.0) LICENSE(Apache-2.0) @@ -17,10 +17,8 @@ NO_LINT() PY_SRCS( TOP_LEVEL freezegun/__init__.py - freezegun/__init__.pyi freezegun/_async.py freezegun/api.py - freezegun/api.pyi freezegun/config.py ) diff --git a/contrib/python/websocket-client/py3/.dist-info/METADATA b/contrib/python/websocket-client/py3/.dist-info/METADATA index 61847cefe3..563e5c0526 100644 --- a/contrib/python/websocket-client/py3/.dist-info/METADATA +++ b/contrib/python/websocket-client/py3/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: websocket-client -Version: 1.7.0 +Version: 1.8.0 Summary: WebSocket client for Python with low level API options Home-page: https://github.com/websocket-client/websocket-client.git Download-URL: https://github.com/websocket-client/websocket-client/releases @@ -32,6 +32,7 @@ License-File: LICENSE Provides-Extra: docs Requires-Dist: Sphinx >=6.0 ; extra == 'docs' Requires-Dist: sphinx-rtd-theme >=1.1.0 ; extra == 'docs' +Requires-Dist: myst-parser >=2.0.0 ; extra == 'docs' Provides-Extra: optional Requires-Dist: python-socks ; extra == 'optional' Requires-Dist: wsaccel ; extra == 'optional' @@ -66,20 +67,20 @@ Please see the [contribution guidelines](https://github.com/websocket-client/web ## Installation -You can use either `python3 setup.py install` or `pip3 install websocket-client` -to install. This module is tested on Python 3.8+. +You can use `pip install websocket-client` to install, or `pip install -e .` +to install from a local copy of the code. This module is tested on Python 3.8+. There are several optional dependencies that can be installed to enable specific websocket-client features. - To install `python-socks` for proxy usage and `wsaccel` for a minor performance boost, use: - `pip3 install websocket-client[optional]` + `pip install websocket-client[optional]` - To install `websockets` to run unit tests using the local echo server, use: - `pip3 install websocket-client[test]` + `pip install websocket-client[test]` - To install `Sphinx` and `sphinx_rtd_theme` to build project documentation, use: - `pip3 install websocket-client[docs]` + `pip install websocket-client[docs]` While not a strict dependency, [rel](https://github.com/bubbleboy14/registeredeventlistener) -is useful when using `run_forever` with automatic reconnect. Install rel with `pip3 install rel`. +is useful when using `run_forever` with automatic reconnect. Install rel with `pip install rel`. Footnote: Some shells, such as zsh, require you to escape the `[` and `]` characters with a `\`. diff --git a/contrib/python/websocket-client/py3/LICENSE b/contrib/python/websocket-client/py3/LICENSE index 88a0d3eb19..62a54ca499 100644 --- a/contrib/python/websocket-client/py3/LICENSE +++ b/contrib/python/websocket-client/py3/LICENSE @@ -187,7 +187,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2023 engn33r + Copyright 2024 engn33r Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/contrib/python/websocket-client/py3/README.md b/contrib/python/websocket-client/py3/README.md index 0bdc40e1c0..14b6b506ce 100644 --- a/contrib/python/websocket-client/py3/README.md +++ b/contrib/python/websocket-client/py3/README.md @@ -26,20 +26,20 @@ Please see the [contribution guidelines](https://github.com/websocket-client/web ## Installation -You can use either `python3 setup.py install` or `pip3 install websocket-client` -to install. This module is tested on Python 3.8+. +You can use `pip install websocket-client` to install, or `pip install -e .` +to install from a local copy of the code. This module is tested on Python 3.8+. There are several optional dependencies that can be installed to enable specific websocket-client features. - To install `python-socks` for proxy usage and `wsaccel` for a minor performance boost, use: - `pip3 install websocket-client[optional]` + `pip install websocket-client[optional]` - To install `websockets` to run unit tests using the local echo server, use: - `pip3 install websocket-client[test]` + `pip install websocket-client[test]` - To install `Sphinx` and `sphinx_rtd_theme` to build project documentation, use: - `pip3 install websocket-client[docs]` + `pip install websocket-client[docs]` While not a strict dependency, [rel](https://github.com/bubbleboy14/registeredeventlistener) -is useful when using `run_forever` with automatic reconnect. Install rel with `pip3 install rel`. +is useful when using `run_forever` with automatic reconnect. Install rel with `pip install rel`. Footnote: Some shells, such as zsh, require you to escape the `[` and `]` characters with a `\`. diff --git a/contrib/python/websocket-client/py3/websocket/__init__.py b/contrib/python/websocket-client/py3/websocket/__init__.py index 2ff7587995..559b38a6b7 100644 --- a/contrib/python/websocket-client/py3/websocket/__init__.py +++ b/contrib/python/websocket-client/py3/websocket/__init__.py @@ -2,7 +2,7 @@ __init__.py websocket - WebSocket client library for Python -Copyright 2023 engn33r +Copyright 2024 engn33r Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,10 +17,10 @@ See the License for the specific language governing permissions and limitations under the License. """ from ._abnf import * -from ._app import WebSocketApp, setReconnect +from ._app import WebSocketApp as WebSocketApp, setReconnect as setReconnect from ._core import * from ._exceptions import * from ._logging import * from ._socket import * -__version__ = "1.7.0" +__version__ = "1.8.0" diff --git a/contrib/python/websocket-client/py3/websocket/_abnf.py b/contrib/python/websocket-client/py3/websocket/_abnf.py index 416ebc8ffc..d7754e0de2 100644 --- a/contrib/python/websocket-client/py3/websocket/_abnf.py +++ b/contrib/python/websocket-client/py3/websocket/_abnf.py @@ -5,14 +5,14 @@ import sys from threading import Lock from typing import Callable, Optional, Union -from ._exceptions import * +from ._exceptions import WebSocketPayloadException, WebSocketProtocolException from ._utils import validate_utf8 """ _abnf.py websocket - WebSocket client library for Python -Copyright 2023 engn33r +Copyright 2024 engn33r Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/contrib/python/websocket-client/py3/websocket/_app.py b/contrib/python/websocket-client/py3/websocket/_app.py index 4d8af3b5b1..9fee76546b 100644 --- a/contrib/python/websocket-client/py3/websocket/_app.py +++ b/contrib/python/websocket-client/py3/websocket/_app.py @@ -13,13 +13,14 @@ from ._exceptions import ( WebSocketException, WebSocketTimeoutException, ) +from ._ssl_compat import SSLEOFError from ._url import parse_url """ _app.py websocket - WebSocket client library for Python -Copyright 2023 engn33r +Copyright 2024 engn33r Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -165,6 +166,7 @@ class WebSocketApp: url: str, header: Union[list, dict, Callable, None] = None, on_open: Optional[Callable[[WebSocket], None]] = None, + on_reconnect: Optional[Callable[[WebSocket], None]] = None, on_message: Optional[Callable[[WebSocket, Any], None]] = None, on_error: Optional[Callable[[WebSocket, Any], None]] = None, on_close: Optional[Callable[[WebSocket, Any, Any], None]] = None, @@ -194,6 +196,10 @@ class WebSocketApp: Callback object which is called at opening websocket. on_open has one argument. The 1st argument is this class object. + on_reconnect: function + Callback object which is called at reconnecting websocket. + on_reconnect has one argument. + The 1st argument is this class object. on_message: function Callback object which is called when received data. on_message has 2 arguments. @@ -244,6 +250,7 @@ class WebSocketApp: self.cookie = cookie self.on_open = on_open + self.on_reconnect = on_reconnect self.on_message = on_message self.on_data = on_data self.on_error = on_error @@ -424,6 +431,7 @@ class WebSocketApp: self.ping_interval = ping_interval self.ping_timeout = ping_timeout self.ping_payload = ping_payload + self.has_done_teardown = False self.keep_running = True def teardown(close_frame: ABNF = None): @@ -495,7 +503,10 @@ class WebSocketApp: if self.ping_interval: self._start_ping_thread() - self._callback(self.on_open) + if reconnecting and self.on_reconnect: + self._callback(self.on_reconnect) + else: + self._callback(self.on_open) dispatcher.read(self.sock.sock, read, check) except ( @@ -516,9 +527,10 @@ class WebSocketApp: except ( WebSocketConnectionClosedException, KeyboardInterrupt, + SSLEOFError, ) as e: if custom_dispatcher: - return handleDisconnect(e) + return handleDisconnect(e, bool(reconnect)) else: raise e diff --git a/contrib/python/websocket-client/py3/websocket/_cookiejar.py b/contrib/python/websocket-client/py3/websocket/_cookiejar.py index 61079402be..7480e5fc21 100644 --- a/contrib/python/websocket-client/py3/websocket/_cookiejar.py +++ b/contrib/python/websocket-client/py3/websocket/_cookiejar.py @@ -5,7 +5,7 @@ from typing import Optional _cookiejar.py websocket - WebSocket client library for Python -Copyright 2023 engn33r +Copyright 2024 engn33r Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -23,13 +23,13 @@ limitations under the License. class SimpleCookieJar: def __init__(self) -> None: - self.jar: dict = dict() + self.jar: dict = {} def add(self, set_cookie: Optional[str]) -> None: if set_cookie: - simpleCookie = http.cookies.SimpleCookie(set_cookie) + simple_cookie = http.cookies.SimpleCookie(set_cookie) - for k, v in simpleCookie.items(): + for v in simple_cookie.values(): if domain := v.get("domain"): if not domain.startswith("."): domain = f".{domain}" @@ -38,25 +38,25 @@ class SimpleCookieJar: if self.jar.get(domain) else http.cookies.SimpleCookie() ) - cookie.update(simpleCookie) + cookie.update(simple_cookie) self.jar[domain.lower()] = cookie def set(self, set_cookie: str) -> None: if set_cookie: - simpleCookie = http.cookies.SimpleCookie(set_cookie) + simple_cookie = http.cookies.SimpleCookie(set_cookie) - for k, v in simpleCookie.items(): + for v in simple_cookie.values(): if domain := v.get("domain"): if not domain.startswith("."): domain = f".{domain}" - self.jar[domain.lower()] = simpleCookie + self.jar[domain.lower()] = simple_cookie def get(self, host: str) -> str: if not host: return "" cookies = [] - for domain, simpleCookie in self.jar.items(): + for domain, _ in self.jar.items(): host = host.lower() if host.endswith(domain) or host == domain[1:]: cookies.append(self.jar.get(domain)) @@ -66,7 +66,7 @@ class SimpleCookieJar: None, sorted( [ - "%s=%s" % (k, v.value) + f"{k}={v.value}" for cookie in filter(None, cookies) for k, v in cookie.items() ] diff --git a/contrib/python/websocket-client/py3/websocket/_core.py b/contrib/python/websocket-client/py3/websocket/_core.py index f28ca4b917..f940ed0573 100644 --- a/contrib/python/websocket-client/py3/websocket/_core.py +++ b/contrib/python/websocket-client/py3/websocket/_core.py @@ -5,20 +5,20 @@ import time from typing import Optional, Union # websocket modules -from ._abnf import * -from ._exceptions import * -from ._handshake import * -from ._http import * -from ._logging import * -from ._socket import * -from ._ssl_compat import * -from ._utils import * +from ._abnf import ABNF, STATUS_NORMAL, continuous_frame, frame_buffer +from ._exceptions import WebSocketProtocolException, WebSocketConnectionClosedException +from ._handshake import SUPPORTED_REDIRECT_STATUSES, handshake +from ._http import connect, proxy_info +from ._logging import debug, error, trace, isEnabledForError, isEnabledForTrace +from ._socket import getdefaulttimeout, recv, send, sock_opt +from ._ssl_compat import ssl +from ._utils import NoLock """ _core.py websocket - WebSocket client library for Python -Copyright 2023 engn33r +Copyright 2024 engn33r Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -259,7 +259,7 @@ class WebSocket: try: self.handshake_response = handshake(self.sock, url, *addrs, **options) - for attempt in range(options.pop("redirect_limit", 3)): + for _ in range(options.pop("redirect_limit", 3)): if self.handshake_response.status in SUPPORTED_REDIRECT_STATUSES: url = self.handshake_response.headers["location"] self.sock.close() diff --git a/contrib/python/websocket-client/py3/websocket/_exceptions.py b/contrib/python/websocket-client/py3/websocket/_exceptions.py index c146fa5d1c..cd196e44a3 100644 --- a/contrib/python/websocket-client/py3/websocket/_exceptions.py +++ b/contrib/python/websocket-client/py3/websocket/_exceptions.py @@ -2,7 +2,7 @@ _exceptions.py websocket - WebSocket client library for Python -Copyright 2023 engn33r +Copyright 2024 engn33r Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/contrib/python/websocket-client/py3/websocket/_handshake.py b/contrib/python/websocket-client/py3/websocket/_handshake.py index e63dc97931..7bd61b82f4 100644 --- a/contrib/python/websocket-client/py3/websocket/_handshake.py +++ b/contrib/python/websocket-client/py3/websocket/_handshake.py @@ -2,7 +2,7 @@ _handshake.py websocket - WebSocket client library for Python -Copyright 2023 engn33r +Copyright 2024 engn33r Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -23,10 +23,10 @@ from base64 import encodebytes as base64encode from http import HTTPStatus from ._cookiejar import SimpleCookieJar -from ._exceptions import * -from ._http import * -from ._logging import * -from ._socket import * +from ._exceptions import WebSocketException, WebSocketBadStatusException +from ._http import read_headers +from ._logging import dump, error +from ._socket import send __all__ = ["handshake_response", "handshake", "SUPPORTED_REDIRECT_STATUSES"] diff --git a/contrib/python/websocket-client/py3/websocket/_http.py b/contrib/python/websocket-client/py3/websocket/_http.py index 977a30f199..9b1bf859d9 100644 --- a/contrib/python/websocket-client/py3/websocket/_http.py +++ b/contrib/python/websocket-client/py3/websocket/_http.py @@ -2,7 +2,7 @@ _http.py websocket - WebSocket client library for Python -Copyright 2023 engn33r +Copyright 2024 engn33r Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -21,11 +21,15 @@ import os import socket from base64 import encodebytes as base64encode -from ._exceptions import * -from ._logging import * -from ._socket import * -from ._ssl_compat import * -from ._url import * +from ._exceptions import ( + WebSocketAddressException, + WebSocketException, + WebSocketProxyException, +) +from ._logging import debug, dump, trace +from ._socket import DEFAULT_SOCKET_OPTION, recv_line, send +from ._ssl_compat import HAVE_SSL, ssl +from ._url import get_proxy_info, parse_url __all__ = ["proxy_info", "connect", "read_headers"] @@ -283,22 +287,22 @@ def _wrap_sni_socket(sock: socket.socket, sslopt: dict, hostname, check_hostname def _ssl_socket(sock: socket.socket, user_sslopt: dict, hostname): - sslopt: dict = dict(cert_reqs=ssl.CERT_REQUIRED) + sslopt: dict = {"cert_reqs": ssl.CERT_REQUIRED} sslopt.update(user_sslopt) - certPath = os.environ.get("WEBSOCKET_CLIENT_CA_BUNDLE") + cert_path = os.environ.get("WEBSOCKET_CLIENT_CA_BUNDLE") if ( - certPath - and os.path.isfile(certPath) + cert_path + and os.path.isfile(cert_path) and user_sslopt.get("ca_certs", None) is None ): - sslopt["ca_certs"] = certPath + sslopt["ca_certs"] = cert_path elif ( - certPath - and os.path.isdir(certPath) + cert_path + and os.path.isdir(cert_path) and user_sslopt.get("ca_cert_path", None) is None ): - sslopt["ca_cert_path"] = certPath + sslopt["ca_cert_path"] = cert_path if sslopt.get("server_hostname", None): hostname = sslopt["server_hostname"] @@ -327,7 +331,7 @@ def _tunnel(sock: socket.socket, host, port: int, auth) -> socket.socket: send(sock, connect_header) try: - status, resp_headers, status_message = read_headers(sock) + status, _, _ = read_headers(sock) except Exception as e: raise WebSocketProxyException(str(e)) diff --git a/contrib/python/websocket-client/py3/websocket/_logging.py b/contrib/python/websocket-client/py3/websocket/_logging.py index b88cda3744..0f673d3aff 100644 --- a/contrib/python/websocket-client/py3/websocket/_logging.py +++ b/contrib/python/websocket-client/py3/websocket/_logging.py @@ -4,7 +4,7 @@ import logging _logging.py websocket - WebSocket client library for Python -Copyright 2023 engn33r +Copyright 2024 engn33r Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/contrib/python/websocket-client/py3/websocket/_socket.py b/contrib/python/websocket-client/py3/websocket/_socket.py index f0ad27a5d4..81094ffc84 100644 --- a/contrib/python/websocket-client/py3/websocket/_socket.py +++ b/contrib/python/websocket-client/py3/websocket/_socket.py @@ -3,15 +3,18 @@ import selectors import socket from typing import Union -from ._exceptions import * -from ._ssl_compat import * -from ._utils import * +from ._exceptions import ( + WebSocketConnectionClosedException, + WebSocketTimeoutException, +) +from ._ssl_compat import SSLError, SSLWantReadError, SSLWantWriteError +from ._utils import extract_error_code, extract_err_message """ _socket.py websocket - WebSocket client library for Python -Copyright 2023 engn33r +Copyright 2024 engn33r Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/contrib/python/websocket-client/py3/websocket/_ssl_compat.py b/contrib/python/websocket-client/py3/websocket/_ssl_compat.py index 029a5c6e84..0a8a32b59b 100644 --- a/contrib/python/websocket-client/py3/websocket/_ssl_compat.py +++ b/contrib/python/websocket-client/py3/websocket/_ssl_compat.py @@ -2,7 +2,7 @@ _ssl_compat.py websocket - WebSocket client library for Python -Copyright 2023 engn33r +Copyright 2024 engn33r Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,11 +16,18 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ -__all__ = ["HAVE_SSL", "ssl", "SSLError", "SSLWantReadError", "SSLWantWriteError"] +__all__ = [ + "HAVE_SSL", + "ssl", + "SSLError", + "SSLEOFError", + "SSLWantReadError", + "SSLWantWriteError", +] try: import ssl - from ssl import SSLError, SSLWantReadError, SSLWantWriteError + from ssl import SSLError, SSLEOFError, SSLWantReadError, SSLWantWriteError HAVE_SSL = True except ImportError: @@ -28,6 +35,9 @@ except ImportError: class SSLError(Exception): pass + class SSLEOFError(Exception): + pass + class SSLWantReadError(Exception): pass diff --git a/contrib/python/websocket-client/py3/websocket/_url.py b/contrib/python/websocket-client/py3/websocket/_url.py index 7d53830e19..902131710b 100644 --- a/contrib/python/websocket-client/py3/websocket/_url.py +++ b/contrib/python/websocket-client/py3/websocket/_url.py @@ -3,12 +3,13 @@ import socket import struct from typing import Optional from urllib.parse import unquote, urlparse +from ._exceptions import WebSocketProxyException """ _url.py websocket - WebSocket client library for Python -Copyright 2023 engn33r +Copyright 2024 engn33r Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -167,6 +168,8 @@ def get_proxy_info( return None, 0, None if proxy_host: + if not proxy_port: + raise WebSocketProxyException("Cannot use port 0 when proxy_host specified") port = proxy_port auth = proxy_auth return proxy_host, port, auth diff --git a/contrib/python/websocket-client/py3/websocket/_utils.py b/contrib/python/websocket-client/py3/websocket/_utils.py index c63adfa593..65f3c0daf7 100644 --- a/contrib/python/websocket-client/py3/websocket/_utils.py +++ b/contrib/python/websocket-client/py3/websocket/_utils.py @@ -4,7 +4,7 @@ from typing import Union _url.py websocket - WebSocket client library for Python -Copyright 2023 engn33r +Copyright 2024 engn33r Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/contrib/python/websocket-client/py3/websocket/_wsdump.py b/contrib/python/websocket-client/py3/websocket/_wsdump.py index 34c3d127ec..d4d76dc509 100644 --- a/contrib/python/websocket-client/py3/websocket/_wsdump.py +++ b/contrib/python/websocket-client/py3/websocket/_wsdump.py @@ -4,7 +4,7 @@ wsdump.py websocket - WebSocket client library for Python -Copyright 2023 engn33r +Copyright 2024 engn33r Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/contrib/python/websocket-client/py3/websocket/tests/test_abnf.py b/contrib/python/websocket-client/py3/websocket/tests/test_abnf.py index df5bc6bd71..a749f13bd5 100644 --- a/contrib/python/websocket-client/py3/websocket/tests/test_abnf.py +++ b/contrib/python/websocket-client/py3/websocket/tests/test_abnf.py @@ -2,14 +2,14 @@ # import unittest -import websocket as ws -from websocket._abnf import * +from websocket._abnf import ABNF, frame_buffer +from websocket._exceptions import WebSocketProtocolException """ test_abnf.py websocket - WebSocket client library for Python -Copyright 2023 engn33r +Copyright 2024 engn33r Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ limitations under the License. class ABNFTest(unittest.TestCase): - def testInit(self): + def test_init(self): a = ABNF(0, 0, 0, 0, opcode=ABNF.OPCODE_PING) self.assertEqual(a.fin, 0) self.assertEqual(a.rsv1, 0) @@ -38,28 +38,28 @@ class ABNFTest(unittest.TestCase): self.assertEqual(a_bad.rsv1, 1) self.assertEqual(a_bad.opcode, 77) - def testValidate(self): + def test_validate(self): a_invalid_ping = ABNF(0, 0, 0, 0, opcode=ABNF.OPCODE_PING) self.assertRaises( - ws._exceptions.WebSocketProtocolException, + WebSocketProtocolException, a_invalid_ping.validate, skip_utf8_validation=False, ) a_bad_rsv_value = ABNF(0, 1, 0, 0, opcode=ABNF.OPCODE_TEXT) self.assertRaises( - ws._exceptions.WebSocketProtocolException, + WebSocketProtocolException, a_bad_rsv_value.validate, skip_utf8_validation=False, ) a_bad_opcode = ABNF(0, 0, 0, 0, opcode=77) self.assertRaises( - ws._exceptions.WebSocketProtocolException, + WebSocketProtocolException, a_bad_opcode.validate, skip_utf8_validation=False, ) a_bad_close_frame = ABNF(0, 0, 0, 0, opcode=ABNF.OPCODE_CLOSE, data=b"\x01") self.assertRaises( - ws._exceptions.WebSocketProtocolException, + WebSocketProtocolException, a_bad_close_frame.validate, skip_utf8_validation=False, ) @@ -67,7 +67,7 @@ class ABNFTest(unittest.TestCase): 0, 0, 0, 0, opcode=ABNF.OPCODE_CLOSE, data=b"\x01\x8a\xaa\xff\xdd" ) self.assertRaises( - ws._exceptions.WebSocketProtocolException, + WebSocketProtocolException, a_bad_close_frame_2.validate, skip_utf8_validation=False, ) @@ -75,12 +75,12 @@ class ABNFTest(unittest.TestCase): 0, 0, 0, 0, opcode=ABNF.OPCODE_CLOSE, data=b"\x03\xe7" ) self.assertRaises( - ws._exceptions.WebSocketProtocolException, + WebSocketProtocolException, a_bad_close_frame_3.validate, skip_utf8_validation=True, ) - def testMask(self): + def test_mask(self): abnf_none_data = ABNF( 0, 0, 0, 0, opcode=ABNF.OPCODE_PING, mask_value=1, data=None ) @@ -91,7 +91,7 @@ class ABNFTest(unittest.TestCase): ) self.assertEqual(abnf_str_data._get_masked(bytes_val), b"aaaa\x00") - def testFormat(self): + def test_format(self): abnf_bad_rsv_bits = ABNF(2, 0, 0, 0, opcode=ABNF.OPCODE_TEXT) self.assertRaises(ValueError, abnf_bad_rsv_bits.format) abnf_bad_opcode = ABNF(0, 0, 0, 0, opcode=5) @@ -110,7 +110,7 @@ class ABNFTest(unittest.TestCase): ) self.assertEqual(b"\x01\x03\x01\x8a\xcc", abnf_no_mask.format()) - def testFrameBuffer(self): + def test_frame_buffer(self): fb = frame_buffer(0, True) self.assertEqual(fb.recv, 0) self.assertEqual(fb.skip_utf8_validation, True) diff --git a/contrib/python/websocket-client/py3/websocket/tests/test_app.py b/contrib/python/websocket-client/py3/websocket/tests/test_app.py index 5ed9a22ed5..18eace5442 100644 --- a/contrib/python/websocket-client/py3/websocket/tests/test_app.py +++ b/contrib/python/websocket-client/py3/websocket/tests/test_app.py @@ -12,7 +12,7 @@ import websocket as ws test_app.py websocket - WebSocket client library for Python -Copyright 2023 engn33r +Copyright 2024 engn33r Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -53,10 +53,13 @@ class WebSocketAppTest(unittest.TestCase): WebSocketAppTest.get_mask_key_id = WebSocketAppTest.NotSetYet() WebSocketAppTest.on_error_data = WebSocketAppTest.NotSetYet() + def close(self): + pass + @unittest.skipUnless( TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" ) - def testKeepRunning(self): + def test_keep_running(self): """A WebSocketApp should keep running as long as its self.keep_running is not False (in the boolean context). """ @@ -69,7 +72,7 @@ class WebSocketAppTest(unittest.TestCase): WebSocketAppTest.keep_running_open = self.keep_running self.keep_running = False - def on_message(wsapp, message): + def on_message(_, message): print(message) self.close() @@ -87,7 +90,7 @@ class WebSocketAppTest(unittest.TestCase): # @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled") @unittest.skipUnless(False, "Test disabled for now (requires rel)") - def testRunForeverDispatcher(self): + def test_run_forever_dispatcher(self): """A WebSocketApp should keep running as long as its self.keep_running is not False (in the boolean context). """ @@ -98,7 +101,7 @@ class WebSocketAppTest(unittest.TestCase): self.recv() self.send("goodbye!") - def on_message(wsapp, message): + def on_message(_, message): print(message) self.close() @@ -115,7 +118,7 @@ class WebSocketAppTest(unittest.TestCase): @unittest.skipUnless( TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" ) - def testRunForeverTeardownCleanExit(self): + def test_run_forever_teardown_clean_exit(self): """The WebSocketApp.run_forever() method should return `False` when the application ends gracefully.""" app = ws.WebSocketApp(f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}") threading.Timer(interval=0.2, function=app.close).start() @@ -123,7 +126,7 @@ class WebSocketAppTest(unittest.TestCase): self.assertEqual(teardown, False) @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def testSockMaskKey(self): + def test_sock_mask_key(self): """A WebSocketApp should forward the received mask_key function down to the actual socket. """ @@ -140,14 +143,14 @@ class WebSocketAppTest(unittest.TestCase): self.assertEqual(id(app.get_mask_key), id(my_mask_key_func)) @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def testInvalidPingIntervalPingTimeout(self): + def test_invalid_ping_interval_ping_timeout(self): """Test exception handling if ping_interval < ping_timeout""" - def on_ping(app, msg): + def on_ping(app, _): print("Got a ping!") app.close() - def on_pong(app, msg): + def on_pong(app, _): print("Got a pong! No need to respond") app.close() @@ -163,14 +166,14 @@ class WebSocketAppTest(unittest.TestCase): ) @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def testPingInterval(self): + def test_ping_interval(self): """Test WebSocketApp proper ping functionality""" - def on_ping(app, msg): + def on_ping(app, _): print("Got a ping!") app.close() - def on_pong(app, msg): + def on_pong(app, _): print("Got a pong! No need to respond") app.close() @@ -182,7 +185,7 @@ class WebSocketAppTest(unittest.TestCase): ) @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def testOpcodeClose(self): + def test_opcode_close(self): """Test WebSocketApp close opcode""" app = ws.WebSocketApp("wss://tsock.us1.twilio.com/v3/wsconnect") @@ -197,7 +200,7 @@ class WebSocketAppTest(unittest.TestCase): # app.run_forever(ping_interval=2, ping_timeout=1, ping_payload="Ping payload") @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def testBadPingInterval(self): + def test_bad_ping_interval(self): """A WebSocketApp handling of negative ping_interval""" app = ws.WebSocketApp("wss://api-pub.bitfinex.com/ws/1") self.assertRaises( @@ -208,7 +211,7 @@ class WebSocketAppTest(unittest.TestCase): ) @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def testBadPingTimeout(self): + def test_bad_ping_timeout(self): """A WebSocketApp handling of negative ping_timeout""" app = ws.WebSocketApp("wss://api-pub.bitfinex.com/ws/1") self.assertRaises( @@ -219,7 +222,7 @@ class WebSocketAppTest(unittest.TestCase): ) @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def testCloseStatusCode(self): + def test_close_status_code(self): """Test extraction of close frame status code and close reason in WebSocketApp""" def on_close(wsapp, close_status_code, close_msg): @@ -249,7 +252,7 @@ class WebSocketAppTest(unittest.TestCase): @unittest.skipUnless( TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" ) - def testCallbackFunctionException(self): + def test_callback_function_exception(self): """Test callback function exception handling""" exc = None @@ -264,7 +267,7 @@ class WebSocketAppTest(unittest.TestCase): nonlocal exc exc = err - def on_pong(app, msg): + def on_pong(app, _): app.close() app = ws.WebSocketApp( @@ -282,7 +285,7 @@ class WebSocketAppTest(unittest.TestCase): @unittest.skipUnless( TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" ) - def testCallbackMethodException(self): + def test_callback_method_exception(self): """Test callback method exception handling""" class Callbacks: @@ -297,14 +300,14 @@ class WebSocketAppTest(unittest.TestCase): ) self.app.run_forever(ping_interval=2, ping_timeout=1) - def on_open(self, app): + def on_open(self, _): raise RuntimeError("Callback failed") def on_error(self, app, err): self.passed_app = app self.exc = err - def on_pong(self, app, msg): + def on_pong(self, app, _): app.close() callbacks = Callbacks() @@ -316,16 +319,16 @@ class WebSocketAppTest(unittest.TestCase): @unittest.skipUnless( TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" ) - def testReconnect(self): + def test_reconnect(self): """Test reconnect""" pong_count = 0 exc = None - def on_error(app, err): + def on_error(_, err): nonlocal exc exc = err - def on_pong(app, msg): + def on_pong(app, _): nonlocal pong_count pong_count += 1 if pong_count == 1: diff --git a/contrib/python/websocket-client/py3/websocket/tests/test_cookiejar.py b/contrib/python/websocket-client/py3/websocket/tests/test_cookiejar.py index 0de87517d4..67eddb627a 100644 --- a/contrib/python/websocket-client/py3/websocket/tests/test_cookiejar.py +++ b/contrib/python/websocket-client/py3/websocket/tests/test_cookiejar.py @@ -6,7 +6,7 @@ from websocket._cookiejar import SimpleCookieJar test_cookiejar.py websocket - WebSocket client library for Python -Copyright 2023 engn33r +Copyright 2024 engn33r Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ limitations under the License. class CookieJarTest(unittest.TestCase): - def testAdd(self): + def test_add(self): cookie_jar = SimpleCookieJar() cookie_jar.add("") self.assertFalse( @@ -67,7 +67,7 @@ class CookieJarTest(unittest.TestCase): self.assertEqual(cookie_jar.get("xyz"), "e=f") self.assertEqual(cookie_jar.get("something"), "") - def testSet(self): + def test_set(self): cookie_jar = SimpleCookieJar() cookie_jar.set("a=b") self.assertFalse( @@ -104,7 +104,7 @@ class CookieJarTest(unittest.TestCase): self.assertEqual(cookie_jar.get("xyz"), "e=f") self.assertEqual(cookie_jar.get("something"), "") - def testGet(self): + def test_get(self): cookie_jar = SimpleCookieJar() cookie_jar.set("a=b; c=d; domain=abc.com") self.assertEqual(cookie_jar.get("abc.com"), "a=b; c=d") diff --git a/contrib/python/websocket-client/py3/websocket/tests/test_http.py b/contrib/python/websocket-client/py3/websocket/tests/test_http.py index 9df36e43ab..b8b363acc4 100644 --- a/contrib/python/websocket-client/py3/websocket/tests/test_http.py +++ b/contrib/python/websocket-client/py3/websocket/tests/test_http.py @@ -7,7 +7,7 @@ import ssl import unittest import websocket -import websocket as ws +from websocket._exceptions import WebSocketProxyException, WebSocketException from websocket._http import ( _get_addrinfo_list, _start_proxied_socket, @@ -15,13 +15,14 @@ from websocket._http import ( connect, proxy_info, read_headers, + HAVE_PYTHON_SOCKS, ) """ test_http.py websocket - WebSocket client library for Python -Copyright 2023 engn33r +Copyright 2024 engn33r Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -94,20 +95,18 @@ class OptsList: class HttpTest(unittest.TestCase): - def testReadHeader(self): - status, header, status_message = read_headers( - HeaderSockMock("data/header01.txt") - ) + def test_read_header(self): + status, header, _ = read_headers(HeaderSockMock("data/header01.txt")) self.assertEqual(status, 101) self.assertEqual(header["connection"], "Upgrade") # header02.txt is intentionally malformed self.assertRaises( - ws.WebSocketException, read_headers, HeaderSockMock("data/header02.txt") + WebSocketException, read_headers, HeaderSockMock("data/header02.txt") ) - def testTunnel(self): + def test_tunnel(self): self.assertRaises( - ws.WebSocketProxyException, + WebSocketProxyException, _tunnel, HeaderSockMock("data/header01.txt"), "example.com", @@ -115,7 +114,7 @@ class HttpTest(unittest.TestCase): ("username", "password"), ) self.assertRaises( - ws.WebSocketProxyException, + WebSocketProxyException, _tunnel, HeaderSockMock("data/header02.txt"), "example.com", @@ -124,9 +123,9 @@ class HttpTest(unittest.TestCase): ) @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def testConnect(self): + def test_connect(self): # Not currently testing an actual proxy connection, so just check whether proxy errors are raised. This requires internet for a DNS lookup - if ws._http.HAVE_PYTHON_SOCKS: + if HAVE_PYTHON_SOCKS: # Need this check, otherwise case where python_socks is not installed triggers # websocket._exceptions.WebSocketException: Python Socks is needed for SOCKS proxying but is not available self.assertRaises( @@ -245,7 +244,7 @@ class HttpTest(unittest.TestCase): @unittest.skipUnless( TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" ) - def testProxyConnect(self): + def test_proxy_connect(self): ws = websocket.WebSocket() ws.connect( f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}", @@ -290,7 +289,7 @@ class HttpTest(unittest.TestCase): # TODO: Test SOCKS4 and SOCK5 proxies with unit tests @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def testSSLopt(self): + def test_sslopt(self): ssloptions = { "check_hostname": False, "server_hostname": "ServerName", @@ -316,7 +315,7 @@ class HttpTest(unittest.TestCase): ws_ssl2.connect("wss://api.bitfinex.com/ws/2") ws_ssl2.close - def testProxyInfo(self): + def test_proxy_info(self): self.assertEqual( proxy_info( http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="http" diff --git a/contrib/python/websocket-client/py3/websocket/tests/test_url.py b/contrib/python/websocket-client/py3/websocket/tests/test_url.py index 792399ebf5..110fdfad70 100644 --- a/contrib/python/websocket-client/py3/websocket/tests/test_url.py +++ b/contrib/python/websocket-client/py3/websocket/tests/test_url.py @@ -9,12 +9,13 @@ from websocket._url import ( get_proxy_info, parse_url, ) +from websocket._exceptions import WebSocketProxyException """ test_url.py websocket - WebSocket client library for Python -Copyright 2023 engn33r +Copyright 2024 engn33r Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -36,7 +37,7 @@ class UrlTest(unittest.TestCase): self.assertTrue(_is_address_in_network("127.1.0.1", "127.0.0.0/8")) self.assertFalse(_is_address_in_network("127.1.0.1", "127.0.0.0/24")) - def testParseUrl(self): + def test_parse_url(self): p = parse_url("ws://www.example.com/r") self.assertEqual(p[0], "www.example.com") self.assertEqual(p[1], 80) @@ -130,9 +131,13 @@ class IsNoProxyHostTest(unittest.TestCase): elif "no_proxy" in os.environ: del os.environ["no_proxy"] - def testMatchAll(self): + def test_match_all(self): self.assertTrue(_is_no_proxy_host("any.websocket.org", ["*"])) self.assertTrue(_is_no_proxy_host("192.168.0.1", ["*"])) + self.assertFalse(_is_no_proxy_host("192.168.0.1", ["192.168.1.1"])) + self.assertFalse( + _is_no_proxy_host("any.websocket.org", ["other.websocket.org"]) + ) self.assertTrue( _is_no_proxy_host("any.websocket.org", ["other.websocket.org", "*"]) ) @@ -142,7 +147,7 @@ class IsNoProxyHostTest(unittest.TestCase): os.environ["no_proxy"] = "other.websocket.org, *" self.assertTrue(_is_no_proxy_host("any.websocket.org", None)) - def testIpAddress(self): + def test_ip_address(self): self.assertTrue(_is_no_proxy_host("127.0.0.1", ["127.0.0.1"])) self.assertFalse(_is_no_proxy_host("127.0.0.2", ["127.0.0.1"])) self.assertTrue( @@ -158,7 +163,7 @@ class IsNoProxyHostTest(unittest.TestCase): self.assertTrue(_is_no_proxy_host("127.0.0.1", None)) self.assertFalse(_is_no_proxy_host("127.0.0.2", None)) - def testIpAddressInRange(self): + def test_ip_address_in_range(self): self.assertTrue(_is_no_proxy_host("127.0.0.1", ["127.0.0.0/8"])) self.assertTrue(_is_no_proxy_host("127.0.0.2", ["127.0.0.0/8"])) self.assertFalse(_is_no_proxy_host("127.1.0.1", ["127.0.0.0/24"])) @@ -168,7 +173,7 @@ class IsNoProxyHostTest(unittest.TestCase): os.environ["no_proxy"] = "127.0.0.0/24" self.assertFalse(_is_no_proxy_host("127.1.0.1", None)) - def testHostnameMatch(self): + def test_hostname_match(self): self.assertTrue(_is_no_proxy_host("my.websocket.org", ["my.websocket.org"])) self.assertTrue( _is_no_proxy_host( @@ -182,7 +187,7 @@ class IsNoProxyHostTest(unittest.TestCase): os.environ["no_proxy"] = "other.websocket.org, my.websocket.org" self.assertTrue(_is_no_proxy_host("my.websocket.org", None)) - def testHostnameMatchDomain(self): + def test_hostname_match_domain(self): self.assertTrue(_is_no_proxy_host("any.websocket.org", [".websocket.org"])) self.assertTrue(_is_no_proxy_host("my.other.websocket.org", [".websocket.org"])) self.assertTrue( @@ -227,10 +232,13 @@ class ProxyInfoTest(unittest.TestCase): elif "no_proxy" in os.environ: del os.environ["no_proxy"] - def testProxyFromArgs(self): - self.assertEqual( - get_proxy_info("echo.websocket.events", False, proxy_host="localhost"), - ("localhost", 0, None), + def test_proxy_from_args(self): + self.assertRaises( + WebSocketProxyException, + get_proxy_info, + "echo.websocket.events", + False, + proxy_host="localhost", ) self.assertEqual( get_proxy_info( @@ -239,10 +247,6 @@ class ProxyInfoTest(unittest.TestCase): ("localhost", 3128, None), ) self.assertEqual( - get_proxy_info("echo.websocket.events", True, proxy_host="localhost"), - ("localhost", 0, None), - ) - self.assertEqual( get_proxy_info( "echo.websocket.events", True, proxy_host="localhost", proxy_port=3128 ), @@ -254,9 +258,10 @@ class ProxyInfoTest(unittest.TestCase): "echo.websocket.events", False, proxy_host="localhost", + proxy_port=9001, proxy_auth=("a", "b"), ), - ("localhost", 0, ("a", "b")), + ("localhost", 9001, ("a", "b")), ) self.assertEqual( get_proxy_info( @@ -273,9 +278,10 @@ class ProxyInfoTest(unittest.TestCase): "echo.websocket.events", True, proxy_host="localhost", + proxy_port=8765, proxy_auth=("a", "b"), ), - ("localhost", 0, ("a", "b")), + ("localhost", 8765, ("a", "b")), ) self.assertEqual( get_proxy_info( @@ -311,7 +317,18 @@ class ProxyInfoTest(unittest.TestCase): (None, 0, None), ) - def testProxyFromEnv(self): + self.assertEqual( + get_proxy_info( + "echo.websocket.events", + True, + proxy_host="localhost", + proxy_port=3128, + no_proxy=[".websocket.events"], + ), + (None, 0, None), + ) + + def test_proxy_from_env(self): os.environ["http_proxy"] = "http://localhost/" self.assertEqual( get_proxy_info("echo.websocket.events", False), ("localhost", None, None) diff --git a/contrib/python/websocket-client/py3/websocket/tests/test_websocket.py b/contrib/python/websocket-client/py3/websocket/tests/test_websocket.py index c3d636426b..b665548cda 100644 --- a/contrib/python/websocket-client/py3/websocket/tests/test_websocket.py +++ b/contrib/python/websocket-client/py3/websocket/tests/test_websocket.py @@ -7,6 +7,7 @@ import unittest from base64 import decodebytes as base64decode import websocket as ws +from websocket._exceptions import WebSocketBadStatusException, WebSocketAddressException from websocket._handshake import _create_sec_websocket_key from websocket._handshake import _validate as _validate_header from websocket._http import read_headers @@ -16,7 +17,7 @@ from websocket._utils import validate_utf8 test_websocket.py websocket - WebSocket client library for Python -Copyright 2023 engn33r +Copyright 2024 engn33r Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -33,7 +34,6 @@ limitations under the License. try: import ssl - from ssl import SSLError except ImportError: # dummy class of SSLError for ssl none-support environment. class SSLError(Exception): @@ -96,24 +96,24 @@ class WebSocketTest(unittest.TestCase): def tearDown(self): pass - def testDefaultTimeout(self): + def test_default_timeout(self): self.assertEqual(ws.getdefaulttimeout(), None) ws.setdefaulttimeout(10) self.assertEqual(ws.getdefaulttimeout(), 10) ws.setdefaulttimeout(None) - def testWSKey(self): + def test_ws_key(self): key = _create_sec_websocket_key() self.assertTrue(key != 24) self.assertTrue("¥n" not in key) - def testNonce(self): + def test_nonce(self): """WebSocket key should be a random 16-byte nonce.""" key = _create_sec_websocket_key() nonce = base64decode(key.encode("utf-8")) self.assertEqual(16, len(nonce)) - def testWsUtils(self): + def test_ws_utils(self): key = "c6b8hTg4EeGb2gQMztV1/g==" required_header = { "upgrade": "websocket", @@ -158,16 +158,12 @@ class WebSocketTest(unittest.TestCase): # This case will print out a logging error using the error() function, but that is expected self.assertEqual(_validate_header(header, key, ["Sub1", "suB2"]), (False, None)) - def testReadHeader(self): - status, header, status_message = read_headers( - HeaderSockMock("data/header01.txt") - ) + def test_read_header(self): + status, header, _ = read_headers(HeaderSockMock("data/header01.txt")) self.assertEqual(status, 101) self.assertEqual(header["connection"], "Upgrade") - status, header, status_message = read_headers( - HeaderSockMock("data/header03.txt") - ) + status, header, _ = read_headers(HeaderSockMock("data/header03.txt")) self.assertEqual(status, 101) self.assertEqual(header["connection"], "Upgrade, Keep-Alive") @@ -176,7 +172,7 @@ class WebSocketTest(unittest.TestCase): ws.WebSocketException, read_headers, HeaderSockMock("data/header02.txt") ) - def testSend(self): + def test_send(self): # TODO: add longer frame data sock = ws.WebSocket() sock.set_mask_key(create_mask_key) @@ -195,7 +191,7 @@ class WebSocketTest(unittest.TestCase): self.assertEqual(sock.send_binary(b"1111111111101"), 19) - def testRecv(self): + def test_recv(self): # TODO: add longer frame data sock = ws.WebSocket() s = sock.sock = SockMock() @@ -211,7 +207,7 @@ class WebSocketTest(unittest.TestCase): self.assertEqual(data, "Hello") @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def testIter(self): + def test_iter(self): count = 2 s = ws.create_connection("wss://api.bitfinex.com/ws/2") s.send('{"event": "subscribe", "channel": "ticker"}') @@ -221,11 +217,11 @@ class WebSocketTest(unittest.TestCase): break @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def testNext(self): + def test_next(self): sock = ws.create_connection("wss://api.bitfinex.com/ws/2") self.assertEqual(str, type(next(sock))) - def testInternalRecvStrict(self): + def test_internal_recv_strict(self): sock = ws.WebSocket() s = sock.sock = SockMock() s.add_packet(b"foo") @@ -242,7 +238,7 @@ class WebSocketTest(unittest.TestCase): with self.assertRaises(ws.WebSocketConnectionClosedException): sock.frame_buffer.recv_strict(1) - def testRecvTimeout(self): + def test_recv_timeout(self): sock = ws.WebSocket() s = sock.sock = SockMock() s.add_packet(b"\x81") @@ -259,7 +255,7 @@ class WebSocketTest(unittest.TestCase): with self.assertRaises(ws.WebSocketConnectionClosedException): sock.recv() - def testRecvWithSimpleFragmentation(self): + def test_recv_with_simple_fragmentation(self): sock = ws.WebSocket() s = sock.sock = SockMock() # OPCODE=TEXT, FIN=0, MSG="Brevity is " @@ -271,7 +267,7 @@ class WebSocketTest(unittest.TestCase): with self.assertRaises(ws.WebSocketConnectionClosedException): sock.recv() - def testRecvWithFireEventOfFragmentation(self): + def test_recv_with_fire_event_of_fragmentation(self): sock = ws.WebSocket(fire_cont_frame=True) s = sock.sock = SockMock() # OPCODE=TEXT, FIN=0, MSG="Brevity is " @@ -297,7 +293,7 @@ class WebSocketTest(unittest.TestCase): with self.assertRaises(ws.WebSocketConnectionClosedException): sock.recv() - def testClose(self): + def test_close(self): sock = ws.WebSocket() sock.connected = True sock.close @@ -309,14 +305,14 @@ class WebSocketTest(unittest.TestCase): sock.recv() self.assertEqual(sock.connected, False) - def testRecvContFragmentation(self): + def test_recv_cont_fragmentation(self): sock = ws.WebSocket() s = sock.sock = SockMock() # OPCODE=CONT, FIN=1, MSG="the soul of wit" s.add_packet(b"\x80\x8fabcd\x15\n\x06D\x12\r\x16\x08A\r\x05D\x16\x0b\x17") self.assertRaises(ws.WebSocketException, sock.recv) - def testRecvWithProlongedFragmentation(self): + def test_recv_with_prolonged_fragmentation(self): sock = ws.WebSocket() s = sock.sock = SockMock() # OPCODE=TEXT, FIN=0, MSG="Once more unto the breach, " @@ -332,7 +328,7 @@ class WebSocketTest(unittest.TestCase): with self.assertRaises(ws.WebSocketConnectionClosedException): sock.recv() - def testRecvWithFragmentationAndControlFrame(self): + def test_recv_with_fragmentation_and_control_frame(self): sock = ws.WebSocket() sock.set_mask_key(create_mask_key) s = sock.sock = SockMock() @@ -353,7 +349,7 @@ class WebSocketTest(unittest.TestCase): @unittest.skipUnless( TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" ) - def testWebSocket(self): + def test_websocket(self): s = ws.create_connection(f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}") self.assertNotEqual(s, None) s.send("Hello, World") @@ -370,7 +366,7 @@ class WebSocketTest(unittest.TestCase): @unittest.skipUnless( TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" ) - def testPingPong(self): + def test_ping_pong(self): s = ws.create_connection(f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}") self.assertNotEqual(s, None) s.ping("Hello") @@ -378,17 +374,13 @@ class WebSocketTest(unittest.TestCase): s.close() @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def testSupportRedirect(self): + def test_support_redirect(self): s = ws.WebSocket() - self.assertRaises( - ws._exceptions.WebSocketBadStatusException, s.connect, "ws://google.com/" - ) + self.assertRaises(WebSocketBadStatusException, s.connect, "ws://google.com/") # Need to find a URL that has a redirect code leading to a websocket @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def testSecureWebSocket(self): - import ssl - + def test_secure_websocket(self): s = ws.create_connection("wss://api.bitfinex.com/ws/2") self.assertNotEqual(s, None) self.assertTrue(isinstance(s.sock, ssl.SSLSocket)) @@ -402,7 +394,7 @@ class WebSocketTest(unittest.TestCase): @unittest.skipUnless( TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" ) - def testWebSocketWithCustomHeader(self): + def test_websocket_with_custom_header(self): s = ws.create_connection( f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}", headers={"User-Agent": "PythonWebsocketClient"}, @@ -418,7 +410,7 @@ class WebSocketTest(unittest.TestCase): @unittest.skipUnless( TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" ) - def testAfterClose(self): + def test_after_close(self): s = ws.create_connection(f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}") self.assertNotEqual(s, None) s.close() @@ -430,7 +422,7 @@ class SockOptTest(unittest.TestCase): @unittest.skipUnless( TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" ) - def testSockOpt(self): + def test_sockopt(self): sockopt = ((socket.IPPROTO_TCP, socket.TCP_NODELAY, 1),) s = ws.create_connection( f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}", sockopt=sockopt @@ -442,7 +434,7 @@ class SockOptTest(unittest.TestCase): class UtilsTest(unittest.TestCase): - def testUtf8Validator(self): + def test_utf8_validator(self): state = validate_utf8(b"\xf0\x90\x80\x80") self.assertEqual(state, True) state = validate_utf8( @@ -455,7 +447,7 @@ class UtilsTest(unittest.TestCase): class HandshakeTest(unittest.TestCase): @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def test_http_SSL(self): + def test_http_ssl(self): websock1 = ws.WebSocket( sslopt={"cert_chain": ssl.get_default_verify_paths().capath}, enable_multithread=False, @@ -467,7 +459,7 @@ class HandshakeTest(unittest.TestCase): ) @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def testManualHeaders(self): + def test_manual_headers(self): websock3 = ws.WebSocket( sslopt={ "ca_certs": ssl.get_default_verify_paths().cafile, @@ -475,7 +467,7 @@ class HandshakeTest(unittest.TestCase): } ) self.assertRaises( - ws._exceptions.WebSocketBadStatusException, + WebSocketBadStatusException, websock3.connect, "wss://api.bitfinex.com/ws/2", cookie="chocolate", @@ -491,16 +483,14 @@ class HandshakeTest(unittest.TestCase): }, ) - def testIPv6(self): + def test_ipv6(self): websock2 = ws.WebSocket() self.assertRaises(ValueError, websock2.connect, "2001:4860:4860::8888") - def testBadURLs(self): + def test_bad_urls(self): websock3 = ws.WebSocket() self.assertRaises(ValueError, websock3.connect, "ws//example.com") - self.assertRaises( - ws.WebSocketAddressException, websock3.connect, "ws://example" - ) + self.assertRaises(WebSocketAddressException, websock3.connect, "ws://example") self.assertRaises(ValueError, websock3.connect, "example.com") diff --git a/contrib/python/websocket-client/py3/ya.make b/contrib/python/websocket-client/py3/ya.make index a7aa5bb650..1197cbe117 100644 --- a/contrib/python/websocket-client/py3/ya.make +++ b/contrib/python/websocket-client/py3/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(1.7.0) +VERSION(1.8.0) LICENSE(Apache-2.0) |