improved type hinting and formatting in miner factory

This commit is contained in:
UpstreamData
2022-05-26 12:14:28 -06:00
parent 9078df680e
commit 942f2a1c8d

View File

@@ -1,12 +1,17 @@
from typing import TypeVar, Iterator, Tuple, List
from miners import BaseMiner
from miners.antminer import * from miners.antminer import *
from miners.whatsminer import * from miners.whatsminer import *
from miners.avalonminer import * from miners.avalonminer import *
from miners._backends.cgminer import CGMiner from miners._backends.cgminer import CGMiner # noqa - Ignore _module import
from miners._backends.bmminer import BMMiner from miners._backends.bmminer import BMMiner # noqa - Ignore _module import
from miners._backends.bosminer import BOSMiner from miners._backends.bosminer import BOSMiner # noqa - Ignore _module import
from miners._backends.btminer import BTMiner from miners._backends.btminer import BTMiner # noqa - Ignore _module import
from miners._backends.bosminer_old import BOSMinerOld from miners._backends.bosminer_old import BOSMinerOld # noqa - Ignore _module import
from miners.unknown import UnknownMiner from miners.unknown import UnknownMiner
@@ -22,6 +27,8 @@ from settings import (
NETWORK_PING_TIMEOUT as PING_TIMEOUT, NETWORK_PING_TIMEOUT as PING_TIMEOUT,
) )
AnyMiner = TypeVar("AnyMiner", bound=BaseMiner)
MINER_CLASSES = { MINER_CLASSES = {
"Antminer S9": { "Antminer S9": {
"Default": BOSMinerS9, "Default": BOSMinerS9,
@@ -170,7 +177,9 @@ class MinerFactory(metaclass=Singleton):
def __init__(self): def __init__(self):
self.miners = {} self.miners = {}
async def get_miner_generator(self, ips: list): async def get_miner_generator(
self, ips: List[ipaddress.ip_address or str]
) -> Iterator[AnyMiner]:
""" """
Get Miner objects from ip addresses using an async generator. Get Miner objects from ip addresses using an async generator.
@@ -192,7 +201,7 @@ class MinerFactory(metaclass=Singleton):
for miner in scanned: for miner in scanned:
yield await miner yield await miner
async def get_miner(self, ip: ipaddress.ip_address or str): async def get_miner(self, ip: ipaddress.ip_address or str) -> AnyMiner:
"""Decide a miner type using the IP address of the miner.""" """Decide a miner type using the IP address of the miner."""
if isinstance(ip, str): if isinstance(ip, str):
ip = ipaddress.ip_address(ip) ip = ipaddress.ip_address(ip)
@@ -218,7 +227,7 @@ class MinerFactory(metaclass=Singleton):
if new_model and not model: if new_model and not model:
model = new_model model = new_model
# if we find the API and model, dont need to loop anymore # if we find the API and model, don't need to loop anymore
if api and model: if api and model:
break break
except asyncio.TimeoutError: except asyncio.TimeoutError:
@@ -265,15 +274,16 @@ class MinerFactory(metaclass=Singleton):
# return the miner # return the miner
return miner return miner
def clear_cached_miners(self): def clear_cached_miners(self) -> None:
"""Clear the miner factory cache.""" """Clear the miner factory cache."""
# empty out self.miners # empty out self.miners
self.miners = {} self.miners = {}
async def _get_miner_type(self, ip: ipaddress.ip_address or str) -> tuple: async def _get_miner_type(
self, ip: ipaddress.ip_address or str
) -> Tuple[str or None, str or None]:
model = None model = None
api = None api = None
data = None
devdetails = None devdetails = None
version = None version = None
@@ -288,10 +298,9 @@ class MinerFactory(metaclass=Singleton):
devdetails = data["devdetails"][0] devdetails = data["devdetails"][0]
version = data["version"][0] version = data["version"][0]
except APIError as e: except APIError:
data = None data = None
if not data: if not data:
try: try:
devdetails = await self._send_api_command(str(ip), "devdetails") devdetails = await self._send_api_command(str(ip), "devdetails")
@@ -350,7 +359,9 @@ class MinerFactory(metaclass=Singleton):
api = "BOSMiner+" api = "BOSMiner+"
# if all that fails, check the Description to see if it is a whatsminer # if all that fails, check the Description to see if it is a whatsminer
if version.get("Description") and "whatsminer" in version.get("Description"): if version.get("Description") and "whatsminer" in version.get(
"Description"
):
api = "BTMiner" api = "BTMiner"
if version and not model: if version and not model:
@@ -364,7 +375,6 @@ class MinerFactory(metaclass=Singleton):
elif "am2-s17" in version["STATUS"][0]["Description"]: elif "am2-s17" in version["STATUS"][0]["Description"]:
model = "Antminer S17" model = "Antminer S17"
if model: if model:
if "V" in model: if "V" in model:
model = model.split("V")[0] model = model.split("V")[0]
@@ -373,7 +383,8 @@ class MinerFactory(metaclass=Singleton):
return model, api return model, api
async def _validate_command(self, data: dict) -> tuple: @staticmethod
async def _validate_command(data: dict) -> Tuple[bool, str or None]:
"""Check if the returned command output is correctly formatted.""" """Check if the returned command output is correctly formatted."""
# check if the data returned is correct or an error # check if the data returned is correct or an error
if not data: if not data:
@@ -399,7 +410,8 @@ class MinerFactory(metaclass=Singleton):
return False, data["STATUS"][0]["Msg"] return False, data["STATUS"][0]["Msg"]
return True, None return True, None
async def _send_api_command(self, ip: ipaddress.ip_address or str, command: str): @staticmethod
async def _send_api_command(ip: ipaddress.ip_address or str, command: str) -> dict:
try: try:
# get reader and writer streams # get reader and writer streams
reader, writer = await asyncio.open_connection(str(ip), 4028) reader, writer = await asyncio.open_connection(str(ip), 4028)
@@ -439,12 +451,12 @@ class MinerFactory(metaclass=Singleton):
str_data = str_data.replace(",}", "}") str_data = str_data.replace(",}", "}")
# fix an error with a btminer return having a newline that breaks json.loads() # fix an error with a btminer return having a newline that breaks json.loads()
str_data = str_data.replace("\n", "") str_data = str_data.replace("\n", "")
# fix an error with a bmminer return not having a specific comma that breaks json.loads() # fix an error with a bmminer return missing a specific comma that breaks json.loads()
str_data = str_data.replace("}{", "},{") str_data = str_data.replace("}{", "},{")
# parse the json # parse the json
data = json.loads(str_data) data = json.loads(str_data)
# handle bad json # handle bad json
except json.decoder.JSONDecodeError as e: except json.decoder.JSONDecodeError:
# raise APIError(f"Decode Error: {data}") # raise APIError(f"Decode Error: {data}")
data = None data = None