diff --git a/.pylintrc b/.pylintrc index 3d6d4a5..c616eb1 100644 --- a/.pylintrc +++ b/.pylintrc @@ -3,28 +3,20 @@ py-version=3.8.0 [MESSAGES CONTROL] disable= - multiple-imports, missing-docstring, - logging-not-lazy, - logging-fstring-interpolation, + multiple-imports, too-few-public-methods, - too-many-public-methods, - too-many-instance-attributes, - dangerous-default-value, - inconsistent-return-statements, - -[SIMILARITIES] -min-similarity-lines=6 + too-many-instance-attributes [VARIABLES] -allowed-redefined-builtins=type,dir,id,all,format,len +allowed-redefined-builtins=all,dir,format,id,len,type [FORMAT] max-line-length=120 expected-line-ending-format=LF [BASIC] -good-names=t,f,id,ip,on,pl,tf,to,A,B,C,D,E,F +good-names=f,t,id,ip,on,pl,tf,to,A,B,C,D,E,F [TYPECHECK] generated-members=websockets diff --git a/bfxapi/client.py b/bfxapi/client.py index 21fdafe..d45b925 100644 --- a/bfxapi/client.py +++ b/bfxapi/client.py @@ -1,5 +1,5 @@ from typing import \ - TYPE_CHECKING, List, Literal, Optional + TYPE_CHECKING, List, Optional from bfxapi._utils.logging import ColorLogger @@ -23,8 +23,7 @@ class Client: wss_host: str = WSS_HOST, filters: Optional[List[str]] = None, timeout: Optional[int] = 60 * 15, - log_filename: Optional[str] = None, - log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = "INFO" + log_filename: Optional[str] = None ) -> None: credentials: Optional["_Credentials"] = None @@ -40,7 +39,7 @@ class Client: self.rest = BfxRestInterface(rest_host, api_key, api_secret) - logger = ColorLogger("bfxapi", level=log_level) + logger = ColorLogger("bfxapi", level="INFO") if log_filename: logger.register(filename=log_filename) diff --git a/bfxapi/rest/endpoints/rest_auth_endpoints.py b/bfxapi/rest/endpoints/rest_auth_endpoints.py index 661e17a..3dc0885 100644 --- a/bfxapi/rest/endpoints/rest_auth_endpoints.py +++ b/bfxapi/rest/endpoints/rest_auth_endpoints.py @@ -22,6 +22,7 @@ from ...types import serializers from ...types.serializers import _Notification +#pylint: disable-next=too-many-public-methods class RestAuthEndpoints(Middleware): def get_user_info(self) -> UserInfo: return serializers.UserInfo \ diff --git a/bfxapi/rest/endpoints/rest_merchant_endpoints.py b/bfxapi/rest/endpoints/rest_merchant_endpoints.py index d2ca4c9..d201a10 100644 --- a/bfxapi/rest/endpoints/rest_merchant_endpoints.py +++ b/bfxapi/rest/endpoints/rest_merchant_endpoints.py @@ -148,6 +148,7 @@ class RestMerchantEndpoints(Middleware): def get_merchant_settings(self, key: MerchantSettingsKey) -> Any: return self._post("auth/r/ext/pay/settings/get", body={ "key": key }) + #pylint: disable-next=dangerous-default-value def list_merchant_settings(self, keys: List[MerchantSettingsKey] = []) -> Dict[MerchantSettingsKey, Any]: return self._post("auth/r/ext/pay/settings/list", body={ "keys": keys }) diff --git a/bfxapi/rest/endpoints/rest_public_endpoints.py b/bfxapi/rest/endpoints/rest_public_endpoints.py index e1c20ff..4401057 100644 --- a/bfxapi/rest/endpoints/rest_public_endpoints.py +++ b/bfxapi/rest/endpoints/rest_public_endpoints.py @@ -17,6 +17,7 @@ from ...types import \ from ...types import serializers +#pylint: disable-next=too-many-public-methods class RestPublicEndpoints(Middleware): def conf(self, config: Config) -> Any: return self._get(f"conf/{config}")[0] diff --git a/bfxapi/types/labeler.py b/bfxapi/types/labeler.py index 52ac497..8ad5896 100644 --- a/bfxapi/types/labeler.py +++ b/bfxapi/types/labeler.py @@ -34,8 +34,8 @@ class _Type: class _Serializer(Generic[T]): def __init__(self, name: str, klass: Type[_Type], labels: List[str], - *, flat: bool = False, ignore: List[str] = [ "_PLACEHOLDER" ]): - self.name, self.klass, self.__labels, self.__flat, self.__ignore = name, klass, labels, flat, ignore + *, flat: bool = False): + self.name, self.klass, self.__labels, self.__flat = name, klass, labels, flat def _serialize(self, *args: Any) -> Iterable[Tuple[str, Any]]: if self.__flat: @@ -46,14 +46,14 @@ class _Serializer(Generic[T]): "arguments should contain the same amount of elements.") for index, label in enumerate(self.__labels): - if label not in self.__ignore: + if label != "_PLACEHOLDER": yield label, args[index] def parse(self, *values: Any) -> T: return cast(T, self.klass(**dict(self._serialize(*values)))) def get_labels(self) -> List[str]: - return [ label for label in self.__labels if label not in self.__ignore ] + return [ label for label in self.__labels if label != "_PLACEHOLDER" ] @classmethod def __flatten(cls, array: List[Any]) -> List[Any]: @@ -68,8 +68,8 @@ class _Serializer(Generic[T]): class _RecursiveSerializer(_Serializer, Generic[T]): def __init__(self, name: str, klass: Type[_Type], labels: List[str], *, serializers: Dict[str, _Serializer[Any]], - flat: bool = False, ignore: List[str] = [ "_PLACEHOLDER" ]): - super().__init__(name, klass, labels, flat=flat, ignore=ignore) + flat: bool = False): + super().__init__(name, klass, labels, flat=flat) self.serializers = serializers @@ -83,14 +83,14 @@ class _RecursiveSerializer(_Serializer, Generic[T]): return cast(T, self.klass(**serialization)) def generate_labeler_serializer(name: str, klass: Type[T], labels: List[str], - *, flat: bool = False, ignore: List[str] = [ "_PLACEHOLDER" ] + *, flat: bool = False ) -> _Serializer[T]: return _Serializer[T](name, klass, labels, \ - flat=flat, ignore=ignore) + flat=flat) def generate_recursive_serializer(name: str, klass: Type[T], labels: List[str], *, serializers: Dict[str, _Serializer[Any]], - flat: bool = False, ignore: List[str] = [ "_PLACEHOLDER" ] + flat: bool = False ) -> _RecursiveSerializer[T]: return _RecursiveSerializer[T](name, klass, labels, \ - serializers=serializers, flat=flat, ignore=ignore) + serializers=serializers, flat=flat) diff --git a/bfxapi/types/notification.py b/bfxapi/types/notification.py index ae02259..add3175 100644 --- a/bfxapi/types/notification.py +++ b/bfxapi/types/notification.py @@ -18,7 +18,7 @@ class _Notification(_Serializer, Generic[T]): __LABELS = [ "mts", "type", "message_id", "_PLACEHOLDER", "data", "code", "status", "text" ] def __init__(self, serializer: Optional[_Serializer] = None, is_iterable: bool = False): - super().__init__("Notification", Notification, _Notification.__LABELS, ignore = [ "_PLACEHOLDER" ]) + super().__init__("Notification", Notification, _Notification.__LABELS) self.serializer, self.is_iterable = serializer, is_iterable diff --git a/bfxapi/websocket/_client/bfx_websocket_client.py b/bfxapi/websocket/_client/bfx_websocket_client.py index ad33985..a8831c0 100644 --- a/bfxapi/websocket/_client/bfx_websocket_client.py +++ b/bfxapi/websocket/_client/bfx_websocket_client.py @@ -3,9 +3,9 @@ from typing import \ Optional, Any from logging import Logger + from datetime import datetime from socket import gaierror - from asyncio import Task import \ @@ -68,6 +68,7 @@ class _Delay: def reset(self) -> None: self.__backoff_delay = _Delay.__BACKOFF_MIN +#pylint: disable-next=too-many-instance-attributes class BfxWebSocketClient(Connection): def __init__(self, host: str, @@ -101,6 +102,7 @@ class BfxWebSocketClient(Connection): stack_trace = traceback.format_exception( \ type(exception), exception, exception.__traceback__) + #pylint: disable-next=logging-not-lazy self.__logger.critical(header + "\n" + \ str().join(stack_trace)[:-1]) @@ -158,12 +160,11 @@ class BfxWebSocketClient(Connection): if isinstance(error, ConnectionClosedError) and error.code in (1006, 1012): if error.code == 1006: - self.__logger.error("Connection lost: no close frame " \ - "received or sent (1006). Trying to reconnect...") + self.__logger.error("Connection lost: trying to reconnect...") if error.code == 1012: - self.__logger.info("WSS server is about to restart, clients need " \ - "to reconnect (server sent 20051). Reconnection attempt in progress...") + self.__logger.warning("WSS server is restarting: all " \ + "clients need to reconnect (server sent 20051).") if self.__timeout: asyncio.get_event_loop().call_later( @@ -177,10 +178,14 @@ class BfxWebSocketClient(Connection): _delay.reset() elif ((isinstance(error, InvalidStatusCode) and error.status_code == 408) or \ isinstance(error, gaierror)) and self.__reconnection: - self.__logger.warning( - f"_Reconnection attempt was unsuccessful (no.{self.__reconnection['attempts']}). " \ - f"Next reconnection attempt in {int(_delay.peek())}.0 seconds. (at the moment " \ - f"the client has been offline for {datetime.now() - self.__reconnection['timestamp']})") + #pylint: disable-next=logging-fstring-interpolation + self.__logger.warning("Reconnection attempt unsuccessful (no." \ + f"{self.__reconnection['attempts']}): next attempt in " \ + f"~{int(_delay.peek())}.0s.") + + #pylint: disable-next=logging-fstring-interpolation + self.__logger.info(f"The client has been offline for " \ + f"{datetime.now() - self.__reconnection['timestamp']}.") self.__reconnection["attempts"] += 1 else: @@ -196,9 +201,10 @@ class BfxWebSocketClient(Connection): async def __connect(self) -> None: async with websockets.client.connect(self._host) as websocket: if self.__reconnection: - self.__logger.info(f"_Reconnection attempt successful (no.{self.__reconnection['attempts']}): The " \ - f"client has been offline for a total of {datetime.now() - self.__reconnection['timestamp']} " \ - f"(connection lost on: {self.__reconnection['timestamp']:%d-%m-%Y at %H:%M:%S}).") + #pylint: disable-next=logging-fstring-interpolation + self.__logger.warning("Reconnection attempt successful (no." \ + f"{self.__reconnection['attempts']}): recovering " \ + "connection state...") self.__reconnection = None diff --git a/bfxapi/websocket/_handlers/auth_events_handler.py b/bfxapi/websocket/_handlers/auth_events_handler.py index d93364d..b6a0c91 100644 --- a/bfxapi/websocket/_handlers/auth_events_handler.py +++ b/bfxapi/websocket/_handlers/auth_events_handler.py @@ -37,7 +37,7 @@ class AuthEventsHandler: def handle(self, abbrevation: str, stream: Any) -> None: if abbrevation == "n": - return self.__notification(stream) + self.__notification(stream) for abbrevations, serializer in AuthEventsHandler.__SERIALIZERS.items(): if abbrevation in abbrevations: @@ -45,12 +45,11 @@ class AuthEventsHandler: if all(isinstance(sub_stream, list) for sub_stream in stream): data = [ serializer.parse(*sub_stream) for sub_stream in stream ] - else: data = serializer.parse(*stream) + else: + data = serializer.parse(*stream) self.__event_emitter.emit(event, data) - break - def __notification(self, stream: Any) -> None: event: str = "notification" diff --git a/bfxapi/websocket/_handlers/public_channels_handler.py b/bfxapi/websocket/_handlers/public_channels_handler.py index 9e46989..33b7af3 100644 --- a/bfxapi/websocket/_handlers/public_channels_handler.py +++ b/bfxapi/websocket/_handlers/public_channels_handler.py @@ -35,6 +35,7 @@ class PublicChannelsHandler: elif subscription["channel"] == "status": self.__status_channel_handler(cast(Status, subscription), stream) + #pylint: disable-next=inconsistent-return-statements def __ticker_channel_handler(self, subscription: Ticker, stream: List[Any]): if subscription["symbol"].startswith("t"): return self.__event_emitter.emit("t_ticker_update", subscription, \ @@ -44,6 +45,7 @@ class PublicChannelsHandler: return self.__event_emitter.emit("f_ticker_update", subscription, \ serializers.FundingCurrencyTicker.parse(*stream[0])) + #pylint: disable-next=inconsistent-return-statements def __trades_channel_handler(self, subscription: Trades, stream: List[Any]): if (event := stream[0]) and event in [ "te", "tu", "fte", "ftu" ]: events = { "te": "t_trade_execution", "tu": "t_trade_execution_update", \ @@ -67,6 +69,7 @@ class PublicChannelsHandler: [ serializers.FundingCurrencyTrade.parse(*sub_stream) \ for sub_stream in stream[0] ]) + #pylint: disable-next=inconsistent-return-statements def __book_channel_handler(self, subscription: Book, stream: List[Any]): if subscription["symbol"].startswith("t"): if all(isinstance(sub_stream, list) for sub_stream in stream[0]): @@ -86,6 +89,7 @@ class PublicChannelsHandler: return self.__event_emitter.emit("f_book_update", subscription, \ serializers.FundingCurrencyBook.parse(*stream[0])) + #pylint: disable-next=inconsistent-return-statements def __raw_book_channel_handler(self, subscription: Book, stream: List[Any]): if subscription["symbol"].startswith("t"): if all(isinstance(sub_stream, list) for sub_stream in stream[0]): @@ -105,6 +109,7 @@ class PublicChannelsHandler: return self.__event_emitter.emit("f_raw_book_update", subscription, \ serializers.FundingCurrencyRawBook.parse(*stream[0])) + #pylint: disable-next=inconsistent-return-statements def __candles_channel_handler(self, subscription: Candles, stream: List[Any]): if all(isinstance(sub_stream, list) for sub_stream in stream[0]): return self.__event_emitter.emit("candles_snapshot", subscription, \ @@ -114,6 +119,7 @@ class PublicChannelsHandler: return self.__event_emitter.emit("candles_update", subscription, \ serializers.Candle.parse(*stream[0])) + #pylint: disable-next=inconsistent-return-statements def __status_channel_handler(self, subscription: Status, stream: List[Any]): if subscription["key"].startswith("deriv:"): return self.__event_emitter.emit("derivatives_status_update", subscription, \ @@ -123,6 +129,7 @@ class PublicChannelsHandler: return self.__event_emitter.emit("liquidation_feed_update", subscription, \ serializers.Liquidation.parse(*stream[0][0])) + #pylint: disable-next=inconsistent-return-statements def __checksum_handler(self, subscription: Book, value: int): return self.__event_emitter.emit( \ "checksum", subscription, value & 0xFFFFFFFF)