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.config import MinerConfig
from pyasic.data import (
BraiinsOSError,
InnosiliconError,
MinerData,
WhatsminerError,
X19Error,
)
from pyasic.data import MinerData
from pyasic.errors import APIError, APIWarning
from pyasic.miners import AnyMiner, DataOptions, get_miner
from pyasic.miners.factory import MinerFactory, miner_factory
from pyasic.miners.listener import MinerListener
from pyasic.miners import *
from pyasic.network import MinerNetwork
from pyasic.rpc.bmminer import BMMinerRPCAPI
from pyasic.rpc.bosminer import BOSMinerRPCAPI
from pyasic.rpc.btminer import BTMinerRPCAPI
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",
]
from pyasic.rpc import *
from pyasic.ssh import *
from pyasic.web import *

View File

@@ -14,14 +14,7 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
import ipaddress
from typing import Union
from pyasic.miners.base import AnyMiner, BaseMiner
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)
from .base import AnyMiner
from .data import DataOptions
from .factory import get_miner, miner_factory
from .listener import MinerListener

View File

@@ -13,19 +13,20 @@
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from __future__ import annotations
import asyncio
import enum
import ipaddress
import json
import re
from typing import AsyncGenerator, Callable, List, Optional, Tuple, Union
from typing import Any, AsyncGenerator, Callable
import anyio
import httpx
from pyasic import settings
from pyasic.logger import logger
from pyasic.miners import AnyMiner
from pyasic.miners.antminer import *
from pyasic.miners.auradine import *
from pyasic.miners.avalonminer import *
@@ -43,6 +44,7 @@ from pyasic.miners.backends import (
ePIC,
)
from pyasic.miners.backends.unknown import UnknownMiner
from pyasic.miners.base import AnyMiner
from pyasic.miners.goldshell import *
from pyasic.miners.innosilicon 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
for fut in asyncio.as_completed(tasks):
res = await fut
@@ -425,8 +427,8 @@ async def concurrent_get_first_result(tasks: list, verification_func: Callable):
class MinerFactory:
async def get_multiple_miners(
self, ips: List[str], limit: int = 200
) -> List[AnyMiner]:
self, ips: list[str], limit: int = 200
) -> list[AnyMiner]:
results = []
async for miner in self.get_miner_generator(ips, limit):
@@ -434,7 +436,9 @@ class MinerFactory:
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 = []
semaphore = asyncio.Semaphore(limit)
@@ -447,7 +451,7 @@ class MinerFactory:
if result is not None:
yield result
async def get_miner(self, ip: str):
async def get_miner(self, ip: str | ipaddress.ip_address) -> AnyMiner | None:
ip = str(ip)
miner_type = None
@@ -499,7 +503,7 @@ class MinerFactory:
return miner
async def _get_miner_type(self, ip: str):
async def _get_miner_type(self, ip: str) -> MinerTypes | None:
tasks = [
asyncio.create_task(self._get_miner_web(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)
async def _get_miner_web(self, ip: str):
async def _get_miner_web(self, ip: str) -> MinerTypes | None:
tasks = []
try:
urls = [f"http://{ip}/", f"https://{ip}/"]
@@ -536,7 +540,7 @@ class MinerFactory:
@staticmethod
async def _web_ping(
session: httpx.AsyncClient, url: str
) -> Tuple[Optional[str], Optional[httpx.Response]]:
) -> tuple[str | None, httpx.Response | None]:
try:
resp = await session.get(url, follow_redirects=True)
return resp.text, resp
@@ -550,7 +554,7 @@ class MinerFactory:
return None, None
@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(
"www-authenticate", ""
):
@@ -578,7 +582,7 @@ class MinerFactory:
if "Miner UI" in web_text:
return MinerTypes.AURADINE
async def _get_miner_socket(self, ip: str):
async def _get_miner_socket(self, ip: str) -> MinerTypes | None:
tasks = []
try:
commands = ["version", "devdetails"]
@@ -602,7 +606,7 @@ class MinerFactory:
pass
@staticmethod
async def _socket_ping(ip: str, cmd: str) -> Optional[str]:
async def _socket_ping(ip: str, cmd: str) -> str | None:
data = b""
try:
reader, writer = await asyncio.wait_for(
@@ -648,7 +652,7 @@ class MinerFactory:
return data.decode("utf-8")
@staticmethod
def _parse_socket_type(data: str) -> MinerTypes:
def _parse_socket_type(data: str) -> MinerTypes | None:
upper_data = data.upper()
if "BOSMINER" in upper_data or "BOSER" in upper_data:
return MinerTypes.BRAIINS_OS
@@ -671,14 +675,14 @@ class MinerFactory:
async def send_web_command(
self,
ip: Union[ipaddress.ip_address, str],
ip: str,
location: str,
auth: Optional[httpx.DigestAuth] = None,
) -> Optional[dict]:
auth: httpx.DigestAuth = None,
) -> dict | None:
async with httpx.AsyncClient(transport=settings.transport()) as session:
try:
data = await session.get(
f"http://{str(ip)}{location}",
f"http://{ip}{location}",
auth=auth,
timeout=settings.get("factory_get_timeout", 3),
)
@@ -697,12 +701,10 @@ class MinerFactory:
else:
return json_data
async def send_api_command(
self, ip: Union[ipaddress.ip_address, str], command: str
) -> Optional[dict]:
async def send_api_command(self, ip: str, command: str) -> dict | None:
data = b""
try:
reader, writer = await asyncio.open_connection(str(ip), 4028)
reader, writer = await asyncio.open_connection(ip, 4028)
except (ConnectionError, OSError):
return
cmd = {"command": command}
@@ -740,7 +742,7 @@ class MinerFactory:
return data
@staticmethod
async def _fix_api_data(data: bytes):
async def _fix_api_data(data: bytes) -> str:
if data.endswith(b"\x00"):
str_data = data.decode("utf-8")[:-1]
else:
@@ -777,9 +779,9 @@ class MinerFactory:
@staticmethod
def _select_miner_from_classes(
ip: ipaddress.ip_address,
miner_model: Union[str, None],
miner_type: Union[MinerTypes, None],
) -> AnyMiner:
miner_model: str | None,
miner_type: MinerTypes | None,
) -> AnyMiner | None:
try:
return MINER_CLASSES[miner_type][str(miner_model).upper()](ip)
except LookupError:
@@ -787,7 +789,7 @@ class MinerFactory:
return MINER_CLASSES[miner_type][None](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 = [
asyncio.create_task(self._get_model_antminer_web(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)
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
auth = httpx.DigestAuth("root", "root")
web_json_data = await self.send_web_command(
@@ -809,7 +811,7 @@ class MinerFactory:
except (TypeError, LookupError):
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")
try:
miner_model = sock_json_data["VERSION"][0]["Type"]
@@ -834,7 +836,7 @@ class MinerFactory:
except (TypeError, LookupError):
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")
try:
@@ -844,7 +846,7 @@ class MinerFactory:
except (TypeError, LookupError):
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")
try:
miner_model = sock_json_data["DEVDETAILS"][0]["Model"].replace("_", "")
@@ -854,7 +856,7 @@ class MinerFactory:
except (TypeError, LookupError):
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")
try:
miner_model = sock_json_data["VERSION"][0]["PROD"]
@@ -865,7 +867,7 @@ class MinerFactory:
except (TypeError, LookupError):
pass
async def get_miner_model_innosilicon(self, ip: str) -> Optional[str]:
async def get_miner_model_innosilicon(self, ip: str) -> str | None:
try:
async with httpx.AsyncClient(transport=settings.transport()) as session:
auth_req = await session.post(
@@ -885,7 +887,7 @@ class MinerFactory:
except (httpx.HTTPError, LookupError):
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")
try:
miner_model = sock_json_data["DEVDETAILS"][0]["Model"].replace(
@@ -909,7 +911,7 @@ class MinerFactory:
except (httpx.HTTPError, LookupError):
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")
try:
miner_model = sock_json_data["STATS"][0]["Type"]
@@ -927,7 +929,7 @@ class MinerFactory:
except (TypeError, LookupError):
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")
try:
miner_model = sock_json_data["Model"]
@@ -935,7 +937,7 @@ class MinerFactory:
except (TypeError, LookupError):
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")
try:
miner_type = sock_json_data["VERSION"][0]["Type"]
@@ -944,7 +946,7 @@ class MinerFactory:
except (TypeError, LookupError):
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")
try:
miner_model = sock_json_data["VERSION"][0]["Type"]
@@ -956,7 +958,7 @@ class MinerFactory:
except (TypeError, LookupError):
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")
try:
return sock_json_data["DEVDETAILS"][0]["Model"]
@@ -965,3 +967,7 @@ class 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
from pyasic.misc import Singleton
class _MinerListener:
class MinerListenerProtocol(asyncio.Protocol):
def __init__(self):
self.responses = {}
self.transport = None
@@ -44,7 +42,7 @@ class _MinerListener:
pass
class MinerListener(metaclass=Singleton):
class MinerListener:
def __init__(self):
self.found_miners = []
self.new_miner = None
@@ -56,10 +54,10 @@ class MinerListener(metaclass=Singleton):
loop = asyncio.get_running_loop()
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(
_MinerListener, local_addr=("0.0.0.0", 8888)
MinerListenerProtocol, local_addr=("0.0.0.0", 8888)
)
while True:
@@ -75,21 +73,3 @@ class MinerListener(metaclass=Singleton):
async def cancel(self):
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
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 decorator(func):
# handle the inner function that the decorator is wrapping