refactor: re-arrange some imports.

This commit is contained in:
UpstreamData
2024-01-25 16:25:25 -07:00
parent a9135e21d4
commit 66be443dc3
5 changed files with 58 additions and 122 deletions

View File

@@ -15,44 +15,10 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from pyasic import settings from pyasic import settings
from pyasic.config import MinerConfig from pyasic.config import MinerConfig
from pyasic.data import ( from pyasic.data import MinerData
BraiinsOSError,
InnosiliconError,
MinerData,
WhatsminerError,
X19Error,
)
from pyasic.errors import APIError, APIWarning from pyasic.errors import APIError, APIWarning
from pyasic.miners import AnyMiner, DataOptions, get_miner from pyasic.miners import *
from pyasic.miners.factory import MinerFactory, miner_factory
from pyasic.miners.listener import MinerListener
from pyasic.network import MinerNetwork from pyasic.network import MinerNetwork
from pyasic.rpc.bmminer import BMMinerRPCAPI from pyasic.rpc import *
from pyasic.rpc.bosminer import BOSMinerRPCAPI from pyasic.ssh import *
from pyasic.rpc.btminer import BTMinerRPCAPI from pyasic.web import *
from pyasic.rpc.cgminer import CGMinerRPCAPI
from pyasic.rpc.unknown import UnknownRPCAPI
__all__ = [
"BMMinerRPCAPI",
"BOSMinerRPCAPI",
"BTMinerRPCAPI",
"CGMinerRPCAPI",
"UnknownRPCAPI",
"MinerConfig",
"MinerData",
"BraiinsOSError",
"InnosiliconError",
"WhatsminerError",
"X19Error",
"APIError",
"APIWarning",
"get_miner",
"AnyMiner",
"DataOptions",
"MinerFactory",
"miner_factory",
"MinerListener",
"MinerNetwork",
"settings",
]

View File

@@ -14,14 +14,7 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import ipaddress from .base import AnyMiner
from typing import Union from .data import DataOptions
from .factory import get_miner, miner_factory
from pyasic.miners.base import AnyMiner, BaseMiner from .listener import MinerListener
from pyasic.miners.data import DataOptions
from pyasic.miners.factory import miner_factory
# abstracted version of get miner that is easier to access
async def get_miner(ip: Union[ipaddress.ip_address, str]) -> AnyMiner:
return await miner_factory.get_miner(ip)

View File

@@ -13,19 +13,20 @@
# See the License for the specific language governing permissions and - # See the License for the specific language governing permissions and -
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from __future__ import annotations
import asyncio import asyncio
import enum import enum
import ipaddress import ipaddress
import json import json
import re import re
from typing import AsyncGenerator, Callable, List, Optional, Tuple, Union from typing import Any, AsyncGenerator, Callable
import anyio import anyio
import httpx import httpx
from pyasic import settings from pyasic import settings
from pyasic.logger import logger from pyasic.logger import logger
from pyasic.miners import AnyMiner
from pyasic.miners.antminer import * from pyasic.miners.antminer import *
from pyasic.miners.auradine import * from pyasic.miners.auradine import *
from pyasic.miners.avalonminer import * from pyasic.miners.avalonminer import *
@@ -43,6 +44,7 @@ from pyasic.miners.backends import (
ePIC, ePIC,
) )
from pyasic.miners.backends.unknown import UnknownMiner from pyasic.miners.backends.unknown import UnknownMiner
from pyasic.miners.base import AnyMiner
from pyasic.miners.goldshell import * from pyasic.miners.goldshell import *
from pyasic.miners.innosilicon import * from pyasic.miners.innosilicon import *
from pyasic.miners.whatsminer import * from pyasic.miners.whatsminer import *
@@ -408,7 +410,7 @@ MINER_CLASSES = {
} }
async def concurrent_get_first_result(tasks: list, verification_func: Callable): async def concurrent_get_first_result(tasks: list, verification_func: Callable) -> Any:
res = None res = None
for fut in asyncio.as_completed(tasks): for fut in asyncio.as_completed(tasks):
res = await fut res = await fut
@@ -425,8 +427,8 @@ async def concurrent_get_first_result(tasks: list, verification_func: Callable):
class MinerFactory: class MinerFactory:
async def get_multiple_miners( async def get_multiple_miners(
self, ips: List[str], limit: int = 200 self, ips: list[str], limit: int = 200
) -> List[AnyMiner]: ) -> list[AnyMiner]:
results = [] results = []
async for miner in self.get_miner_generator(ips, limit): async for miner in self.get_miner_generator(ips, limit):
@@ -434,7 +436,9 @@ class MinerFactory:
return results return results
async def get_miner_generator(self, ips: list, limit: int = 200) -> AsyncGenerator: async def get_miner_generator(
self, ips: list, limit: int = 200
) -> AsyncGenerator[AnyMiner]:
tasks = [] tasks = []
semaphore = asyncio.Semaphore(limit) semaphore = asyncio.Semaphore(limit)
@@ -447,7 +451,7 @@ class MinerFactory:
if result is not None: if result is not None:
yield result yield result
async def get_miner(self, ip: str): async def get_miner(self, ip: str | ipaddress.ip_address) -> AnyMiner | None:
ip = str(ip) ip = str(ip)
miner_type = None miner_type = None
@@ -499,7 +503,7 @@ class MinerFactory:
return miner return miner
async def _get_miner_type(self, ip: str): async def _get_miner_type(self, ip: str) -> MinerTypes | None:
tasks = [ tasks = [
asyncio.create_task(self._get_miner_web(ip)), asyncio.create_task(self._get_miner_web(ip)),
asyncio.create_task(self._get_miner_socket(ip)), asyncio.create_task(self._get_miner_socket(ip)),
@@ -507,7 +511,7 @@ class MinerFactory:
return await concurrent_get_first_result(tasks, lambda x: x is not None) return await concurrent_get_first_result(tasks, lambda x: x is not None)
async def _get_miner_web(self, ip: str): async def _get_miner_web(self, ip: str) -> MinerTypes | None:
tasks = [] tasks = []
try: try:
urls = [f"http://{ip}/", f"https://{ip}/"] urls = [f"http://{ip}/", f"https://{ip}/"]
@@ -536,7 +540,7 @@ class MinerFactory:
@staticmethod @staticmethod
async def _web_ping( async def _web_ping(
session: httpx.AsyncClient, url: str session: httpx.AsyncClient, url: str
) -> Tuple[Optional[str], Optional[httpx.Response]]: ) -> tuple[str | None, httpx.Response | None]:
try: try:
resp = await session.get(url, follow_redirects=True) resp = await session.get(url, follow_redirects=True)
return resp.text, resp return resp.text, resp
@@ -550,7 +554,7 @@ class MinerFactory:
return None, None return None, None
@staticmethod @staticmethod
def _parse_web_type(web_text: str, web_resp: httpx.Response) -> MinerTypes: def _parse_web_type(web_text: str, web_resp: httpx.Response) -> MinerTypes | None:
if web_resp.status_code == 401 and 'realm="antMiner' in web_resp.headers.get( if web_resp.status_code == 401 and 'realm="antMiner' in web_resp.headers.get(
"www-authenticate", "" "www-authenticate", ""
): ):
@@ -578,7 +582,7 @@ class MinerFactory:
if "Miner UI" in web_text: if "Miner UI" in web_text:
return MinerTypes.AURADINE return MinerTypes.AURADINE
async def _get_miner_socket(self, ip: str): async def _get_miner_socket(self, ip: str) -> MinerTypes | None:
tasks = [] tasks = []
try: try:
commands = ["version", "devdetails"] commands = ["version", "devdetails"]
@@ -602,7 +606,7 @@ class MinerFactory:
pass pass
@staticmethod @staticmethod
async def _socket_ping(ip: str, cmd: str) -> Optional[str]: async def _socket_ping(ip: str, cmd: str) -> str | None:
data = b"" data = b""
try: try:
reader, writer = await asyncio.wait_for( reader, writer = await asyncio.wait_for(
@@ -648,7 +652,7 @@ class MinerFactory:
return data.decode("utf-8") return data.decode("utf-8")
@staticmethod @staticmethod
def _parse_socket_type(data: str) -> MinerTypes: def _parse_socket_type(data: str) -> MinerTypes | None:
upper_data = data.upper() upper_data = data.upper()
if "BOSMINER" in upper_data or "BOSER" in upper_data: if "BOSMINER" in upper_data or "BOSER" in upper_data:
return MinerTypes.BRAIINS_OS return MinerTypes.BRAIINS_OS
@@ -671,14 +675,14 @@ class MinerFactory:
async def send_web_command( async def send_web_command(
self, self,
ip: Union[ipaddress.ip_address, str], ip: str,
location: str, location: str,
auth: Optional[httpx.DigestAuth] = None, auth: httpx.DigestAuth = None,
) -> Optional[dict]: ) -> dict | None:
async with httpx.AsyncClient(transport=settings.transport()) as session: async with httpx.AsyncClient(transport=settings.transport()) as session:
try: try:
data = await session.get( data = await session.get(
f"http://{str(ip)}{location}", f"http://{ip}{location}",
auth=auth, auth=auth,
timeout=settings.get("factory_get_timeout", 3), timeout=settings.get("factory_get_timeout", 3),
) )
@@ -697,12 +701,10 @@ class MinerFactory:
else: else:
return json_data return json_data
async def send_api_command( async def send_api_command(self, ip: str, command: str) -> dict | None:
self, ip: Union[ipaddress.ip_address, str], command: str
) -> Optional[dict]:
data = b"" data = b""
try: try:
reader, writer = await asyncio.open_connection(str(ip), 4028) reader, writer = await asyncio.open_connection(ip, 4028)
except (ConnectionError, OSError): except (ConnectionError, OSError):
return return
cmd = {"command": command} cmd = {"command": command}
@@ -740,7 +742,7 @@ class MinerFactory:
return data return data
@staticmethod @staticmethod
async def _fix_api_data(data: bytes): async def _fix_api_data(data: bytes) -> str:
if data.endswith(b"\x00"): if data.endswith(b"\x00"):
str_data = data.decode("utf-8")[:-1] str_data = data.decode("utf-8")[:-1]
else: else:
@@ -777,9 +779,9 @@ class MinerFactory:
@staticmethod @staticmethod
def _select_miner_from_classes( def _select_miner_from_classes(
ip: ipaddress.ip_address, ip: ipaddress.ip_address,
miner_model: Union[str, None], miner_model: str | None,
miner_type: Union[MinerTypes, None], miner_type: MinerTypes | None,
) -> AnyMiner: ) -> AnyMiner | None:
try: try:
return MINER_CLASSES[miner_type][str(miner_model).upper()](ip) return MINER_CLASSES[miner_type][str(miner_model).upper()](ip)
except LookupError: except LookupError:
@@ -787,7 +789,7 @@ class MinerFactory:
return MINER_CLASSES[miner_type][None](ip) return MINER_CLASSES[miner_type][None](ip)
return UnknownMiner(str(ip)) return UnknownMiner(str(ip))
async def get_miner_model_antminer(self, ip: str): async def get_miner_model_antminer(self, ip: str) -> str | None:
tasks = [ tasks = [
asyncio.create_task(self._get_model_antminer_web(ip)), asyncio.create_task(self._get_model_antminer_web(ip)),
asyncio.create_task(self._get_model_antminer_sock(ip)), asyncio.create_task(self._get_model_antminer_sock(ip)),
@@ -795,7 +797,7 @@ class MinerFactory:
return await concurrent_get_first_result(tasks, lambda x: x is not None) return await concurrent_get_first_result(tasks, lambda x: x is not None)
async def _get_model_antminer_web(self, ip: str): async def _get_model_antminer_web(self, ip: str) -> str | None:
# last resort, this is slow # last resort, this is slow
auth = httpx.DigestAuth("root", "root") auth = httpx.DigestAuth("root", "root")
web_json_data = await self.send_web_command( web_json_data = await self.send_web_command(
@@ -809,7 +811,7 @@ class MinerFactory:
except (TypeError, LookupError): except (TypeError, LookupError):
pass pass
async def _get_model_antminer_sock(self, ip: str): async def _get_model_antminer_sock(self, ip: str) -> str | None:
sock_json_data = await self.send_api_command(ip, "version") sock_json_data = await self.send_api_command(ip, "version")
try: try:
miner_model = sock_json_data["VERSION"][0]["Type"] miner_model = sock_json_data["VERSION"][0]["Type"]
@@ -834,7 +836,7 @@ class MinerFactory:
except (TypeError, LookupError): except (TypeError, LookupError):
pass pass
async def get_miner_model_goldshell(self, ip: str): async def get_miner_model_goldshell(self, ip: str) -> str | None:
json_data = await self.send_web_command(ip, "/mcb/status") json_data = await self.send_web_command(ip, "/mcb/status")
try: try:
@@ -844,7 +846,7 @@ class MinerFactory:
except (TypeError, LookupError): except (TypeError, LookupError):
pass pass
async def get_miner_model_whatsminer(self, ip: str): async def get_miner_model_whatsminer(self, ip: str) -> str | None:
sock_json_data = await self.send_api_command(ip, "devdetails") sock_json_data = await self.send_api_command(ip, "devdetails")
try: try:
miner_model = sock_json_data["DEVDETAILS"][0]["Model"].replace("_", "") miner_model = sock_json_data["DEVDETAILS"][0]["Model"].replace("_", "")
@@ -854,7 +856,7 @@ class MinerFactory:
except (TypeError, LookupError): except (TypeError, LookupError):
pass pass
async def get_miner_model_avalonminer(self, ip: str) -> Optional[str]: async def get_miner_model_avalonminer(self, ip: str) -> str | None:
sock_json_data = await self.send_api_command(ip, "version") sock_json_data = await self.send_api_command(ip, "version")
try: try:
miner_model = sock_json_data["VERSION"][0]["PROD"] miner_model = sock_json_data["VERSION"][0]["PROD"]
@@ -865,7 +867,7 @@ class MinerFactory:
except (TypeError, LookupError): except (TypeError, LookupError):
pass pass
async def get_miner_model_innosilicon(self, ip: str) -> Optional[str]: async def get_miner_model_innosilicon(self, ip: str) -> str | None:
try: try:
async with httpx.AsyncClient(transport=settings.transport()) as session: async with httpx.AsyncClient(transport=settings.transport()) as session:
auth_req = await session.post( auth_req = await session.post(
@@ -885,7 +887,7 @@ class MinerFactory:
except (httpx.HTTPError, LookupError): except (httpx.HTTPError, LookupError):
pass pass
async def get_miner_model_braiins_os(self, ip: str) -> Optional[str]: async def get_miner_model_braiins_os(self, ip: str) -> str | None:
sock_json_data = await self.send_api_command(ip, "devdetails") sock_json_data = await self.send_api_command(ip, "devdetails")
try: try:
miner_model = sock_json_data["DEVDETAILS"][0]["Model"].replace( miner_model = sock_json_data["DEVDETAILS"][0]["Model"].replace(
@@ -909,7 +911,7 @@ class MinerFactory:
except (httpx.HTTPError, LookupError): except (httpx.HTTPError, LookupError):
pass pass
async def get_miner_model_vnish(self, ip: str) -> Optional[str]: async def get_miner_model_vnish(self, ip: str) -> str | None:
sock_json_data = await self.send_api_command(ip, "stats") sock_json_data = await self.send_api_command(ip, "stats")
try: try:
miner_model = sock_json_data["STATS"][0]["Type"] miner_model = sock_json_data["STATS"][0]["Type"]
@@ -927,7 +929,7 @@ class MinerFactory:
except (TypeError, LookupError): except (TypeError, LookupError):
pass pass
async def get_miner_model_epic(self, ip: str) -> Optional[str]: async def get_miner_model_epic(self, ip: str) -> str | None:
sock_json_data = await self.send_web_command(ip, ":4028/capabilities") sock_json_data = await self.send_web_command(ip, ":4028/capabilities")
try: try:
miner_model = sock_json_data["Model"] miner_model = sock_json_data["Model"]
@@ -935,7 +937,7 @@ class MinerFactory:
except (TypeError, LookupError): except (TypeError, LookupError):
pass pass
async def get_miner_model_hiveon(self, ip: str) -> Optional[str]: async def get_miner_model_hiveon(self, ip: str) -> str | None:
sock_json_data = await self.send_api_command(ip, "version") sock_json_data = await self.send_api_command(ip, "version")
try: try:
miner_type = sock_json_data["VERSION"][0]["Type"] miner_type = sock_json_data["VERSION"][0]["Type"]
@@ -944,7 +946,7 @@ class MinerFactory:
except (TypeError, LookupError): except (TypeError, LookupError):
pass pass
async def get_miner_model_luxos(self, ip: str): async def get_miner_model_luxos(self, ip: str) -> str | None:
sock_json_data = await self.send_api_command(ip, "version") sock_json_data = await self.send_api_command(ip, "version")
try: try:
miner_model = sock_json_data["VERSION"][0]["Type"] miner_model = sock_json_data["VERSION"][0]["Type"]
@@ -956,7 +958,7 @@ class MinerFactory:
except (TypeError, LookupError): except (TypeError, LookupError):
pass pass
async def get_miner_model_auradine(self, ip: str): async def get_miner_model_auradine(self, ip: str) -> str | None:
sock_json_data = await self.send_api_command(ip, "devdetails") sock_json_data = await self.send_api_command(ip, "devdetails")
try: try:
return sock_json_data["DEVDETAILS"][0]["Model"] return sock_json_data["DEVDETAILS"][0]["Model"]
@@ -965,3 +967,7 @@ class MinerFactory:
miner_factory = MinerFactory() miner_factory = MinerFactory()
# abstracted version of get miner that is easier to access
async def get_miner(ip: ipaddress.ip_address | str) -> AnyMiner:
return await miner_factory.get_miner(ip)

View File

@@ -16,10 +16,8 @@
import asyncio import asyncio
from pyasic.misc import Singleton
class MinerListenerProtocol(asyncio.Protocol):
class _MinerListener:
def __init__(self): def __init__(self):
self.responses = {} self.responses = {}
self.transport = None self.transport = None
@@ -44,7 +42,7 @@ class _MinerListener:
pass pass
class MinerListener(metaclass=Singleton): class MinerListener:
def __init__(self): def __init__(self):
self.found_miners = [] self.found_miners = []
self.new_miner = None self.new_miner = None
@@ -56,10 +54,10 @@ class MinerListener(metaclass=Singleton):
loop = asyncio.get_running_loop() loop = asyncio.get_running_loop()
transport_14235, _ = await loop.create_datagram_endpoint( transport_14235, _ = await loop.create_datagram_endpoint(
_MinerListener, local_addr=("0.0.0.0", 14235) MinerListenerProtocol, local_addr=("0.0.0.0", 14235)
) )
transport_8888, _ = await loop.create_datagram_endpoint( transport_8888, _ = await loop.create_datagram_endpoint(
_MinerListener, local_addr=("0.0.0.0", 8888) MinerListenerProtocol, local_addr=("0.0.0.0", 8888)
) )
while True: while True:
@@ -75,21 +73,3 @@ class MinerListener(metaclass=Singleton):
async def cancel(self): async def cancel(self):
self.stop = True self.stop = True
async def main():
await asyncio.gather(run(), cancel())
async def run():
async for miner in MinerListener().listen():
print(miner)
async def cancel():
await asyncio.sleep(60)
await MinerListener().cancel()
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -18,15 +18,6 @@ from copy import deepcopy
from pyasic.errors import APIError from pyasic.errors import APIError
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
def api_min_version(version: str): def api_min_version(version: str):
def decorator(func): def decorator(func):
# handle the inner function that the decorator is wrapping # handle the inner function that the decorator is wrapping