• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

safe-global / safe-eth-py / 19324708014

13 Nov 2025 08:06AM UTC coverage: 85.227% (-8.6%) from 93.85%
19324708014

Pull #2120

github

web-flow
Merge eb6a26957 into 0a3842ae6
Pull Request #2120: Update UserOperationV07

34 of 39 new or added lines in 6 files covered. (87.18%)

919 existing lines in 26 files now uncovered.

9011 of 10573 relevant lines covered (85.23%)

0.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

41.86
/safe_eth/safe/api/base_api.py
1
from abc import ABC, abstractmethod
1✔
2
from typing import Dict, Optional
1✔
3

4
import requests
1✔
5

6
from safe_eth.eth.ethereum_client import (
1✔
7
    EthereumClient,
8
    EthereumNetwork,
9
    EthereumNetworkNotSupported,
10
)
11
from safe_eth.util.http import build_full_url, prepare_http_session
1✔
12

13

14
class SafeAPIException(Exception):
1✔
15
    pass
1✔
16

17

18
class SafeBaseAPI(ABC):
1✔
19
    def __init__(
1✔
20
        self,
21
        network: EthereumNetwork,
22
        ethereum_client: Optional[EthereumClient] = None,
23
        base_url: Optional[str] = None,
24
        api_key: Optional[str] = None,
25
        request_timeout: int = 10,
26
    ):
27
        """
28
        :param network: Network for the transaction service
29
        :param ethereum_client:
30
        :param base_url: If a custom transaction service is used
31
        :param api_key: Api key of authenticated Safe services
32
        :raises: EthereumNetworkNotSupported
33
        """
UNCOV
34
        self.network = network
×
UNCOV
35
        self.ethereum_client = ethereum_client
×
UNCOV
36
        self.base_url = self._get_api_base_url(network, base_url)
×
UNCOV
37
        self.api_key = api_key
×
UNCOV
38
        self.http_session = prepare_http_session(10, 100)
×
UNCOV
39
        self.request_timeout = request_timeout
×
40

41
    @abstractmethod
1✔
42
    def _get_url_by_network(self, network: EthereumNetwork) -> Optional[str]:
1✔
43
        """
44
        Should return the base URL for the given network.
45
        :param network: EthereumNetwork to get the base URL for.
46
        :return: base URL for the given network. None if not found.
47
        """
48
        pass
×
49

50
    def _get_api_base_url(
1✔
51
        self, network: EthereumNetwork, custom_base_url: Optional[str] = None
52
    ) -> str:
53
        """
54
        Returns the base API URL for the specified Ethereum network.
55
        If a custom_base_url is provided, it will be used instead of the default.
56
        Otherwise, the method attempts to retrieve the URL associated with the given network.
57

58
        :param network: The EthereumNetwork to get the base URL for.
59
        :param custom_base_url: Optional custom base URL to override the default.
60
        :return: The base URL corresponding to the specified network.
61
        :raises EthereumNetworkNotSupported: If the network is not supported and no URL is found.
62
        """
UNCOV
63
        base_url = custom_base_url or self._get_url_by_network(network)
×
UNCOV
64
        if not base_url:
×
UNCOV
65
            raise EthereumNetworkNotSupported(network)
×
UNCOV
66
        return base_url
×
67

68
    @classmethod
1✔
69
    def from_ethereum_client(
1✔
70
        cls,
71
        ethereum_client: EthereumClient,
72
        api_key: Optional[str] = None,
73
    ) -> "SafeBaseAPI":
UNCOV
74
        ethereum_network = ethereum_client.get_network()
×
UNCOV
75
        return cls(ethereum_network, ethereum_client=ethereum_client, api_key=api_key)
×
76

77
    def _get_request_headers(self, include_json_body: bool = False) -> Dict[str, str]:
1✔
78
        """
79
        Build the default HTTP headers for a request.
80

81
        :param include_json_body: If it includes the JSON Content-Type header.
82
        :return: Dictionary of headers to include in the request.
83
        """
UNCOV
84
        headers = {}
×
UNCOV
85
        if include_json_body:
×
86
            headers["Content-Type"] = "application/json"
×
UNCOV
87
        if self.api_key:
×
UNCOV
88
            headers["Authorization"] = f"Bearer {self.api_key}"
×
UNCOV
89
        return headers
×
90

91
    def _get_request(self, url: str) -> requests.Response:
1✔
UNCOV
92
        full_url = build_full_url(self.base_url, url)
×
UNCOV
93
        return self.http_session.get(
×
94
            full_url, headers=self._get_request_headers(), timeout=self.request_timeout
95
        )
96

97
    def _post_request(self, url: str, payload: Dict) -> requests.Response:
1✔
98
        full_url = build_full_url(self.base_url, url)
×
99
        return self.http_session.post(
×
100
            full_url,
101
            json=payload,
102
            headers=self._get_request_headers(include_json_body=True),
103
            timeout=self.request_timeout,
104
        )
105

106
    def _delete_request(self, url: str, payload: Dict) -> requests.Response:
1✔
107
        full_url = build_full_url(self.base_url, url)
×
108
        return self.http_session.delete(
×
109
            full_url,
110
            json=payload,
111
            headers=self._get_request_headers(include_json_body=True),
112
            timeout=self.request_timeout,
113
        )
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc