File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change 22
33## Unreleased
44
5+ ## [ 0.21.0] - 2025-02-28
6+
7+ ### Added
8+ - Ability to check UserOperation status using a string comparison.
9+
510## [ 0.20.0] - 2025-02-25
611
712### Added
Original file line number Diff line number Diff line change 1- __version__ = "0.20 .0"
1+ __version__ = "0.21 .0"
Original file line number Diff line number Diff line change 55from cdp .api_clients import ApiClients
66from cdp .cdp_api_client import CdpApiClient
77from cdp .constants import SDK_DEFAULT_SOURCE
8- from cdp .errors import InvalidConfigurationError
8+ from cdp .errors import InvalidConfigurationError , UninitializedSDKError
99
1010
1111class Cdp :
@@ -18,7 +18,7 @@ class Cdp:
1818 debugging (bool): Whether debugging is enabled.
1919 base_path (str): The base URL for the Platform API.
2020 max_network_retries (int): The maximum number of network retries.
21- api_clients (Optional[ApiClients]) : The Platform API clients instance.
21+ api_clients: The Platform API clients instance.
2222
2323 """
2424
@@ -30,7 +30,15 @@ class Cdp:
3030 debugging = False
3131 base_path = "https://api.cdp.coinbase.com/platform"
3232 max_network_retries = 3
33- api_clients : ApiClients | None = None
33+
34+ class ApiClientsWrapper :
35+ """Wrapper that raises a helpful error when SDK is not initialized."""
36+
37+ def __getattr__ (self , _name ):
38+ """Raise an error when accessing an attribute of the ApiClientsWrapper."""
39+ raise UninitializedSDKError ()
40+
41+ api_clients = ApiClientsWrapper ()
3442
3543 def __new__ (cls ):
3644 """Create or return the singleton instance of the Cdp class.
Original file line number Diff line number Diff line change 44from cdp .client .exceptions import ApiException
55
66
7+ class UninitializedSDKError (Exception ):
8+ """Exception raised when trying to access CDP API clients before SDK initialization."""
9+
10+ def __init__ (self ):
11+ message = (
12+ "Coinbase SDK has not been initialized. Please initialize by calling either:\n \n "
13+ + "- Cdp.configure(api_key_name='...', private_key='...')\n "
14+ "- Cdp.configure_from_json(file_path='/path/to/api_keys.json')\n \n "
15+ "If needed, register for API keys at https://portal.cdp.coinbase.com/ or view the docs at https://docs.cdp.coinbase.com/wallet-api/docs/welcome"
16+ )
17+ super ().__init__ (message )
18+
19+
720class ApiError (Exception ):
821 """A wrapper for API exceptions to provide more context."""
922
Original file line number Diff line number Diff line change @@ -41,6 +41,16 @@ def __repr__(self) -> str:
4141 """Return a string representation of the Status."""
4242 return str (self )
4343
44+ def __eq__ (self , other ):
45+ """Check if the status is equal to another object. Supports string comparison."""
46+ if isinstance (other , str ):
47+ return self .value == other
48+ return super ().__eq__ (other )
49+
50+ def __hash__ (self ):
51+ """Return a hash value for the enum member to allow use as dictionary keys."""
52+ return hash (self .name )
53+
4454 def __init__ (self , model : UserOperationModel , smart_wallet_address : str ) -> None :
4555 """Initialize the UserOperation class.
4656
Original file line number Diff line number Diff line change 1414
1515project = 'CDP SDK'
1616author = 'Coinbase Developer Platform'
17- release = '0.20 .0'
17+ release = '0.21 .0'
1818
1919# -- General configuration ---------------------------------------------------
2020# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
Original file line number Diff line number Diff line change 11[tool .poetry ]
22name = " cdp-sdk"
3- version = " 0.20 .0"
3+ version = " 0.21 .0"
44description = " CDP Python SDK"
55authors = [" John Peterson <john.peterson@coinbase.com>" ]
66license = " LICENSE.md"
Original file line number Diff line number Diff line change 11import os
2+ from unittest .mock import MagicMock
3+
4+ import pytest
5+
6+ from cdp import Cdp
7+ from cdp .api_clients import ApiClients
8+
9+
10+ @pytest .fixture (autouse = True )
11+ def initialize_cdp (request ):
12+ """Initialize the CDP SDK with mock API clients before each test."""
13+ # Skip this fixture for e2e tests
14+ if request .node .get_closest_marker ("e2e" ):
15+ yield
16+ return
17+
18+ original_api_clients = Cdp .api_clients
19+ mock_api_clients = MagicMock (spec = ApiClients )
20+ Cdp .api_clients = mock_api_clients
21+ yield
22+ Cdp .api_clients = original_api_clients
23+
224
325factory_modules = [
426 f [:- 3 ] for f in os .listdir ("./tests/factories" ) if f .endswith (".py" ) and f != "__init__.py"
Original file line number Diff line number Diff line change @@ -12,6 +12,7 @@ def dummy_key_factory():
1212 - "ed25519-32": Returns a base64-encoded 32-byte Ed25519 private key.
1313 - "ed25519-64": Returns a base64-encoded 64-byte dummy Ed25519 key (the first 32 bytes will be used).
1414 """
15+
1516 def _create_dummy (key_type : str = "ecdsa" ) -> str :
1617 if key_type == "ecdsa" :
1718 return (
@@ -25,9 +26,10 @@ def _create_dummy(key_type: str = "ecdsa") -> str:
2526 return "BXyKC+eFINc/6ztE/3neSaPGgeiU9aDRpaDnAbaA/vyTrUNgtuh/1oX6Vp+OEObV3SLWF+OkF2EQNPtpl0pbfA=="
2627 elif key_type == "ed25519-64" :
2728 # Create a 64-byte dummy by concatenating a 32-byte sequence with itself.
28- dummy_32 = b' \x01 ' * 32
29+ dummy_32 = b" \x01 " * 32
2930 dummy_64 = dummy_32 + dummy_32
3031 return base64 .b64encode (dummy_64 ).decode ("utf-8" )
3132 else :
3233 raise ValueError ("Unsupported key type for dummy key creation" )
34+
3335 return _create_dummy
Original file line number Diff line number Diff line change @@ -10,18 +10,21 @@ def test_parse_private_key_pem_ec(dummy_key_factory):
1010 parsed_key = _parse_private_key (dummy_key )
1111 assert isinstance (parsed_key , ec .EllipticCurvePrivateKey )
1212
13+
1314def test_parse_private_key_ed25519_32 (dummy_key_factory ):
1415 """Test that a base64-encoded 32-byte Ed25519 key is parsed correctly using a dummy key from the factory."""
1516 dummy_key = dummy_key_factory ("ed25519-32" )
1617 parsed_key = _parse_private_key (dummy_key )
1718 assert isinstance (parsed_key , ed25519 .Ed25519PrivateKey )
1819
20+
1921def test_parse_private_key_ed25519_64 (dummy_key_factory ):
2022 """Test that a base64-encoded 64-byte input is parsed correctly by taking the first 32 bytes using a dummy key from the factory."""
2123 dummy_key = dummy_key_factory ("ed25519-64" )
2224 parsed_key = _parse_private_key (dummy_key )
2325 assert isinstance (parsed_key , ed25519 .Ed25519PrivateKey )
2426
27+
2528def test_parse_private_key_invalid ():
2629 """Test that an invalid key string raises a ValueError."""
2730 with pytest .raises (ValueError , match = "Could not parse the private key" ):
You can’t perform that action at this time.
0 commit comments