Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,15 @@ poetry install
poetry shell
```

### Pre-Commit Hooks
## Adding dependencies to the project
If your changes require you to install a python package/module using `poetry add <some package>` or
`poetry add <some package> -D` for a dev dependency. You will also need to run the following command to
regenerate a `requirements.txt` file that includes the newly added dependencies:
```bash
poetry export -f requirements.txt --output requirements.txt --without-hashes --dev
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


## Pre-Commit Hooks
We are using [Pre-Commit](https://pre-commit.com/) to enforce formatting, lint rules, and code analysis so that
this repo is always in good health.
- If you choose not to globally install `pre-commit`, then you can skip installing via `pip` or `homebrew` directly.
Expand Down
187 changes: 96 additions & 91 deletions poetry.lock

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ dataclasses-json = "^0.5.3"
fuuid = "^0.1.0"

[tool.poetry.dev-dependencies]
pytest = "^4.6"
pytest = ">=5.0"
pytest-cov = "^2.11.1"
pytest-mock = "^3.6.1"
black = "^20.8b1"
flake8 = "^3.8.4"
Sphinx = "^3.5.2"
tox = "^3.23.0"
coverage = "^5.5"
pytest-cov = "^2.11.1"
isort = "^5.8.0"
responses = "^0.13.3"
coveralls = "^3.1.0"
Expand Down
28 changes: 14 additions & 14 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ aiohttp==3.7.4.post0; python_version >= "3.6"
alabaster==0.7.12; python_version >= "3.5"
appdirs==1.4.4; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
async-timeout==3.0.1; python_full_version >= "3.5.3" and python_version >= "3.6"
atomicwrites==1.4.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0"
atomicwrites==1.4.0; python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6") or sys_platform == "win32" and python_version >= "3.6" and python_full_version >= "3.4.0" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6")
attrs==21.2.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
babel==2.9.1; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.5"
base58==2.1.0; python_version >= "3.7" and python_version < "4.0"
Expand All @@ -11,7 +11,7 @@ certifi==2021.5.30; python_version >= "3.5" and python_full_version < "3.0.0" or
cfgv==3.3.0; python_full_version >= "3.6.1"
chardet==4.0.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
click==8.0.1; python_version >= "3.6"
colorama==0.4.4; python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0") and platform_system == "Windows" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0") or sys_platform == "win32" and python_version >= "3.6" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0") and python_full_version >= "3.5.0" and platform_system == "Windows" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0")
colorama==0.4.4; python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" and platform_system == "Windows" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6") or sys_platform == "win32" and python_version >= "3.6" and python_full_version >= "3.5.0" and platform_system == "Windows" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6")
coverage==5.5; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0" and python_version < "4")
coveralls==3.1.0; python_version >= "3.5"
dataclasses-json==0.5.4; python_version >= "3.6"
Expand All @@ -24,32 +24,33 @@ fuuid==0.1.0; python_version >= "3.7" and python_version < "4.0"
identify==2.2.10; python_full_version >= "3.6.1"
idna==2.10; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
imagesize==1.2.0; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.5"
importlib-metadata==4.6.0; python_full_version >= "3.6.1" and python_version < "3.8" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.8" or python_full_version >= "3.4.0" and python_version < "3.8" and python_version >= "3.6") and (python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.8" or python_full_version >= "3.5.0" and python_version < "3.8" and python_version >= "3.6")
importlib-metadata==4.6.1; python_version < "3.8" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.8" or python_full_version >= "3.5.0" and python_version < "3.8" and python_version >= "3.6") and python_full_version >= "3.6.1" and (python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.8" or python_full_version >= "3.4.0" and python_version >= "3.6" and python_version < "3.8") and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6")
iniconfig==1.1.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
isort==5.9.1; python_full_version >= "3.6.1" and python_version < "4.0"
jinja2==3.0.1; python_version >= "3.6"
markupsafe==2.0.1; python_version >= "3.6"
marshmallow==3.12.1; python_version >= "3.6"
marshmallow-enum==1.5.1; python_version >= "3.6"
mccabe==0.6.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0"
more-itertools==8.8.0; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5"
multidict==5.1.0; python_version >= "3.6"
mypy-extensions==0.4.3; python_version >= "3.6"
nodeenv==1.6.0; python_full_version >= "3.6.1"
packaging==20.9; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5"
packaging==21.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
pathspec==0.8.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
pluggy==0.13.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0"
pluggy==0.13.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
pre-commit==2.13.0; python_full_version >= "3.6.1"
py==1.10.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0"
py==1.10.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
pycodestyle==2.7.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0"
pyflakes==2.3.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0"
pygments==2.9.0; python_version >= "3.5"
pyparsing==2.4.7; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0"
pytest==4.6.11; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0")
pyparsing==2.4.7; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6"
pytest==6.2.4; python_version >= "3.6"
pytest-cov==2.12.1; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0")
pytest-mock==3.6.1; python_version >= "3.6"
python-dotenv==0.15.0
pytz==2021.1; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.5"
pyyaml==5.4.1; python_full_version >= "3.6.1"
regex==2021.4.4; python_version >= "3.6"
regex==2021.7.6; python_version >= "3.6"
requests==2.25.1; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0")
responses==0.13.3; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0")
six==1.16.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0"
Expand All @@ -62,13 +63,12 @@ sphinxcontrib-jsmath==1.0.1; python_version >= "3.5"
sphinxcontrib-qthelp==1.0.3; python_version >= "3.5"
sphinxcontrib-serializinghtml==1.1.5; python_version >= "3.5"
stringcase==1.2.0; python_version >= "3.6"
toml==0.10.2; python_full_version >= "3.6.1" and python_version >= "3.6"
toml==0.10.2; python_full_version >= "3.6.1" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6")
tox==3.23.1; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0")
typed-ast==1.4.3; python_version >= "3.6"
typing-extensions==3.10.0.0; python_version < "3.8" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.8" or python_full_version >= "3.4.0" and python_version < "3.8" and python_version >= "3.6")
typing-extensions==3.10.0.0; python_version < "3.8" and python_version >= "3.6"
typing-inspect==0.7.1; python_version >= "3.6"
urllib3==1.26.6; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version < "4" and python_version >= "3.5"
virtualenv==20.4.7; python_full_version >= "3.6.1"
wcwidth==0.2.5; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0"
yarl==1.6.3; python_version >= "3.6"
zipp==3.4.1; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.8" or python_full_version >= "3.4.0" and python_version < "3.8" and python_version >= "3.6"
zipp==3.5.0; python_version < "3.8" and python_version >= "3.6"
23 changes: 16 additions & 7 deletions shipengine_sdk/events/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,13 @@ def __init__(


class Dispatcher:
def __init__(self, events: List[str]) -> None:
self.events = {event: dict() for event in events}
def __init__(self, events: Optional[List[str]] = None) -> None:
events_list = [Events.ON_REQUEST_SENT.value, Events.ON_RESPONSE_RECEIVED.value]
if events:
for i in events:
events_list.append(i)

self.events = {event: dict() for event in events_list}

def get_subscribers(self, event: Optional[str] = None):
return self.events[event]
Expand All @@ -103,6 +108,12 @@ def dispatch(self, event, event_name: str = None):
for subscriber, callback in self.get_subscribers(event_name).items():
callback(event)

def to_dict(self):
return (lambda o: o.__dict__)(self)

def to_json(self):
return json.dumps(self, default=lambda o: o.__dict__, indent=2)


class Subscriber:
def __init__(self, name=None) -> None:
Expand All @@ -129,13 +140,11 @@ def __init__(self, name=None) -> None:
# You can add your own event consumption logic by adding/overriding the parent `update()` method below.
@staticmethod
def update(event: Union[RequestSentEvent, ResponseReceivedEvent]):
print(event.to_dict())
# print(event.to_dict())
return event


def emit_event(emitted_event_type: str, event_data, config):
dispatcher = Dispatcher([Events.ON_REQUEST_SENT.value, Events.ON_RESPONSE_RECEIVED.value])
dispatcher.register(event=Events.ON_REQUEST_SENT.value, subscriber=config.event_listener)
dispatcher.register(event=Events.ON_RESPONSE_RECEIVED.value, subscriber=config.event_listener)
def emit_event(emitted_event_type: str, event_data, dispatcher: Dispatcher):
if emitted_event_type == RequestSentEvent.REQUEST_SENT:
request_sent_event = RequestSentEvent(
message=event_data.message,
Expand Down
16 changes: 12 additions & 4 deletions shipengine_sdk/http_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

from ..errors import ShipEngineError
from ..events import (
Dispatcher,
EventOptions,
RequestSentEvent,
ResponseReceivedEvent,
Expand All @@ -23,6 +24,7 @@
)
from ..jsonrpc.process_request import handle_response, wrap_request
from ..models import ErrorCode, ErrorSource, ErrorType
from ..models.enums import Events
from ..shipengine_config import ShipEngineConfig
from ..util.sdk_assertions import check_response_for_errors

Expand Down Expand Up @@ -61,10 +63,16 @@ def __call__(self, request: Request, *args, **kwargs) -> Request:


class ShipEngineClient:
_BASE_URI: str = ""
_DISPATCHER: Dispatcher = Dispatcher()

def __init__(self) -> None:
def __init__(self, config: ShipEngineConfig) -> None:
"""A `JSON-RPC 2.0` HTTP client used to send all HTTP requests from the SDK."""
self._DISPATCHER.register(
event=Events.ON_REQUEST_SENT.value, subscriber=config.event_listener
)
self._DISPATCHER.register(
event=Events.ON_RESPONSE_RECEIVED.value, subscriber=config.event_listener
)
self.session = requests.session()

def send_rpc_request(
Expand Down Expand Up @@ -104,7 +112,7 @@ def send_rpc_request(
request_sent_event = emit_event(
emitted_event_type=RequestSentEvent.REQUEST_SENT,
event_data=request_event_data,
config=config,
dispatcher=self._DISPATCHER,
)

try:
Expand Down Expand Up @@ -135,7 +143,7 @@ def send_rpc_request(
emit_event(
emitted_event_type=ResponseReceivedEvent.RESPONSE_RECEIVED,
event_data=response_event_data,
config=config,
dispatcher=self._DISPATCHER,
)

check_response_for_errors(status_code=status_code, response_body=resp_body, config=config)
Expand Down
4 changes: 2 additions & 2 deletions shipengine_sdk/jsonrpc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ def rpc_request(
def rpc_request_loop(
method: str, params: Optional[Dict[str, Any]], config: ShipEngineConfig
) -> Dict[str, Any]:
client: ShipEngineClient = ShipEngineClient()
api_response: Optional[Dict[str, Any]] = None
client: ShipEngineClient = ShipEngineClient(config=config)
retry: int = 0
while retry <= config.retries:
try:
Expand All @@ -36,6 +35,7 @@ def rpc_request_loop(
and err.retry_after < config.timeout
):
time.sleep(err.retry_after)
continue
else:
raise err
retry += 1
Expand Down
2 changes: 1 addition & 1 deletion shipengine_sdk/services/address_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def validate(address: Address, config: ShipEngineConfig) -> AddressValidateResul
api_response: Dict[str, Any] = rpc_request(
method=RPCMethods.ADDRESS_VALIDATE.value,
config=config,
params={"address": address.to_dict()}, # type: ignore
params={"address": address.to_dict()},
)
result: Dict[str, Any] = api_response["result"]
return AddressValidateResult(
Expand Down
1 change: 1 addition & 0 deletions tests/events/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Tests around events emitted from ShipEngine SDK."""
13 changes: 13 additions & 0 deletions tests/events/test_request_sent_event.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""Test that `RequestSentEvents` are emitted from the SDK properly."""
from pytest_mock import MockerFixture

# from shipengine_sdk.events import ShipEngineEventListener

# from ..util import stub_shipengine_config


class TestRequestSentEvent:
def test_config_with_retries_disabled(self, mocker: MockerFixture) -> None:
"""Tests that the SDK does not automatically retry if retries in config is set to 0."""
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this spec running?

# spy = mocker.spy(ShipEngineEventListener, "update")
# shipengine = stub_shipengine_config()