',tox)
+
+### run tests wit tox
+tox: .tox
+
+tox-clean:
rm -rf .tox
+
+test-clean: tox-clean
+ rm -f .coverage
+
+test-sterile: test-clean
+ find logs -type f -not -name README.md -exec rm -f '{}' +
diff --git a/make/version.mk b/make/version.mk
index 556f709..31f9f00 100644
--- a/make/version.mk
+++ b/make/version.mk
@@ -40,8 +40,8 @@ bump-major: version-update
version-update:
$(call gitclean)
[ -f .bumpversion.cfg ] || { echo "$$BUMPVERSION_CFG" >.bumpversion.cfg; git add .bumpversion.cfg; }
- $(MAKE) requirements.txt requirements-dev.txt requirements-docs.txt
- git add requirements.txt requirements-dev.txt requirements-docs.txt
+ $(MAKE) --no-print-directory requirements
+ git add requirements*.txt
sed -E -i $(module)/version.py -e "s/(.*__timestamp__.*=).*/\1 \"$$(date --rfc-3339=seconds)\"/"
git add $(module)/version.py VERSION
@echo "Updated version.py timestamp and requirements.txt"
@@ -49,3 +49,6 @@ version-update:
# clean up version tempfiles
version-clean:
@:
+
+version-sterile:
+ rm -f .bumpversion.cfg
diff --git a/pyproject.toml b/pyproject.toml
index 106b7a8..87cbdbe 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -40,6 +40,7 @@ dev = [
"pyrate-limiter",
"pytest",
"pytest-datadir",
+ "tox"
]
docs = [
"m2r2",
diff --git a/pytest.ini b/pytest.ini
index 65afe8e..1c40189 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -1,2 +1,2 @@
[pytest]
-addopts = -xv
+addopts = -x
diff --git a/requirements-dev.txt b/requirements-dev.txt
index bfdb5b4..98edf91 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -9,3 +9,4 @@ pdbpp
pyrate-limiter
pytest
pytest-datadir
+tox
diff --git a/requirements-docs.txt b/requirements-docs.txt
deleted file mode 100644
index 42c731e..0000000
--- a/requirements-docs.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-m2r2
-sphinx
-sphinx-click
-sphinx-rtd-theme
diff --git a/rstms_etherscan_python/__init__.py b/rstms_etherscan_python/__init__.py
index 29f86c0..45d933e 100644
--- a/rstms_etherscan_python/__init__.py
+++ b/rstms_etherscan_python/__init__.py
@@ -7,6 +7,7 @@
"""
+from .etherscan import Etherscan
from .modules.accounts import Accounts as accounts
from .modules.blocks import Blocks as blocks
from .modules.contracts import Contracts as contracts
@@ -19,6 +20,7 @@
from .version import __version__
__all__ = [
+ "Etherscan",
"__version__",
"accounts",
"blocks",
diff --git a/rstms_etherscan_python/configs/GOERLI-stable.json b/rstms_etherscan_python/configs/GOERLI-stable.json
index 5fc57ab..03cde83 100644
--- a/rstms_etherscan_python/configs/GOERLI-stable.json
+++ b/rstms_etherscan_python/configs/GOERLI-stable.json
@@ -81,14 +81,14 @@
"gas": "0x5f5e0ff"
}
},
- "get_est_confirmation_time": {
+ "_get_est_confirmation_time": {
"module": "gastracker",
"kwargs": {
"gas_price": "2000000000"
}
},
- "get_gas_oracle": {
- "module": "gastracker",
+ "_get_gas_oracle": {
+ "module": "gastracker",
"kwargs": {}
},
"get_block_reward_by_block_number": {
@@ -97,10 +97,10 @@
"block_no": "2165403"
}
},
- "get_est_block_countdown_time_by_block_number": {
+ "_get_est_block_countdown_time_by_block_number": {
"module": "blocks",
"kwargs": {
- "block_no": "99999999"
+ "block_no": "16701588"
}
},
"get_block_number_by_timestamp": {
diff --git a/rstms_etherscan_python/configs/MAIN-stable.json b/rstms_etherscan_python/configs/MAIN-stable.json
index 6629a42..5e70803 100644
--- a/rstms_etherscan_python/configs/MAIN-stable.json
+++ b/rstms_etherscan_python/configs/MAIN-stable.json
@@ -94,13 +94,13 @@
"get_block_reward_by_block_number": {
"module": "blocks",
"kwargs": {
- "block_no": "2165403"
+ "block_no": "12697906"
}
},
- "get_est_block_countdown_time_by_block_number": {
+ "_get_est_block_countdown_time_by_block_number": {
"module": "blocks",
"kwargs": {
- "block_no": "99999999"
+ "block_no": "16701588"
}
},
"get_block_number_by_timestamp": {
diff --git a/rstms_etherscan_python/etherscan.py b/rstms_etherscan_python/etherscan.py
index ef6199b..06c45af 100644
--- a/rstms_etherscan_python/etherscan.py
+++ b/rstms_etherscan_python/etherscan.py
@@ -3,8 +3,6 @@
import requests
-import etherscan
-
from . import configs
from .enums.fields_enum import FieldsEnum as fields
from .utils.parsing import ResponseParser as parser
@@ -37,6 +35,8 @@ def wrapper(*args, **kwargs):
@classmethod
def from_config(cls, api_key: str, config_path: str, net: str):
+ import rstms_etherscan_python as etherscan
+
config = cls.__load_config(config_path)
for func, v in config.items():
if not func.startswith("_"): # disabled if _
diff --git a/rstms_etherscan_python/exceptions.py b/rstms_etherscan_python/exceptions.py
new file mode 100644
index 0000000..ed3c245
--- /dev/null
+++ b/rstms_etherscan_python/exceptions.py
@@ -0,0 +1,17 @@
+# Etherscan API Exceptions
+
+
+class EtherscanUnauthorizedEndpoint(Exception):
+ pass
+
+
+class EtherscanErrorResponse(Exception):
+ pass
+
+
+class EtherscanStatusFailure(Exception):
+ pass
+
+
+class EtherscanUnexpectedResponse(Exception):
+ pass
diff --git a/rstms_etherscan_python/utils/parsing.py b/rstms_etherscan_python/utils/parsing.py
index b08d0c8..e95c629 100644
--- a/rstms_etherscan_python/utils/parsing.py
+++ b/rstms_etherscan_python/utils/parsing.py
@@ -1,19 +1,36 @@
import requests
+from ..exceptions import (
+ EtherscanErrorResponse,
+ EtherscanStatusFailure,
+ EtherscanUnauthorizedEndpoint,
+ EtherscanUnexpectedResponse,
+)
+
+PRO_ENDPOINT_RESPONSE = (
+ "Sorry, it looks like you are trying to access an API Pro endpoint. Contact us to upgrade to API Pro."
+)
+
class ResponseParser:
@staticmethod
def parse(response: requests.Response):
content = response.json()
- result = content["result"]
+ if "result" in content.keys():
+ result = content["result"]
+ elif "error" in content.keys():
+ raise EtherscanErrorResponse(f"response={content}")
+ else:
+ raise EtherscanUnexpectedResponse(f"response={content}")
if "status" in content.keys():
status = bool(int(content["status"]))
- message = content["message"]
- assert status, f"{result} -- {message}"
+ if result == PRO_ENDPOINT_RESPONSE:
+ url, _, _ = response.url.partition("apikey=")
+ url += "apikey=..."
+ raise EtherscanUnauthorizedEndpoint(f"{result} {url=}")
+ if status is None:
+ raise EtherscanStatusFailure(f"response={content}")
else:
# GETH or Parity proxy msg format
- # TODO: see if we need those values
- # jsonrpc = content["jsonrpc"]
- # cid = int(content["id"])
- pass
+ result = dict(jsonrpc=content["jsonrpc"], cid=int(content["id"]), result=result)
return result
diff --git a/rstms_etherscan_python/version.py b/rstms_etherscan_python/version.py
index c377db3..254c121 100644
--- a/rstms_etherscan_python/version.py
+++ b/rstms_etherscan_python/version.py
@@ -1 +1 @@
-__version__ = "2.1.8"
+__version__ = "2.1.9"
diff --git a/tests/test_modules.py b/tests/test_modules.py
index eae929d..35156ef 100644
--- a/tests/test_modules.py
+++ b/tests/test_modules.py
@@ -4,11 +4,17 @@
from datetime import datetime
from unittest import TestCase
-from etherscan.etherscan import Etherscan
+from rstms_etherscan_python import Etherscan
+from rstms_etherscan_python.exceptions import (
+ EtherscanErrorResponse,
+ EtherscanUnauthorizedEndpoint,
+)
-CONFIG_PATH = "etherscan/configs/{}-stable.json"
+CONFIG_PATH = "rstms_etherscan_python/configs/{}-stable.json"
API_KEY = os.environ["API_KEY"] # Encrypted env var by Travis
+TEST_API_PRO_ENDPOINTS = "TEST_API_PRO_ENDPOINTS" in os.environ
+
def test_init():
api = Etherscan(API_KEY)
@@ -38,8 +44,20 @@ def methods(self, net):
for fun, v in config.items():
if not fun.startswith("_"): # disabled if _
if v["module"] == self._MODULE:
- res = getattr(etherscan, fun)(**v["kwargs"])
- print(f"METHOD: {fun}, RTYPE: {type(res)}")
+ try:
+ res = getattr(etherscan, fun)(**v["kwargs"])
+ rtype = type(res)
+ except EtherscanUnauthorizedEndpoint as exc:
+ if TEST_API_PRO_ENDPOINTS:
+ raise exc from exc
+ else:
+ rtype = type(exc)
+ res = repr(exc)
+ except EtherscanErrorResponse as exc:
+ rtype = type(exc)
+ res = repr(exc)
+
+ print(f"METHOD: {fun}, RTYPE: {rtype}")
# Create log files (will update existing ones)
fname = f"logs/standard/{net}-{fun}.json"
log = {
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..e0ecba7
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,30 @@
+[tox]
+envlist = flake8, py310
+isolated_build = True
+
+[testenv:flake8]
+skip_install = True
+basepython = python
+deps = flake8
+commands = flake8 rstms_etherscan_python tests
+package = skip
+
+[testenv:py310]
+setenv =
+passenv =
+ API_KEY
+ TEST_API_PRO_ENDPOINTS
+package = skip
+skip_install = True
+allowlist_externals = pip pytest
+commands =
+ pip install -f dist .[dev]
+ pytest {env:PYTEST_OPTIONS} --basetemp={envtmpdir}
+
+[flake8]
+max-line-length = 120
+show-source = False
+max-complexity = 10
+extend-ignore =
+ E501,
+ W505
pFad - Phonifier reborn
Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy