Skip to content

Commit f471aad

Browse files
Resolve "Allow usage of other Kraken instances via CLI" (#407)
1 parent 7b3d8b2 commit f471aad

File tree

13 files changed

+383
-88
lines changed

13 files changed

+383
-88
lines changed

.github/workflows/_codecov.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ jobs:
9191
FUTURES_SECRET_KEY: ${{ secrets.FUTURES_SECRET_KEY }}
9292
FUTURES_SANDBOX_KEY: ${{ secrets.FUTURES_SANDBOX_KEY }}
9393
FUTURES_SANDBOX_SECRET: ${{ secrets.FUTURES_SANDBOX_SECRET }}
94-
run: pytest -vv --cov=kraken --cov-report=xml:coverage.xml --cov-report=term tests
94+
run: pytest -vv -x --cov=kraken --cov-report=xml:coverage.xml --cov-report=term tests
9595

9696
- name: Export coverage report
9797
uses: actions/upload-artifact@v4

.github/workflows/_test_futures_private.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,12 @@ jobs:
9898
FUTURES_SECRET_KEY: ${{ secrets.FUTURES_SECRET_KEY }}
9999
FUTURES_SANDBOX_KEY: ${{ secrets.FUTURES_SANDBOX_KEY }}
100100
FUTURES_SANDBOX_SECRET: ${{ secrets.FUTURES_SANDBOX_SECRET }}
101-
run: pytest -vv -m "futures and futures_auth and not futures_websocket" tests
101+
run: pytest -vv -x -m "futures and futures_auth and not futures_websocket" tests
102102

103103
- name: Testing Futures websocket client
104104
env:
105105
FUTURES_API_KEY: ${{ secrets.FUTURES_API_KEY }}
106106
FUTURES_SECRET_KEY: ${{ secrets.FUTURES_SECRET_KEY }}
107107
FUTURES_SANDBOX_KEY: ${{ secrets.FUTURES_SANDBOX_KEY }}
108108
FUTURES_SANDBOX_SECRET: ${{ secrets.FUTURES_SANDBOX_SECRET }}
109-
run: pytest -vv -m "futures and futures_auth and futures_websocket" tests
109+
run: pytest -vv -x -m "futures and futures_auth and futures_websocket" tests

.github/workflows/_test_futures_public.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ jobs:
7979
uv pip install $wheel -r requirements-dev.txt
8080
8181
- name: Testing Futures REST endpoints
82-
run: pytest -vv -m "futures and not futures_auth and not futures_websocket" tests
82+
run: pytest -vv -x -n auto -m "futures and not futures_auth and not futures_websocket" tests
8383

8484
- name: Testing Futures websocket endpoints
85-
run: pytest -vv -m "futures and not futures_auth and futures_websocket" tests
85+
run: pytest -vv -x -n auto -m "futures and not futures_auth and futures_websocket" tests

.github/workflows/_test_spot_private.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,10 @@ jobs:
100100
SPOT_SECRET_KEY: ${{ secrets.SPOT_SECRET_KEY }}
101101
XSTOCKS_API_KEY: ${{ secrets.XSTOCKS_API_KEY }}
102102
XSTOCKS_SECRET_KEY: ${{ secrets.XSTOCKS_SECRET_KEY }}
103-
run: pytest -vv -m "spot and spot_auth and not spot_websocket" tests
103+
run: pytest -vv -x -m "spot and spot_auth and not spot_websocket" tests
104104

105105
- name: Testing Spot websocket client
106106
env:
107107
SPOT_API_KEY: ${{ secrets.SPOT_API_KEY }}
108108
SPOT_SECRET_KEY: ${{ secrets.SPOT_SECRET_KEY }}
109-
run: pytest -vv -m "spot and spot_auth and spot_websocket" tests
109+
run: pytest -vv -x -m "spot and spot_auth and spot_websocket" tests

.github/workflows/_test_spot_public.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ jobs:
8080
uv pip install $wheel -r requirements-dev.txt
8181
8282
- name: Testing Spot REST endpoints
83-
run: pytest -vv -m "spot and not spot_auth and not spot_websocket" tests
83+
run: pytest -vv -x -n auto -m "spot and not spot_auth and not spot_websocket" tests
8484

8585
- name: Testing Spot websocket endpoints
86-
run: pytest -vv -m "spot and not spot_auth and spot_websocket" tests
86+
run: pytest -vv -x -n auto -m "spot and not spot_auth and spot_websocket" tests

.pre-commit-config.yaml

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,20 @@
77

88
repos:
99
- repo: https://github.com/astral-sh/ruff-pre-commit
10-
rev: v0.13.0
10+
rev: v0.13.3
1111
hooks:
12-
- id: ruff
12+
- id: ruff-check
1313
args:
1414
- --preview
1515
- --fix
1616
- --exit-non-zero-on-fix
17+
# Formatter disabled since it does not work together with COM812
18+
# - id: ruff-format
19+
# args:
20+
# - --preview
21+
# - --exit-non-zero-on-fix
1722
- repo: https://github.com/pre-commit/mirrors-mypy
18-
rev: v1.18.1
23+
rev: v1.18.2
1924
hooks:
2025
- id: mypy
2126
name: mypy
@@ -67,15 +72,15 @@ repos:
6772
- id: rst-inline-touching-normal
6873
- id: text-unicode-replacement-char
6974
- repo: https://github.com/psf/black
70-
rev: 25.1.0
75+
rev: 25.9.0
7176
hooks:
7277
- id: black
7378
- repo: https://github.com/rbubley/mirrors-prettier
7479
rev: v3.6.2
7580
hooks:
7681
- id: prettier
7782
- repo: https://github.com/PyCQA/isort # TODO: remove as soon as ruff is stable
78-
rev: 6.0.1
83+
rev: 6.1.0
7984
hooks:
8085
- id: isort
8186
args:

Makefile

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ UV ?= uv
1010
PYTHON := python
1111
PYTEST := $(UV) run pytest
1212
PYTEST_OPTS := -vv --junit-xml=pytest.xml
13-
PYTEST_COV_OPTS := $(PYTEST_OPTS) --cov=kraken --cov-report=xml:coverage.xml --cov-report=term-missing
13+
PYTEST_COV_OPTS := $(PYTEST_OPTS) --cov=kraken --cov-report=xml:coverage.xml --cov-report=term-missing --cov-report=html
1414
TEST_DIR := tests
1515

1616
## ======= M A K E F I L E - T A R G E T S =====================================
@@ -57,6 +57,7 @@ dev: check-uv
5757
.PHONY: test
5858
test:
5959
@rm .cache/tests/*.log || true
60+
./tests/cli/basic.sh
6061
$(PYTEST) $(PYTEST_OPTS) $(TEST_DIR)
6162

6263
.PHONY: tests
@@ -81,6 +82,7 @@ wip:
8182
.PHONY: coverage
8283
coverage:
8384
@rm .cache/tests/*.log || true
85+
./tests/cli/basic.sh
8486
$(PYTEST) $(PYTEST_COV_OPTS) $(TEST_DIR)
8587

8688
## doctest Run the documentation related tests
@@ -90,11 +92,11 @@ doctest:
9092
cd docs && make doctest
9193

9294
## ======= M I S C E L A N I O U S =============================================
93-
## pre-commit Run the pre-commit targets
95+
## prek Run the pre-commit targets
9496
##
95-
.PHONY: pre-commit
96-
pre-commit:
97-
@pre-commit run -a
97+
.PHONY: prek
98+
prek:
99+
@prek run -a
98100

99101
## ruff Run ruff without fix
100102
##

examples/futures_trading_bot_template.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
import asyncio
1717
import logging
18-
import logging.config
1918
import os
2019
import sys
2120
import traceback

requirements-dev.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ pytest-cov
55
pytest-mock
66
pytest-retry
77
pytest-timeout
8+
pytest-xdist

src/kraken/cli.py

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@
2525

2626
import logging
2727
import sys
28-
from re import sub as re_sub
2928
from typing import TYPE_CHECKING, Any
29+
from urllib.parse import urlparse
3030

3131
from click import echo
3232
from cloup import (
@@ -48,8 +48,12 @@
4848
LOG: logging.Logger = logging.getLogger(__name__)
4949

5050

51-
def print_version(ctx: Context, param: Any, value: Any) -> None: # noqa: ANN401, ARG001
52-
"""Prints the version of the package"""
51+
def _print_version(
52+
ctx: Context,
53+
param: Any, # noqa: ANN401, ARG001
54+
value: Any, # noqa: ANN401
55+
) -> None:
56+
"""Prints the version of the package."""
5357
if not value or ctx.resilient_parsing:
5458
return
5559
from importlib.metadata import version # noqa: PLC0415
@@ -58,6 +62,30 @@ def print_version(ctx: Context, param: Any, value: Any) -> None: # noqa: ANN401
5862
ctx.exit()
5963

6064

65+
def _get_base_url(url: str) -> str:
66+
"""Extracts the base URL from a full URL."""
67+
68+
parsed_url = urlparse(url)
69+
if parsed_url.scheme and parsed_url.netloc:
70+
return f"{parsed_url.scheme}://{parsed_url.netloc}"
71+
return ""
72+
73+
74+
def _get_uri_path(url: str) -> str:
75+
"""
76+
Extracts the URI path from a full URL or returns the URL if it's already a
77+
path.
78+
"""
79+
80+
parsed_url = urlparse(url)
81+
if parsed_url.scheme and parsed_url.netloc:
82+
path = parsed_url.path
83+
if parsed_url.query:
84+
path += f"?{parsed_url.query}"
85+
return path
86+
return url
87+
88+
6189
@group(
6290
context_settings={
6391
"auto_envvar_prefix": "KRAKEN",
@@ -76,7 +104,7 @@ def print_version(ctx: Context, param: Any, value: Any) -> None: # noqa: ANN401
76104
@option(
77105
"--version",
78106
is_flag=True,
79-
callback=print_version,
107+
callback=_print_version,
80108
expose_value=False,
81109
is_eager=True,
82110
)
@@ -142,26 +170,27 @@ def spot(ctx: Context, url: str, **kwargs: dict) -> None: # noqa: ARG001
142170
"""Access the Kraken Spot REST API"""
143171
from kraken.base_api import SpotClient # noqa: PLC0415
144172

145-
LOG.debug("Initialize the Kraken client")
146173
client = SpotClient(
147174
key=kwargs["api_key"], # type: ignore[arg-type]
148175
secret=kwargs["secret_key"], # type: ignore[arg-type]
176+
url=_get_base_url(url),
149177
)
150178

179+
uri = _get_uri_path(url)
151180
try:
152181
response = (
153182
client.request( # pylint: disable=protected-access,no-value-for-parameter
154183
method=kwargs["x"], # type: ignore[arg-type]
155-
uri=(uri := re_sub(r"https://.*.com", "", url)),
184+
uri=uri,
156185
params=orloads(kwargs.get("data") or "{}"),
157186
timeout=kwargs["timeout"], # type: ignore[arg-type]
158187
auth="private" in uri.lower(),
159188
)
160189
)
161190
except JSONDecodeError as exc:
162-
LOG.error(f"Could not parse the passed data. {exc}") # noqa: G004
191+
LOG.error("Could not parse the passed data. %s", exc)
163192
except Exception as exc: # noqa: BLE001
164-
LOG.error(f"Exception occurred: {exc}") # noqa: G004
193+
LOG.error("Exception occurred: %s", exc)
165194
sys.exit(1)
166195
else:
167196
echo(response)
@@ -218,27 +247,28 @@ def futures(ctx: Context, url: str, **kwargs: dict) -> None: # noqa: ARG001
218247
"""Access the Kraken Futures REST API"""
219248
from kraken.base_api import FuturesClient # noqa: PLC0415
220249

221-
LOG.debug("Initialize the Kraken client")
222250
client = FuturesClient(
223251
key=kwargs["api_key"], # type: ignore[arg-type]
224252
secret=kwargs["secret_key"], # type: ignore[arg-type]
253+
url=_get_base_url(url),
225254
)
226255

256+
uri = _get_uri_path(url)
227257
try:
228258
response = (
229259
client.request( # pylint: disable=protected-access,no-value-for-parameter
230260
method=kwargs["x"], # type: ignore[arg-type]
231-
uri=(uri := re_sub(r"https://.*.com", "", url)),
261+
uri=uri,
232262
post_params=orloads(kwargs.get("data") or "{}"),
233263
query_params=orloads(kwargs.get("query") or "{}"),
234264
timeout=kwargs["timeout"], # type: ignore[arg-type]
235265
auth="derivatives" in uri.lower(),
236266
)
237267
)
238268
except JSONDecodeError as exc:
239-
LOG.error(f"Could not parse the passed data. {exc}") # noqa: G004
269+
LOG.error("Could not parse the passed data. %s", exc)
240270
except Exception as exc: # noqa: BLE001
241-
LOG.error(f"Exception occurred: {exc}") # noqa: G004
271+
LOG.error("Exception occurred: %s", exc)
242272
sys.exit(1)
243273
else:
244274
echo(response)

0 commit comments

Comments
 (0)