diff --git a/.github/res/badges/code-style.svg b/.github/res/badges/code-style.svg new file mode 100644 index 0000000..0f81b57 --- /dev/null +++ b/.github/res/badges/code-style.svg @@ -0,0 +1 @@ +Code StyleCode Styleblackblack \ No newline at end of file diff --git a/.github/res/badges/python-version.svg b/.github/res/badges/python-version.svg new file mode 100644 index 0000000..1e7d84c --- /dev/null +++ b/.github/res/badges/python-version.svg @@ -0,0 +1 @@ +pythonpython3.8-3.113.8-3.11 \ No newline at end of file diff --git a/.github/workflows/lint-test.yaml b/.github/workflows/lint-test.yaml index 9b27d5b..8d11c36 100644 --- a/.github/workflows/lint-test.yaml +++ b/.github/workflows/lint-test.yaml @@ -1,5 +1,5 @@ -name: lint-and-test -run-name: Lint and Test +name: ci-regressions +run-name: Run CI Regressions on: pull_request: branches: @@ -9,25 +9,36 @@ on: - main jobs: - lint: - name: Build Lint and Test Code + regressions: + name: CI Regression Tests runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - - name: Install Dependencies - run: | - sudo apt-get update - sudo apt-get upgrade -y - sudo apt-get install -y poetry - poetry install + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: '3.12' + cache: 'pip' - - name: Build Lint and Test - run: | - make ci-build-lint-test + - name: Grab Tox and Poetry + run: pip install tox poetry + + - name: Lint and Test + run: tox env: - MARKETSTACK_TOKEN_TEST: "${{ secrets.MarketstackFreeTestToken }}" + MARKETSTACK_TOKEN_TEST: "${{ secrets.MARKETSTACK_TOKEN_TEST }}" + + - name: Run a Build + run: poetry build + + - name: Create a build-artifacts tarball + run: | + tar -czf \ + build-artifacts.tar.gz \ + test-artifacts-* \ + dist - name: Upload Artifacts uses: actions/upload-artifact@v3 diff --git a/.gitignore b/.gitignore index 2cccbc3..ad8c18f 100644 --- a/.gitignore +++ b/.gitignore @@ -160,4 +160,5 @@ cython_debug/ #.idea/ # Build Artifact ignores -build-artifacts +build-artifacts*/ +build-artifacts.tar.gz diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..eba8aeb --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Marko Vejnovic + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile deleted file mode 100644 index 72e1c0a..0000000 --- a/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -PYTHON = poetry run python - -# Directories or files which can be linted -LINTABLE_NODES = aiomarketstack tests - -.PHONY: lint autolint test build - -lint: - $(PYTHON) -m mypy aiomarketstack tests - $(PYTHON) -m ruff check . - -autolint: - $(PYTHON) -m ruff check --fix . - -test: - $(PYTHON) -m pytest \ - --cov=aiomarketstack \ - --cov-report=html \ - --cov-report=term - -build: - mkdir -p build-artifacts - poetry build - rm -rf build-artifacts/dist - mv dist build-artifacts/dist - -ci-build-lint-test: build lint test - tar -czf build-artifacts.tar.gz build-artifacts diff --git a/README.md b/README.md index 2df646b..4283cde 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,16 @@ # aiomarketstack -`aiomarketstack` is a thin-and-light, Python-only `asyncio`-based -[marketsack](https://marketstack.com/) client. +![Python 3.8-3.11](.github/res/badges/python-version.svg) +![Code Style](.github/res/badges/code-style.svg) -## Getting Started +_`aiomarketstack` is an unofficial Python-only `asyncio`-based +[Marketstack](https://marketstack.com/) client._ + +This library aims to be extremely observable and, therefore, comes with +`structlog` (which I adore ❤️) support out of the box. I am also currently +working on `OpenTelemetry` metrics as well. + +## Installing To get started, grab yourself a copy of `aiomarketstack`: @@ -11,7 +18,132 @@ To get started, grab yourself a copy of `aiomarketstack`: pip install aiomarketstack ``` +## Getting Started + +The easiest way to use client is via the `HttpMarketstackClient`. The following +example illustrates the most powerful utilities in `aiomarketstack`. + +```python +from __future__ import annotations + +import asyncio + +from datetime import date +import matplotlib.pyplot as plt + +from aiomarketstack import HttpMarketstackClient, MarketstackPlan +from aiomarketstack.exceptions import ResponseError +from aiomarketstack.types import Eod + + +async def main(): + date_range = (date(2023, 1, 1), date(2023, 1, 31)) + + async with HttpMarketstackClient( + "your-token-here", + MarketstackPlan.FREE + ) as client: + try: + eod_values: tuple[Eod, ...] = ( + await client.get_eod_range(("AMZN", ), date_range) + ) + except ResponseError as resp_err: + print(f"Uh-oh, a response error ocurred: {resp_err}") + raise + + # Note that there will be missing data (if the market was closed). + plt_xaxis = [eod["date"] for eod in eod_values] + plt_yaxes = { + key: [eod[key] for eod in eod_values] + for key in {"open", "high", "low", "close"} + } + + for label, data in plt_yaxes.items(): + plt.plot(plt_xaxis, data, label=label.capitalize()) + plt.legend() + plt.grid() + plt.title("End-of-day prices for AMZN for January 2023") + plt.xlabel("Date in January") + plt.ylabel("Price in $") + plt.show() + + +asyncio.run(main()) +``` + +Run this example to get a pretty output: + +![Matplotlib Example Output](docs/res/example_matplotlib_plot.png) + +## Documentation + +In progress! + +### Why not [marketstack-python](https://github.com/mreiche/marketstack-python)? + +The main reason this library exists is because I found the great +[marketstack-python](https://github.com/mreiche/marketstack-python) to be +unwieldy for my needs. + +The three main advantages of `aiomarketstack` are: + +* OpenTelemetry Metrics (Currently In Progress) +* [structlog](https://www.structlog.org/en/stable/)-style Logging. +* Much more flexible API + +The main disadvantage of this library, compared with +[marketstack-python](https://github.com/mreiche/marketstack-python) is that it +does not and **will not** cache your queries. This is not and will not be the +goal of this library. + +## Development + +To manually build `aiomarketstack` for development, we use `poetry`. Get +started with: + +```bash +git clone https://github.com/markovejnovic/aiomarketstack.git +cd aiomarketstack +poetry install && poetry build +``` + +### Unit Tests + +You can test your code against test suite via `tox`. + +```bash +MARKETSTACK_TOKEN_TEST= tox +``` + +> [!CAUTION] +> This will consume about 30 API requests. + +## Contributing +In progress! Feel free to open a issue if you spot something! +## License -## Dependencies +``` +MIT License + +Copyright (c) 2023 Marko Vejnovic + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` diff --git a/aiomarketstack/__init__.py b/aiomarketstack/__init__.py index a1baf15..58227be 100644 --- a/aiomarketstack/__init__.py +++ b/aiomarketstack/__init__.py @@ -21,7 +21,7 @@ from __future__ import annotations import warnings -from datetime import date, datetime +from datetime import date, datetime, timezone from enum import IntEnum from typing import ( TYPE_CHECKING, @@ -29,13 +29,15 @@ Callable, Collection, Literal, - Self, ) import aiohttp import structlog from aiohttp import ClientResponse, ClientSession +if TYPE_CHECKING: + from typing_extensions import Self + from aiomarketstack.exceptions import ( ForbiddenError, FunctionAccessRestrictedError, @@ -306,9 +308,16 @@ def _deserialize_eod(raw_eod: RawEod) -> Eod: "dividend": raw_eod["dividend"], "symbol": raw_eod["symbol"], "exchange": raw_eod["exchange"], - "date": datetime.fromisoformat(raw_eod["date"]).date(), + "date": _MarketstackClient._parse_marketstack_date(raw_eod["date"]), } + @staticmethod + def _parse_marketstack_date(date_str: str) -> date: + # Necessary because python < 3.11's fromisoformat didn't support IS8601. + marketstack_date_format = "%Y-%m-%dT%H:%M:%S%z" + return datetime.strptime(date_str, marketstack_date_format) \ + .replace(tzinfo=timezone.utc).date() + class HttpMarketstackClient(_MarketstackClient): """The main marketstack client. diff --git a/aiomarketstack/exceptions.py b/aiomarketstack/exceptions.py index 77fadde..fa53ab2 100644 --- a/aiomarketstack/exceptions.py +++ b/aiomarketstack/exceptions.py @@ -1,8 +1,8 @@ """Marketstack Error Response Errors.""" -from typing import Self from aiohttp.client import ClientResponse +from typing_extensions import Self class ResponseError(Exception): diff --git a/aiomarketstack/types.py b/aiomarketstack/types.py index 0240681..c8157ff 100644 --- a/aiomarketstack/types.py +++ b/aiomarketstack/types.py @@ -2,11 +2,13 @@ from __future__ import annotations -from typing import TYPE_CHECKING, NotRequired, Required, Sequence, TypedDict +from typing import TYPE_CHECKING, Sequence, TypedDict if TYPE_CHECKING: from datetime import date + from typing_extensions import NotRequired, Required + class Eod(TypedDict): """Represents an end-of-day marketstack return type.""" diff --git a/docs/examples/matplotlib_plot.py b/docs/examples/matplotlib_plot.py new file mode 100644 index 0000000..9ac0e80 --- /dev/null +++ b/docs/examples/matplotlib_plot.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +import asyncio + +from datetime import date +import matplotlib.pyplot as plt + +from aiomarketstack import HttpMarketstackClient, MarketstackPlan +from aiomarketstack.exceptions import ResponseError +from aiomarketstack.types import Eod + + +async def main(): + date_range = (date(2023, 1, 1), date(2023, 1, 31)) + + async with HttpMarketstackClient( + "your-token-here", + MarketstackPlan.FREE + ) as client: + try: + eod_values: tuple[Eod, ...] = ( + await client.get_eod_range(("AMZN", ), date_range) + ) + except ResponseError as resp_err: + print(f"Uh-oh, a response error ocurred: {resp_err}") + raise + + # Note that there will be missing data (if the market was closed). + plt_xaxis = [eod["date"] for eod in eod_values] + plt_yaxes = { + key: [eod[key] for eod in eod_values] + for key in {"open", "high", "low", "close"} + } + + for label, data in plt_yaxes.items(): + plt.plot(plt_xaxis, data, label=label) + plt.legend() + plt.grid() + plt.title("End-of-day prices for AMZN for January 2023") + plt.xlabel("Date in January") + plt.ylabel("Price in $") + plt.show() + + +asyncio.run(main()) diff --git a/docs/res/example_matplotlib_plot.png b/docs/res/example_matplotlib_plot.png new file mode 100644 index 0000000..61071b5 Binary files /dev/null and b/docs/res/example_matplotlib_plot.png differ diff --git a/matplotlib_plot.py b/matplotlib_plot.py new file mode 100644 index 0000000..e3b1049 --- /dev/null +++ b/matplotlib_plot.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +import asyncio + +from datetime import date +import matplotlib.pyplot as plt + +from aiomarketstack import HttpMarketstackClient, MarketstackPlan +from aiomarketstack.exceptions import ResponseError +from aiomarketstack.types import Eod + + +async def main(): + date_range = (date(2023, 1, 1), date(2023, 1, 31)) + + async with HttpMarketstackClient( + "3dd3d00e96ae31eddb921276a1650ba9", + MarketstackPlan.FREE + ) as client: + try: + eod_values: tuple[Eod, ...] = ( + await client.get_eod_range(("AMZN", ), date_range) + ) + except ResponseError as resp_err: + print(f"Uh-oh, a response error ocurred: {resp_err}") + raise + + # Note that there will be missing data (if the market was closed). + plt_xaxis = [eod["date"] for eod in eod_values] + plt_yaxes = { + key: [eod[key] for eod in eod_values] + for key in {"open", "high", "low", "close"} + } + + for label, data in plt_yaxes.items(): + plt.plot(plt_xaxis, data, label=label) + plt.legend() + plt.grid() + plt.title("End-of-day prices for AMZN for January 2023") + plt.xlabel("Date in January") + plt.ylabel("Price in $") + plt.show() + + +asyncio.run(main()) diff --git a/poetry.lock b/poetry.lock index 5aee000..cc8ce4d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -466,47 +466,39 @@ files = [ [[package]] name = "numpy" -version = "1.26.2" +version = "1.24.4" description = "Fundamental package for array computing in Python" optional = false -python-versions = ">=3.9" +python-versions = ">=3.8" files = [ - {file = "numpy-1.26.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3703fc9258a4a122d17043e57b35e5ef1c5a5837c3db8be396c82e04c1cf9b0f"}, - {file = "numpy-1.26.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cc392fdcbd21d4be6ae1bb4475a03ce3b025cd49a9be5345d76d7585aea69440"}, - {file = "numpy-1.26.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36340109af8da8805d8851ef1d74761b3b88e81a9bd80b290bbfed61bd2b4f75"}, - {file = "numpy-1.26.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcc008217145b3d77abd3e4d5ef586e3bdfba8fe17940769f8aa09b99e856c00"}, - {file = "numpy-1.26.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3ced40d4e9e18242f70dd02d739e44698df3dcb010d31f495ff00a31ef6014fe"}, - {file = "numpy-1.26.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b272d4cecc32c9e19911891446b72e986157e6a1809b7b56518b4f3755267523"}, - {file = "numpy-1.26.2-cp310-cp310-win32.whl", hash = "sha256:22f8fc02fdbc829e7a8c578dd8d2e15a9074b630d4da29cda483337e300e3ee9"}, - {file = "numpy-1.26.2-cp310-cp310-win_amd64.whl", hash = "sha256:26c9d33f8e8b846d5a65dd068c14e04018d05533b348d9eaeef6c1bd787f9919"}, - {file = "numpy-1.26.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b96e7b9c624ef3ae2ae0e04fa9b460f6b9f17ad8b4bec6d7756510f1f6c0c841"}, - {file = "numpy-1.26.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aa18428111fb9a591d7a9cc1b48150097ba6a7e8299fb56bdf574df650e7d1f1"}, - {file = "numpy-1.26.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06fa1ed84aa60ea6ef9f91ba57b5ed963c3729534e6e54055fc151fad0423f0a"}, - {file = "numpy-1.26.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96ca5482c3dbdd051bcd1fce8034603d6ebfc125a7bd59f55b40d8f5d246832b"}, - {file = "numpy-1.26.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:854ab91a2906ef29dc3925a064fcd365c7b4da743f84b123002f6139bcb3f8a7"}, - {file = "numpy-1.26.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f43740ab089277d403aa07567be138fc2a89d4d9892d113b76153e0e412409f8"}, - {file = "numpy-1.26.2-cp311-cp311-win32.whl", hash = "sha256:a2bbc29fcb1771cd7b7425f98b05307776a6baf43035d3b80c4b0f29e9545186"}, - {file = "numpy-1.26.2-cp311-cp311-win_amd64.whl", hash = "sha256:2b3fca8a5b00184828d12b073af4d0fc5fdd94b1632c2477526f6bd7842d700d"}, - {file = "numpy-1.26.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a4cd6ed4a339c21f1d1b0fdf13426cb3b284555c27ac2f156dfdaaa7e16bfab0"}, - {file = "numpy-1.26.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5d5244aabd6ed7f312268b9247be47343a654ebea52a60f002dc70c769048e75"}, - {file = "numpy-1.26.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a3cdb4d9c70e6b8c0814239ead47da00934666f668426fc6e94cce869e13fd7"}, - {file = "numpy-1.26.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa317b2325f7aa0a9471663e6093c210cb2ae9c0ad824732b307d2c51983d5b6"}, - {file = "numpy-1.26.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:174a8880739c16c925799c018f3f55b8130c1f7c8e75ab0a6fa9d41cab092fd6"}, - {file = "numpy-1.26.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f79b231bf5c16b1f39c7f4875e1ded36abee1591e98742b05d8a0fb55d8a3eec"}, - {file = "numpy-1.26.2-cp312-cp312-win32.whl", hash = "sha256:4a06263321dfd3598cacb252f51e521a8cb4b6df471bb12a7ee5cbab20ea9167"}, - {file = "numpy-1.26.2-cp312-cp312-win_amd64.whl", hash = "sha256:b04f5dc6b3efdaab541f7857351aac359e6ae3c126e2edb376929bd3b7f92d7e"}, - {file = "numpy-1.26.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4eb8df4bf8d3d90d091e0146f6c28492b0be84da3e409ebef54349f71ed271ef"}, - {file = "numpy-1.26.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1a13860fdcd95de7cf58bd6f8bc5a5ef81c0b0625eb2c9a783948847abbef2c2"}, - {file = "numpy-1.26.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64308ebc366a8ed63fd0bf426b6a9468060962f1a4339ab1074c228fa6ade8e3"}, - {file = "numpy-1.26.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baf8aab04a2c0e859da118f0b38617e5ee65d75b83795055fb66c0d5e9e9b818"}, - {file = "numpy-1.26.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d73a3abcac238250091b11caef9ad12413dab01669511779bc9b29261dd50210"}, - {file = "numpy-1.26.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b361d369fc7e5e1714cf827b731ca32bff8d411212fccd29ad98ad622449cc36"}, - {file = "numpy-1.26.2-cp39-cp39-win32.whl", hash = "sha256:bd3f0091e845164a20bd5a326860c840fe2af79fa12e0469a12768a3ec578d80"}, - {file = "numpy-1.26.2-cp39-cp39-win_amd64.whl", hash = "sha256:2beef57fb031dcc0dc8fa4fe297a742027b954949cabb52a2a376c144e5e6060"}, - {file = "numpy-1.26.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1cc3d5029a30fb5f06704ad6b23b35e11309491c999838c31f124fee32107c79"}, - {file = "numpy-1.26.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94cc3c222bb9fb5a12e334d0479b97bb2df446fbe622b470928f5284ffca3f8d"}, - {file = "numpy-1.26.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fe6b44fb8fcdf7eda4ef4461b97b3f63c466b27ab151bec2366db8b197387841"}, - {file = "numpy-1.26.2.tar.gz", hash = "sha256:f65738447676ab5777f11e6bbbdb8ce11b785e105f690bc45966574816b6d3ea"}, + {file = "numpy-1.24.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0bfb52d2169d58c1cdb8cc1f16989101639b34c7d3ce60ed70b19c63eba0b64"}, + {file = "numpy-1.24.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ed094d4f0c177b1b8e7aa9cba7d6ceed51c0e569a5318ac0ca9a090680a6a1b1"}, + {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79fc682a374c4a8ed08b331bef9c5f582585d1048fa6d80bc6c35bc384eee9b4"}, + {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ffe43c74893dbf38c2b0a1f5428760a1a9c98285553c89e12d70a96a7f3a4d6"}, + {file = "numpy-1.24.4-cp310-cp310-win32.whl", hash = "sha256:4c21decb6ea94057331e111a5bed9a79d335658c27ce2adb580fb4d54f2ad9bc"}, + {file = "numpy-1.24.4-cp310-cp310-win_amd64.whl", hash = "sha256:b4bea75e47d9586d31e892a7401f76e909712a0fd510f58f5337bea9572c571e"}, + {file = "numpy-1.24.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f136bab9c2cfd8da131132c2cf6cc27331dd6fae65f95f69dcd4ae3c3639c810"}, + {file = "numpy-1.24.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2926dac25b313635e4d6cf4dc4e51c8c0ebfed60b801c799ffc4c32bf3d1254"}, + {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:222e40d0e2548690405b0b3c7b21d1169117391c2e82c378467ef9ab4c8f0da7"}, + {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7215847ce88a85ce39baf9e89070cb860c98fdddacbaa6c0da3ffb31b3350bd5"}, + {file = "numpy-1.24.4-cp311-cp311-win32.whl", hash = "sha256:4979217d7de511a8d57f4b4b5b2b965f707768440c17cb70fbf254c4b225238d"}, + {file = "numpy-1.24.4-cp311-cp311-win_amd64.whl", hash = "sha256:b7b1fc9864d7d39e28f41d089bfd6353cb5f27ecd9905348c24187a768c79694"}, + {file = "numpy-1.24.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1452241c290f3e2a312c137a9999cdbf63f78864d63c79039bda65ee86943f61"}, + {file = "numpy-1.24.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:04640dab83f7c6c85abf9cd729c5b65f1ebd0ccf9de90b270cd61935eef0197f"}, + {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5425b114831d1e77e4b5d812b69d11d962e104095a5b9c3b641a218abcc050e"}, + {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd80e219fd4c71fc3699fc1dadac5dcf4fd882bfc6f7ec53d30fa197b8ee22dc"}, + {file = "numpy-1.24.4-cp38-cp38-win32.whl", hash = "sha256:4602244f345453db537be5314d3983dbf5834a9701b7723ec28923e2889e0bb2"}, + {file = "numpy-1.24.4-cp38-cp38-win_amd64.whl", hash = "sha256:692f2e0f55794943c5bfff12b3f56f99af76f902fc47487bdfe97856de51a706"}, + {file = "numpy-1.24.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2541312fbf09977f3b3ad449c4e5f4bb55d0dbf79226d7724211acc905049400"}, + {file = "numpy-1.24.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9667575fb6d13c95f1b36aca12c5ee3356bf001b714fc354eb5465ce1609e62f"}, + {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3a86ed21e4f87050382c7bc96571755193c4c1392490744ac73d660e8f564a9"}, + {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d11efb4dbecbdf22508d55e48d9c8384db795e1b7b51ea735289ff96613ff74d"}, + {file = "numpy-1.24.4-cp39-cp39-win32.whl", hash = "sha256:6620c0acd41dbcb368610bb2f4d83145674040025e5536954782467100aa8835"}, + {file = "numpy-1.24.4-cp39-cp39-win_amd64.whl", hash = "sha256:befe2bf740fd8373cf56149a5c23a0f601e82869598d41f8e188a0e9869926f8"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:31f13e25b4e304632a4619d0e0777662c2ffea99fcae2029556b17d8ff958aef"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95f7ac6540e95bc440ad77f56e520da5bf877f87dca58bd095288dce8940532a"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e98f220aa76ca2a977fe435f5b04d7b3470c0a2e6312907b37ba6068f26787f2"}, + {file = "numpy-1.24.4.tar.gz", hash = "sha256:80f5e3a4e498641401868df4208b74581206afbee7cf7b8329daae82676d9463"}, ] [[package]] @@ -763,5 +755,5 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" -python-versions = "^3.9" -content-hash = "4c8c4e960bb38cd71626bccdc1807f7a5d367bb82a80a3bf0985b273265fdecf" +python-versions = "^3.8" +content-hash = "f5701842e4394c82e7fc96fb993b29a3a4d6c65ff378520e5fd328bc3278fdae" diff --git a/pyproject.toml b/pyproject.toml index 780cc40..d7de9bb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,17 +7,18 @@ readme = "README.md" packages = [{ include = "aiomarketstack" }] [tool.poetry.dependencies] -aiohttp = "^3.7.3" -python = "^3.9" -structlog = "^23.1.0" +aiohttp = {version = "^3.9.1", python = "^3.8"} +python = "^3.8" +structlog = {version = "^23.2.0", python = "^3.8"} +typing-extensions = {version = "^4.8.0", python = "^3.8"} [tool.poetry.group.test.dependencies] -mypy = "^1.7.1" -numpy = "^1.26.2" -pytest = "^7.4.3" -pytest-asyncio = "^0.23.1" -pytest-cov = "^4.1.0" -ruff = "^0.1.6" +mypy = {version = "^1.7.1", python = "^3.8"} +numpy = {version = "^1.24", python = "^3.8"} +pytest = {version = "^7.4.3", python = "^3.8"} +pytest-asyncio = {version = "^0.23.1", python = "^3.8"} +pytest-cov = {version = "^4.1.0", python = "^3.8"} +ruff = {version = "^0.1.6", python = "^3.8"} [build-system] requires = ["poetry-core"] diff --git a/tests/__init__.py b/tests/__init__.py index afed29a..a873eba 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,9 +1,7 @@ """aiomarketstack's testing package.""" -import unittest - from . import test_aiomarketstack -ALL_TESTS = [ - unittest.defaultTestLoader.loadTestsFromModule(test_aiomarketstack), +__all__ = [ + "test_aiomarketstack", ] diff --git a/tests/__main__.py b/tests/__main__.py deleted file mode 100644 index 2682385..0000000 --- a/tests/__main__.py +++ /dev/null @@ -1,6 +0,0 @@ -"""Run the aiomarketstack test suite.""" -import unittest - -from . import ALL_TESTS - -unittest.TextTestRunner().run(*ALL_TESTS) diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..e98bdd8 --- /dev/null +++ b/tox.ini @@ -0,0 +1,21 @@ +[tox] +isolated_build = True +envlist = py38,py39,py310,py311 + +[testenv] +deps = + mypy + numpy + pytest + pytest-asyncio + pytest-cov + ruff +commands = + pytest \ + --cov=aiomarketstack \ + --cov-report=html:test-artifacts-{envname} \ + --cov-report=term tests/ + mypy aiomarketstack tests + ruff check . +passenv = + MARKETSTACK_TOKEN_TEST