Skip to content

Commit 3d7f156

Browse files
author
Matthias Zimmermann
committed
refactor conftest.py to use ProviderBuilder
1 parent 9695671 commit 3d7f156

File tree

3 files changed

+63
-49
lines changed

3 files changed

+63
-49
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,13 @@ Here's a "Hello World!" example showing how to use the Python Arkiv SDK:
2727
from web3 import HTTPProvider
2828
from arkiv import Arkiv
2929
from arkiv.account import NamedAccount
30+
from arkiv.provider import ProviderBuilder
3031

3132
with open ('wallet_alice.json', 'r') as f:
3233
wallet = f.read()
3334

3435
# Initialize Arkiv client (extends Web3)
35-
provider = HTTPProvider('https://kaolin.hoodi.arkiv.network/rpc')
36+
provider = ProviderBuilder().kaolin().build()
3637
account = NamedAccount.from_wallet('Alice', wallet, 's3cret')
3738
client = Arkiv(provider, account = account)
3839

src/arkiv/provider.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Provider builder for creating Web3 providers with Arkiv presets."""
22

3-
from typing import Literal
3+
from typing import Literal, cast
44

55
from web3.providers import HTTPProvider, WebSocketProvider
66
from web3.providers.base import BaseProvider
@@ -45,8 +45,8 @@ class ProviderBuilder:
4545

4646
def __init__(self) -> None:
4747
"""Initialize the provider builder."""
48-
self._network: str = NETWORK_DEFAULT
49-
self._transport: TransportType = TRANSPORT_DEFAULT
48+
self._network: str | None = NETWORK_DEFAULT
49+
self._transport: TransportType = cast(TransportType, TRANSPORT_DEFAULT)
5050
self._port: int | None = DEFAULT_PORT # Set default port for localhost
5151
self._url: str | None = None
5252

@@ -99,7 +99,7 @@ def http(self) -> "ProviderBuilder":
9999
Returns:
100100
Self for method chaining
101101
"""
102-
self._transport = HTTP
102+
self._transport = cast(TransportType, HTTP)
103103
return self
104104

105105
def ws(self) -> "ProviderBuilder":
@@ -109,7 +109,7 @@ def ws(self) -> "ProviderBuilder":
109109
Returns:
110110
Self for method chaining
111111
"""
112-
self._transport = WS
112+
self._transport = cast(TransportType, WS)
113113
return self
114114

115115
def build(self) -> BaseProvider:
@@ -123,6 +123,7 @@ def build(self) -> BaseProvider:
123123
Raises:
124124
ValueError: If no URL has been configured or if transport is not available
125125
"""
126+
url: str
126127
# Custom URL overrides network constant
127128
if self._url is not None:
128129
url = self._url
@@ -132,13 +133,14 @@ def build(self) -> BaseProvider:
132133
if network_urls is None:
133134
raise ValueError(f"Unknown network: {self._network}")
134135

135-
url = network_urls.get(self._transport)
136-
if url is None:
136+
url_from_network = network_urls.get(self._transport)
137+
if url_from_network is None:
137138
available = ", ".join(network_urls.keys())
138139
raise ValueError(
139140
f"Transport '{self._transport}' is not available for network '{self._network}'. "
140141
f"Available transports: {available}"
141142
)
143+
url = url_from_network
142144

143145
if self._port is not None:
144146
# Append port only for localhost
@@ -154,4 +156,4 @@ def build(self) -> BaseProvider:
154156
if self._transport == HTTP:
155157
return HTTPProvider(url)
156158
else:
157-
return WebSocketProvider(url)
159+
return cast(BaseProvider, WebSocketProvider(url))

tests/conftest.py

Lines changed: 51 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212
import pytest
1313
from testcontainers.core.container import DockerContainer
1414
from web3 import Web3
15+
from web3.providers.base import BaseProvider
1516

1617
from arkiv import Arkiv
1718
from arkiv.account import NamedAccount
19+
from arkiv.provider import ProviderBuilder
1820
from tests.node_container import create_container_node
1921

2022
from .utils import create_account
@@ -46,6 +48,30 @@ def _load_env_if_available() -> None:
4648
logger.debug("python-dotenv not available, using system environment only")
4749

4850

51+
def check_and_get_web3(name: str, provider: BaseProvider) -> Web3:
52+
"""Create and verify a Web3 client connection."""
53+
return verify_connection(name, Web3(provider))
54+
55+
56+
def check_and_get_arkiv(
57+
name: str, provider: BaseProvider, account: NamedAccount | None = None
58+
) -> Arkiv:
59+
"""Create and verify an Arkiv client connection."""
60+
if account:
61+
return verify_connection(name, Arkiv(provider, account=account))
62+
else:
63+
return verify_connection(name, Arkiv(provider))
64+
65+
66+
def verify_connection(name: str, client: Web3) -> Web3:
67+
"""Verify Web3 client connection and return checked client."""
68+
assert client.is_connected(), (
69+
f"{name}: Failed to connect to {client.provider.endpoint_uri}"
70+
) # type: ignore[attr-defined]
71+
logger.info(f"{name} client connected to {client.provider.endpoint_uri}") # type: ignore[attr-defined]
72+
return client
73+
74+
4975
# Load environment on import
5076
_load_env_if_available()
5177

@@ -74,59 +100,44 @@ def arkiv_node() -> Generator[tuple[DockerContainer | None, str, str], None, Non
74100

75101

76102
@pytest.fixture(scope="session")
77-
def web3_client_http(arkiv_node: tuple[DockerContainer | None, str, str]) -> Web3:
78-
"""
79-
Provide Web3 client connected to HTTP endpoint.
80-
81-
Returns:
82-
Web3 client instance connected to the arkiv node
83-
"""
103+
def testcontainer_provider_http(
104+
arkiv_node: tuple[DockerContainer | None, str, str],
105+
) -> BaseProvider:
106+
"""Returns testcontainer HTTPProvider using the ProviderBuilder."""
84107
_, http_url, _ = arkiv_node
85-
client = Web3(Web3.HTTPProvider(http_url))
108+
return ProviderBuilder().custom(http_url).build()
86109

87-
# Verify connection
88-
assert client.is_connected(), f"Failed to connect to {http_url}"
89110

90-
logger.info(f"Web3 client connected to {http_url}")
91-
return client
111+
@pytest.fixture(scope="session")
112+
def testcontainer_provider_ws(
113+
arkiv_node: tuple[DockerContainer | None, str, str],
114+
) -> BaseProvider:
115+
"""Returns testcontainer WebSocketProvider using the ProviderBuilder."""
116+
_, _, ws_url = arkiv_node
117+
# Note: .ws() is to make transport explicit, it is not strictly necessary as the URL scheme is ws:// or wss://
118+
return ProviderBuilder().custom(ws_url).ws().build()
92119

93120

94121
@pytest.fixture(scope="session")
95-
def arkiv_ro_client_http(arkiv_node: tuple[DockerContainer | None, str, str]) -> Arkiv:
96-
"""
97-
Provide Arkiv client connected to HTTP endpoint.
122+
def web3_client_http(testcontainer_provider_http: BaseProvider) -> Web3:
123+
"""Return Web3 client connected to HTTP endpoint."""
124+
return check_and_get_web3("Web3 HTTP", testcontainer_provider_http)
98125

99-
Returns:
100-
Web3 client instance connected to the arkiv node
101-
"""
102-
_, http_url, _ = arkiv_node
103-
client = Arkiv(Web3.HTTPProvider(http_url))
104126

105-
# Verify connection
106-
assert client.is_connected(), f"Failed to connect Arkiv client to {http_url}"
107-
108-
logger.info(f"Arkiv client connected to {http_url}")
109-
return client
127+
@pytest.fixture(scope="session")
128+
def arkiv_ro_client_http(testcontainer_provider_http: BaseProvider) -> Arkiv:
129+
"""Return a read only Arkiv client connected to HTTP endpoint."""
130+
return check_and_get_arkiv("Arkiv HTTP (read only)", testcontainer_provider_http)
110131

111132

112133
@pytest.fixture(scope="session")
113134
def arkiv_client_http(
114-
arkiv_node: tuple[DockerContainer | None, str, str], account_1: NamedAccount
135+
testcontainer_provider_http: BaseProvider, account_1: NamedAccount
115136
) -> Arkiv:
116-
"""
117-
Provide Arkiv client connected to HTTP endpoint.
118-
119-
Returns:
120-
Web3 client instance connected to the arkiv node
121-
"""
122-
_, http_url, _ = arkiv_node
123-
client = Arkiv(Web3.HTTPProvider(http_url), account=account_1)
124-
125-
# Verify connection
126-
assert client.is_connected(), f"Failed to connect Arkiv client to {http_url}"
127-
128-
logger.info(f"Arkiv client connected to {http_url}")
129-
return client
137+
"""Return a read only Arkiv client connected to HTTP endpoint."""
138+
return check_and_get_arkiv(
139+
"Arkiv HTTP", testcontainer_provider_http, account=account_1
140+
)
130141

131142

132143
@pytest.fixture(scope="session")

0 commit comments

Comments
 (0)