Skip to content

Commit

Permalink
Enforce and refactor Client identification
Browse files Browse the repository at this point in the history
  • Loading branch information
cachitas committed Apr 4, 2024
1 parent 6a0c503 commit 1d449f6
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 52 deletions.
16 changes: 7 additions & 9 deletions src/stringx/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,28 @@
STRING API client using httpx.
"""

from .client import Client
from .client import Client, __version__

__version__ = "0.3.0"
__all__ = ["__version__", "Client"]

__all__ = ["Client"]

DEFAULT_CALLER_IDENTITY = f"{__name__} {__version__}"
identity = ""


def map(identifiers: list[str], species: int):
with Client() as client:
with Client(identity=identity) as client:
return client.map(identifiers=identifiers, species=species)


def network(identifiers: list[str], species: int):
with Client() as client:
with Client(identity=identity) as client:
return client.network(identifiers=identifiers, species=species)


def interaction_partners(identifiers: list[str], species: int):
with Client() as client:
with Client(identity=identity) as client:
return client.interaction_partners(identifiers=identifiers, species=species)


def version():
with Client() as client:
with Client(identity=identity) as client:
return client.version()
19 changes: 9 additions & 10 deletions src/stringx/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@

import httpx

logger = logging.getLogger(__name__)
__version__ = "0.3.0"

logger = logging.getLogger("stringx")


class Client(httpx.Client):
def __init__(
self, base_url: str = "https://string-db.org", *, identity: str | None = None
) -> None:
from stringx import DEFAULT_CALLER_IDENTITY
def __init__(self, identity: str, base_url: str = "https://string-db.org") -> None:
if not identity:
raise ValueError("Client identity must be a non-empty string.")

self.identity = f"{identity} (python-stringx/{__version__})"

super().__init__(
params={"caller_identity": identity or DEFAULT_CALLER_IDENTITY},
params={"caller_identity": self.identity},
base_url=base_url,
)

Expand All @@ -27,11 +30,7 @@ def request(
if params is None:
params = {}
url = "/".join(["api", format, endpoint])
logger.info("POST", url, params)
print("params BEFORE request", self.params)
response = super().request(method, url, params=params)
print("params AFTER request", self.params)
logger.info(response.status_code)
response.raise_for_status()
return response.json()

Expand Down
8 changes: 8 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import pytest
import stringx


@pytest.fixture(scope="session")
def test_client():
with stringx.Client(identity="test client") as client:
yield client
74 changes: 41 additions & 33 deletions tests/test_client.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,51 @@
import httpx
import pytest
import stringx


def test_default_base_url():
with stringx.Client() as client:
with stringx.Client("test") as client:
assert client.base_url.scheme == "https"
assert client.base_url.host == "string-db.org"
assert client.base_url.path == "/"


def test_custom_address():
with stringx.Client("https://example.com/api/v2") as client:
with stringx.Client("test", "https://example.com/api/v2") as client:
assert client.base_url.host == "example.com"
assert client.base_url.path == "/api/v2/"


def test_timeout():
with stringx.Client() as client:
with stringx.Client("test") as client:
assert client.timeout.connect == 5.0
assert client.timeout.pool == 5.0
assert client.timeout.read == 5.0
assert client.timeout.write == 5.0


def test_caller_identity():
with stringx.Client() as client:
assert client.params["caller_identity"] == stringx.DEFAULT_CALLER_IDENTITY
def test_mandatory_client_identity():
with pytest.raises(TypeError, match="identity"):
stringx.Client() # type: ignore

with pytest.raises(ValueError, match="Client identity must be a non-empty string"):
stringx.Client(identity="")


def test_client_identity(test_client):
assert test_client.identity == f"test client (python-stringx/{stringx.__version__})"


def test_caller_identity(test_client):
assert test_client.params["caller_identity"] == test_client.identity


def test_custom_caller_identity():
custom_identity = "random tool"
with stringx.Client(identity=custom_identity) as client:
assert client.params["caller_identity"] == custom_identity
with stringx.Client(identity="random tool") as client:
assert client.params["caller_identity"] == client.identity


def test_map(httpx_mock):
def test_map(httpx_mock, test_client):
httpx_mock.add_response(
url=httpx.URL(
"https://string-db.org/api/json/get_string_ids",
Expand All @@ -43,18 +54,17 @@ def test_map(httpx_mock):
"species": "7227",
"limit": 1,
"echo_query": True,
"caller_identity": stringx.DEFAULT_CALLER_IDENTITY,
"caller_identity": test_client.identity,
},
),
method="POST",
json=True,
)

with stringx.Client() as client:
client.map(["some_identifier"], 7227)
test_client.map(["some_identifier"], 7227)


def test_network(httpx_mock):
def test_network(httpx_mock, test_client):
httpx_mock.add_response(
url=httpx.URL(
"https://string-db.org/api/json/network",
Expand All @@ -63,7 +73,7 @@ def test_network(httpx_mock):
"species": "7227",
"network_type": "functional",
"show_query_node_labels": 0,
"caller_identity": stringx.DEFAULT_CALLER_IDENTITY,
"caller_identity": test_client.identity,
},
),
method="POST",
Expand All @@ -80,44 +90,42 @@ def test_network(httpx_mock):
"network_type": "physical",
"add_nodes": 2,
"show_query_node_labels": 1,
"caller_identity": stringx.DEFAULT_CALLER_IDENTITY,
"caller_identity": test_client.identity,
},
),
method="POST",
json=True,
)

with stringx.Client() as client:
client.network(["id1"], 7227)
client.network(
identifiers=["id1", "id2"],
species=7227,
required_score=1,
network_type="physical",
add_nodes=2,
show_query_node_labels=True,
)
test_client.network(["id1"], 7227)
test_client.network(
identifiers=["id1", "id2"],
species=7227,
required_score=1,
network_type="physical",
add_nodes=2,
show_query_node_labels=True,
)


def test_interaction_partners(httpx_mock):
def test_interaction_partners(httpx_mock, test_client):
httpx_mock.add_response(
url=httpx.URL(
"https://string-db.org/api/json/interaction_partners",
params={
"identifiers": "id1\rid2",
"species": "7227",
"caller_identity": stringx.DEFAULT_CALLER_IDENTITY,
"caller_identity": test_client.identity,
},
),
method="POST",
json=True,
)

with stringx.Client() as client:
client.interaction_partners(["id1", "id2"], 7227)
test_client.interaction_partners(["id1", "id2"], 7227)


def test_version(httpx_mock):
def test_version(httpx_mock, test_client):
httpx_mock.add_response(
url=httpx.URL("https://string-db.org/api/json/version"),
method="GET",
Expand All @@ -128,5 +136,5 @@ def test_version(httpx_mock):
}
],
)
with stringx.Client() as client:
client.version()

test_client.version()
23 changes: 23 additions & 0 deletions tests/test_stringx.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
import pytest
import stringx

stringx.identity = "test client"


def test_invalid_identity():
stringx.identity = ""

with pytest.raises(ValueError):
stringx.network(["7227.FBpp0074940"], species=7227)


def test_valid_identity(httpx_mock):
httpx_mock.add_response(json={})

stringx.identity = "someone"

stringx.network(["7227.FBpp0074940"], species=7227)

assert (
httpx_mock.get_request().url.params["caller_identity"]
== f"{stringx.identity} (python-stringx/{stringx.__version__})"
)


def test_map():
identifiers = stringx.map(["edin"], 7227)
Expand Down

0 comments on commit 1d449f6

Please sign in to comment.