Skip to content

Commit 7678f7d

Browse files
authored
Merge branch 'staging' into fix/2188-configure_uvicorn_event_loop
2 parents fb8c0ea + 3db617d commit 7678f7d

File tree

11 files changed

+162
-92
lines changed

11 files changed

+162
-92
lines changed

bittensor/core/settings.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,18 @@
77
from munch import munchify
88

99

10+
READ_ONLY = os.getenv("READ_ONLY") == "1"
11+
1012
HOME_DIR = Path.home()
1113
USER_BITTENSOR_DIR = HOME_DIR / ".bittensor"
1214
WALLETS_DIR = USER_BITTENSOR_DIR / "wallets"
1315
MINERS_DIR = USER_BITTENSOR_DIR / "miners"
1416

1517

16-
# Create dirs if they don't exist
17-
WALLETS_DIR.mkdir(parents=True, exist_ok=True)
18-
MINERS_DIR.mkdir(parents=True, exist_ok=True)
18+
if not READ_ONLY:
19+
# Create dirs if they don't exist
20+
WALLETS_DIR.mkdir(parents=True, exist_ok=True)
21+
MINERS_DIR.mkdir(parents=True, exist_ok=True)
1922

2023
# Bittensor networks name
2124
NETWORKS = ["finney", "test", "archive", "local", "subvortex", "rao", "latent-lite"]

bittensor/utils/btlogging/loggingmachine.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from statemachine import State, StateMachine
1818

19+
from bittensor.core.settings import READ_ONLY
1920
from bittensor.core.config import Config
2021
from bittensor.utils.btlogging.console import BittensorConsole
2122
from .defines import (
@@ -584,9 +585,12 @@ def add_args(cls, parser: argparse.ArgumentParser, prefix: str = None):
584585
default_logging_info = os.getenv("BT_LOGGING_INFO") or False
585586
default_logging_trace = os.getenv("BT_LOGGING_TRACE") or False
586587
default_logging_record_log = os.getenv("BT_LOGGING_RECORD_LOG") or False
587-
default_logging_logging_dir = os.getenv(
588-
"BT_LOGGING_LOGGING_DIR"
589-
) or os.path.join("~", ".bittensor", "miners")
588+
default_logging_logging_dir = (
589+
None
590+
if READ_ONLY
591+
else os.getenv("BT_LOGGING_LOGGING_DIR")
592+
or os.path.join("~", ".bittensor", "miners")
593+
)
590594
parser.add_argument(
591595
"--" + prefix_str + "logging.debug",
592596
action="store_true",

tests/unit_tests/conftest.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import pytest
22
from aioresponses import aioresponses
3+
from async_substrate_interface.sync_substrate import SubstrateInterface
4+
5+
import bittensor.core.subtensor
36

47

58
@pytest.fixture
@@ -11,3 +14,30 @@ def force_legacy_torch_compatible_api(monkeypatch):
1114
def mock_aio_response():
1215
with aioresponses() as m:
1316
yield m
17+
18+
19+
@pytest.fixture
20+
def mock_substrate_interface(mocker):
21+
mocked = mocker.MagicMock(
22+
autospec=SubstrateInterface,
23+
)
24+
25+
mocker.patch("bittensor.core.subtensor.SubstrateInterface", return_value=mocked)
26+
27+
return mocked
28+
29+
30+
@pytest.fixture
31+
def subtensor(mock_substrate_interface):
32+
return bittensor.core.subtensor.Subtensor()
33+
34+
35+
@pytest.fixture
36+
def mock_get_external_ip(mocker):
37+
mocked = mocker.Mock(
38+
return_value="192.168.1.1",
39+
)
40+
41+
mocker.patch("bittensor.utils.networking.get_external_ip", mocked)
42+
43+
return mocked

tests/unit_tests/extrinsics/asyncex/test_commit_reveal.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
@pytest.fixture
1212
def subtensor(mocker):
1313
fake_substrate = mocker.AsyncMock()
14-
fake_substrate.websocket.sock.getsockopt.return_value = 0
14+
fake_substrate.websocket.socket.getsockopt.return_value = 0
1515
mocker.patch.object(
1616
subtensor_module, "AsyncSubstrateInterface", return_value=fake_substrate
1717
)

tests/unit_tests/extrinsics/test_commit_reveal.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,6 @@
66
from bittensor.core import subtensor as subtensor_module
77
from bittensor.core.chain_data import SubnetHyperparameters
88
from bittensor.core.extrinsics import commit_reveal
9-
from bittensor.core.subtensor import Subtensor
10-
11-
12-
@pytest.fixture
13-
def subtensor(mocker):
14-
fake_substrate = mocker.MagicMock()
15-
fake_substrate.websocket.sock.getsockopt.return_value = 0
16-
mocker.patch.object(
17-
subtensor_module, "SubstrateInterface", return_value=fake_substrate
18-
)
19-
yield Subtensor()
209

2110

2211
@pytest.fixture

tests/unit_tests/extrinsics/test_commit_weights.py

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,10 @@
1-
import pytest
21
from bittensor_wallet import Wallet
32

4-
from bittensor.core import subtensor as subtensor_module
5-
from bittensor.core.settings import version_as_int
6-
from bittensor.core.subtensor import Subtensor
73
from bittensor.core.extrinsics.commit_weights import (
84
_do_commit_weights,
95
_do_reveal_weights,
106
)
11-
12-
13-
@pytest.fixture
14-
def subtensor(mocker):
15-
fake_substrate = mocker.MagicMock()
16-
fake_substrate.websocket.sock.getsockopt.return_value = 0
17-
mocker.patch.object(
18-
subtensor_module, "SubstrateInterface", return_value=fake_substrate
19-
)
20-
return Subtensor()
7+
from bittensor.core.settings import version_as_int
218

229

2310
def test_do_commit_weights(subtensor, mocker):

tests/unit_tests/extrinsics/test_transfer.py

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,7 @@
1-
import pytest
2-
3-
from bittensor.core import subtensor as subtensor_module
41
from bittensor.core.extrinsics.transfer import _do_transfer
5-
from bittensor.core.subtensor import Subtensor
62
from bittensor.utils.balance import Balance
73

84

9-
@pytest.fixture
10-
def subtensor(mocker):
11-
fake_substrate = mocker.MagicMock()
12-
fake_substrate.websocket.sock.getsockopt.return_value = 0
13-
mocker.patch.object(
14-
subtensor_module, "SubstrateInterface", return_value=fake_substrate
15-
)
16-
return Subtensor()
17-
18-
195
def test_do_transfer_is_success_true(subtensor, mocker):
206
"""Successful do_transfer call."""
217
# Prep

tests/unit_tests/test_axon.py

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
1+
import asyncio
2+
import contextlib
13
import re
4+
import threading
25
import time
36
from dataclasses import dataclass
47
from typing import Any, Optional, Tuple
58
from unittest import IsolatedAsyncioTestCase
69
from unittest.mock import AsyncMock, MagicMock, patch
710

11+
import aiohttp
812
import fastapi
913
import netaddr
1014
import pydantic
1115
import pytest
16+
import uvicorn
1217
from fastapi.testclient import TestClient
1318
from starlette.requests import Request
1419

15-
from bittensor.core.axon import AxonMiddleware, Axon
20+
from bittensor.core.axon import Axon, AxonMiddleware, FastAPIThreadedServer
1621
from bittensor.core.errors import RunException
1722
from bittensor.core.settings import version_as_int
1823
from bittensor.core.stream import StreamingSynapse
@@ -26,7 +31,7 @@
2631
)
2732

2833

29-
def test_attach_initial():
34+
def test_attach_initial(mock_get_external_ip):
3035
# Create a mock AxonServer instance
3136
server = Axon()
3237

@@ -71,7 +76,7 @@ def wrong_verify_fn(synapse: TestSynapse) -> bool:
7176
server.attach(forward_fn, blacklist_fn, priority_fn, wrong_verify_fn)
7277

7378

74-
def test_attach():
79+
def test_attach(mock_get_external_ip):
7580
# Create a mock AxonServer instance
7681
server = Axon()
7782

@@ -144,7 +149,7 @@ def mock_request():
144149

145150

146151
@pytest.fixture
147-
def axon_instance():
152+
def axon_instance(mock_get_external_ip):
148153
axon = Axon()
149154
axon.required_hash_fields = {"test_endpoint": ["field1", "field2"]}
150155
axon.forward_class_types = {
@@ -329,7 +334,7 @@ async def test_verify_body_integrity_error_cases(
329334
(MockInfo(), "MockInfoString", "edge_case_empty_string"),
330335
],
331336
)
332-
def test_to_string(info_return, expected_output, test_id):
337+
def test_to_string(info_return, expected_output, test_id, mock_get_external_ip):
333338
# Arrange
334339
axon = Axon()
335340
with patch.object(axon, "info", return_value=info_return):
@@ -358,7 +363,9 @@ def test_to_string(info_return, expected_output, test_id):
358363
),
359364
],
360365
)
361-
def test_valid_ipv4_and_ipv6_address(ip, port, expected_ip_type, test_id):
366+
def test_valid_ipv4_and_ipv6_address(
367+
ip, port, expected_ip_type, test_id, mock_get_external_ip
368+
):
362369
# Arrange
363370
hotkey = MockHotkey("5EemgxS7cmYbD34esCFoBgUZZC8JdnGtQvV5Qw3QFUCRRtGP")
364371
coldkey = MockHotkey("5EemgxS7cmYbD34esCFoBgUZZC8JdnGtQvV5Qw3QFUCRRtGP")
@@ -431,7 +438,14 @@ def test_invalid_ip_address(ip, port, expected_exception):
431438
],
432439
)
433440
def test_axon_str_representation(
434-
ip, port, ss58_address, started, forward_fns, expected_str, test_id
441+
ip,
442+
port,
443+
ss58_address,
444+
started,
445+
forward_fns,
446+
expected_str,
447+
test_id,
448+
mock_get_external_ip,
435449
):
436450
# Arrange
437451
hotkey = MockHotkey(ss58_address)
@@ -765,3 +779,50 @@ async def forward_fn(synapse: streaming_synapse_cls):
765779
"computed_body_hash": "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a",
766780
},
767781
)
782+
783+
784+
@pytest.mark.asyncio
785+
async def test_threaded_fastapi():
786+
server_started = threading.Event()
787+
server_stopped = threading.Event()
788+
789+
@contextlib.asynccontextmanager
790+
async def lifespan(app):
791+
server_started.set()
792+
yield
793+
server_stopped.set()
794+
795+
app = fastapi.FastAPI(
796+
lifespan=lifespan,
797+
)
798+
app.get("/")(lambda: "Hello World")
799+
800+
server = FastAPIThreadedServer(
801+
uvicorn.Config(app, loop="none"),
802+
)
803+
server.start()
804+
805+
server_started.wait(3.0)
806+
807+
async def wait_for_server():
808+
while not (server.started or server_stopped.is_set()):
809+
await asyncio.sleep(1.0)
810+
811+
await asyncio.wait_for(wait_for_server(), 7.0)
812+
813+
assert server.is_running is True
814+
815+
async with aiohttp.ClientSession(
816+
base_url="http://127.0.0.1:8000",
817+
) as session:
818+
async with session.get("/") as response:
819+
assert await response.text() == '"Hello World"'
820+
821+
server.stop()
822+
823+
assert server.should_exit is True
824+
825+
server_stopped.wait()
826+
827+
with pytest.raises(aiohttp.ClientConnectorError):
828+
await session.get("/")

tests/unit_tests/test_dendrite.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def dummy(synapse: SynapseDummy) -> SynapseDummy:
2929

3030

3131
@pytest.fixture
32-
def setup_dendrite():
32+
def setup_dendrite(mock_get_external_ip):
3333
# Assuming bittensor.Wallet() returns a wallet object
3434
user_wallet = get_mock_wallet()
3535
dendrite_obj = Dendrite(user_wallet)
@@ -51,7 +51,10 @@ def axon_info():
5151
@pytest.fixture(scope="session")
5252
def setup_axon():
5353
wallet = get_mock_wallet()
54-
axon = Axon(wallet)
54+
axon = Axon(
55+
wallet,
56+
external_ip="192.168.1.1",
57+
)
5558
axon.attach(forward_fn=dummy)
5659
axon.start()
5760
yield axon
@@ -103,15 +106,15 @@ def __await__(self):
103106
return self().__await__()
104107

105108

106-
def test_dendrite_create_wallet():
109+
def test_dendrite_create_wallet(mock_get_external_ip):
107110
d = Dendrite(get_mock_wallet())
108111
d = Dendrite(get_mock_wallet().hotkey)
109112
d = Dendrite(get_mock_wallet().coldkeypub)
110113
assert d.__str__() == d.__repr__()
111114

112115

113116
@pytest.mark.asyncio
114-
async def test_forward_many():
117+
async def test_forward_many(mock_get_external_ip):
115118
n = 10
116119
d = Dendrite(wallet=get_mock_wallet())
117120
d.call = AsyncMock()
@@ -128,7 +131,7 @@ async def test_forward_many():
128131
assert len([resp]) == 1
129132

130133

131-
def test_pre_process_synapse():
134+
def test_pre_process_synapse(mock_get_external_ip):
132135
d = Dendrite(wallet=get_mock_wallet())
133136
s = Synapse()
134137
synapse = d.preprocess_synapse_for_request(

0 commit comments

Comments
 (0)