diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml
index d830f7187..6596a5315 100644
--- a/.github/workflows/python-ci.yml
+++ b/.github/workflows/python-ci.yml
@@ -23,7 +23,7 @@ jobs:
if: "!contains(github.event.head_commit.message, 'skipci')"
runs-on: ${{ matrix.os }}
env:
- USING_COVERAGE: "3.9"
+ USING_COVERAGE: "3.10"
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
@@ -34,9 +34,9 @@ jobs:
uses: actions/checkout@v3
- name: Setup Python
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v5
with:
- python-version: 3.9
+ python-version: "3.10"
- name: Install Poetry
uses: snok/install-poetry@v1.3.2
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index eb6b95e38..fec48fbdb 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -15,7 +15,7 @@ jobs:
if: "github.event.pull_request.merged == true"
runs-on: ${{ matrix.os }}
env:
- USING_COVERAGE: "3.9"
+ USING_COVERAGE: "3.10"
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
@@ -26,9 +26,9 @@ jobs:
uses: actions/checkout@v3
- name: Setup Python
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v5
with:
- python-version: 3.9
+ python-version: "3.10"
- name: Install Poetry
uses: snok/install-poetry@v1.3.2
diff --git a/.github/workflows/version-compatibility.yml b/.github/workflows/version-compatibility.yml
index d2ce366a5..19c094116 100644
--- a/.github/workflows/version-compatibility.yml
+++ b/.github/workflows/version-compatibility.yml
@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- - uses: actions/setup-python@v2
+ - uses: actions/setup-python@v5
- name: Install Poetry
uses: snok/install-poetry@v1
- name: Check version compatibility has been tested
diff --git a/README.md b/README.md
index 13067d622..7532c5619 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,7 @@
[](https://octue-python-sdk.readthedocs.io/en/latest/?badge=latest)
[](https://github.com/pre-commit/pre-commit)
[](https://github.com/ambv/black)
+[](https://doi.org/10.5281/zenodo.10961975)
# Octue Python SDK
@@ -15,7 +16,9 @@ Read the docs [here.](https://octue-python-sdk.readthedocs.io/en/latest/)
Uses our [twined](https://twined.readthedocs.io/en/latest/) library for data validation.
## Installation and usage
+
To install, run one of:
+
```shell
pip install octue
```
@@ -25,6 +28,7 @@ poetry add octue
```
The command line interface (CLI) can then be accessed via:
+
```shell
octue --help
```
@@ -59,6 +63,7 @@ Commands:
```
## Deprecated code
+
When code is deprecated, it will still work but a deprecation warning will be issued with a suggestion on how to update
it. After an adjustment period, deprecations will be removed from the codebase according to the [code removal schedule](https://github.com/octue/octue-sdk-python/issues/415).
This constitutes a breaking change.
@@ -66,6 +71,7 @@ This constitutes a breaking change.
## Developer notes
### Installation
+
We use [Poetry](https://python-poetry.org/) as our package manager. For development, run the following from the
repository root, which will editably install the package:
@@ -76,18 +82,24 @@ poetry install --all-extras
Then run the tests to check everything's working.
### Testing
+
These environment variables need to be set to run the tests:
-* `GOOGLE_APPLICATION_CREDENTIALS=/absolute/path/to/service/account/file.json`
-* `TEST_PROJECT_NAME=`
+
+- `GOOGLE_APPLICATION_CREDENTIALS=/absolute/path/to/service/account/file.json`
+- `TEST_PROJECT_NAME=`
Then, from the repository root, run
+
```shell
python3 -m unittest
```
+
or
+
```shell
tox
```
## Contributing
+
Take a look at our [contributing](/docs/contributing.md) page.
diff --git a/docs/source/asking_questions.rst b/docs/source/asking_questions.rst
index acc4b6448..36db95bc8 100644
--- a/docs/source/asking_questions.rst
+++ b/docs/source/asking_questions.rst
@@ -35,7 +35,7 @@ Asking a question
backend={"name": "GCPPubSubBackend", "project_name": "my-project"},
)
- answer = child.ask(
+ answer, question_uuid = child.ask(
input_values={"height": 32, "width": 3},
input_manifest=manifest,
)
@@ -104,7 +104,6 @@ access the event store and run:
**Options**
- ``kind`` - Only retrieve this kind of event if present (e.g. "result")
-- ``include_attributes`` - If ``True``, retrieve all the events' attributes as well
- ``include_backend_metadata`` - If ``True``, retrieve information about the service backend that produced the event
- ``limit`` - If set to a positive integer, limit the number of events returned to this
@@ -232,7 +231,7 @@ this:
.. code-block:: python
- answer = analysis.children["elevation"].ask(input_values={"longitude": 0, "latitude": 1})
+ answer, question_uuid = analysis.children["elevation"].ask(input_values={"longitude": 0, "latitude": 1})
if your app configuration file is:
@@ -323,7 +322,7 @@ then you can override them like this:
.. code-block:: python
- answer = child.ask(
+ answer, question_uuid = child.ask(
input_values={"height": 32, "width": 3},
children=[
{
diff --git a/docs/source/manifest.rst b/docs/source/manifest.rst
index 8d00af312..7a13fc808 100644
--- a/docs/source/manifest.rst
+++ b/docs/source/manifest.rst
@@ -49,7 +49,7 @@ Get an Octue service to analyse data for you as part of a larger analysis.
backend={"name": "GCPPubSubBackend", "project_name": "my-project"},
)
- answer = child.ask(input_manifest=manifest)
+ answer, question_uuid = child.ask(input_manifest=manifest)
See :doc:`here ` for more information.
@@ -108,7 +108,7 @@ the cloud and then download them again for each service (as would happen with cl
}
)
- analysis.children["wind_speed"].ask(
+ answer, question_uuid = analysis.children["wind_speed"].ask(
input_values=analysis.input_values,
input_manifest=analysis.input_manifest,
allow_local_files=True,
diff --git a/docs/source/testing_services.rst b/docs/source/testing_services.rst
index df2419124..cf3612922 100644
--- a/docs/source/testing_services.rst
+++ b/docs/source/testing_services.rst
@@ -87,7 +87,7 @@ Instantiating a child emulator in python
def handle_monitor_message(message):
...
- result = child_emulator.ask(
+ result, question_uuid = child_emulator.ask(
input_values={"hello": "world"},
handle_monitor_message=handle_monitor_message,
)
@@ -133,7 +133,7 @@ You can then instantiate a child emulator from this in python:
def handle_monitor_message(message):
...
- result = child_emulator.ask(
+ result, question_uuid = child_emulator.ask(
input_values={"hello": "world"},
handle_monitor_message=handle_monitor_message,
)
@@ -226,7 +226,7 @@ child.
backend={"name": "GCPPubSubBackend", "project_name": "my-project"},
)
- result = child.ask(input_values=[1, 2, 3, 4])
+ result, question_uuid = child.ask(input_values=[1, 2, 3, 4])
child.received_events
>>> [
@@ -260,6 +260,6 @@ You can then feed these into a child emulator to emulate one possible response o
child_emulator = ChildEmulator(events=child.received_events)
child_emulator.ask(input_values=[1, 2, 3, 4])
- >>> {"some": "results"}
+ >>> {"some": "results"}, "9cab579f-c486-4324-ac9b-96491d26266b"
You can also create test fixtures from :ref:`downloaded service crash diagnostics `.
diff --git a/docs/source/troubleshooting_services.rst b/docs/source/troubleshooting_services.rst
index e861a572d..f60ca5fde 100644
--- a/docs/source/troubleshooting_services.rst
+++ b/docs/source/troubleshooting_services.rst
@@ -121,7 +121,7 @@ For example:
backend={"name": "GCPPubSubBackend", "project_name": "my-project"},
)
- answer = child.ask(
+ answer, question_uuid = child.ask(
input_values={"height": 32, "width": 3},
save_diagnostics="SAVE_DIAGNOSTICS_OFF",
)
diff --git a/octue/cloud/deployment/google/cloud_run/Dockerfile-python311 b/octue/cloud/deployment/google/cloud_run/Dockerfile-python311
index 2a34ef365..48203db1c 100644
--- a/octue/cloud/deployment/google/cloud_run/Dockerfile-python311
+++ b/octue/cloud/deployment/google/cloud_run/Dockerfile-python311
@@ -1,4 +1,4 @@
-FROM windpioneers/gdal-python:little-gecko-gdal-2.4.1-python-3.11-slim
+FROM windpioneers/gdal-python:modest-heron-gdal-2.4.1-python-3.11-slim
# Ensure print statements and log messages appear promptly in Cloud Logging.
ENV PYTHONUNBUFFERED True
diff --git a/octue/cloud/emulators/child.py b/octue/cloud/emulators/child.py
index 7614c5fd8..7f210a1d6 100644
--- a/octue/cloud/emulators/child.py
+++ b/octue/cloud/emulators/child.py
@@ -125,12 +125,12 @@ def ask(
:param bool asynchronous: if `True`, don't create an answer subscription
:param float timeout: time in seconds to wait for an answer before raising a timeout error
:raise TimeoutError: if the timeout is exceeded while waiting for an answer
- :return dict: a dictionary containing the keys "output_values" and "output_manifest"
+ :return dict, str: a dictionary containing the keys "output_values" and "output_manifest", and the question UUID
"""
with ServicePatcher():
self._child.serve(allow_existing=True)
- subscription, _ = self._parent.ask(
+ subscription, question_uuid = self._parent.ask(
service_id=self._child.id,
input_values=input_values,
input_manifest=input_manifest,
@@ -141,13 +141,15 @@ def ask(
asynchronous=asynchronous,
)
- return self._parent.wait_for_answer(
+ answer = self._parent.wait_for_answer(
subscription,
handle_monitor_message=handle_monitor_message,
record_events=record_events,
timeout=timeout,
)
+ return answer, question_uuid
+
def _emulate_analysis(
self,
analysis_id,
diff --git a/octue/cloud/pub_sub/bigquery.py b/octue/cloud/pub_sub/bigquery.py
index d02833c07..70b278b4d 100644
--- a/octue/cloud/pub_sub/bigquery.py
+++ b/octue/cloud/pub_sub/bigquery.py
@@ -3,53 +3,46 @@
from google.cloud.bigquery import Client, QueryJobConfig, ScalarQueryParameter
from octue.cloud.events.validation import VALID_EVENT_KINDS
+from octue.exceptions import ServiceNotFound
+from octue.resources import Manifest
-def get_events(
- table_id,
- sender,
- question_uuid,
- kind=None,
- include_attributes=False,
- include_backend_metadata=False,
- limit=1000,
-):
+def get_events(table_id, sender, question_uuid, kind=None, include_backend_metadata=False, limit=1000):
"""Get Octue service events for a question from a sender from a Google BigQuery event store.
:param str table_id: the full ID of the table e.g. "your-project.your-dataset.your-table"
:param str sender: the SRUID of the sender of the events
:param str question_uuid: the UUID of the question to get the events for
:param str|None kind: the kind of event to get; if `None`, all event kinds are returned
- :param bool include_attributes: if `True`, include events' attributes (excluding question UUID)
:param bool include_backend_metadata: if `True`, include the service backend metadata
:param int limit: the maximum number of events to return
+ :raise ValueError: if the `kind` parameter is invalid
+ :raise octue.exceptions.ServiceNotFound: if the sender hasn't emitted any events related to the question UUID (or any events at all)
:return list(dict): the events for the question
"""
if kind:
if kind not in VALID_EVENT_KINDS:
raise ValueError(f"`kind` must be one of {VALID_EVENT_KINDS!r}; received {kind!r}.")
- event_kind_condition = [f'AND JSON_EXTRACT_SCALAR(event, "$.kind") = "{kind}"']
+ event_kind_condition = [f"AND kind={kind!r}"]
else:
event_kind_condition = []
client = Client()
- fields = ["`event`"]
-
- if include_attributes:
- fields.extend(
- (
- "`datetime`",
- "`uuid`",
- "`originator`",
- "`sender`",
- "`sender_type`",
- "`sender_sdk_version`",
- "`recipient`",
- "`order`",
- "`other_attributes`",
- )
- )
+
+ fields = [
+ "`event`",
+ "`kind`",
+ "`datetime`",
+ "`uuid`",
+ "`originator`",
+ "`sender`",
+ "`sender_type`",
+ "`sender_sdk_version`",
+ "`recipient`",
+ "`order`",
+ "`other_attributes`",
+ ]
if include_backend_metadata:
fields.extend(("`backend`", "`backend_metadata`"))
@@ -74,16 +67,64 @@ def get_events(
)
query_job = client.query(query, job_config=job_config)
- rows = query_job.result()
- df = rows.to_dataframe()
+ result = query_job.result()
+
+ if result.total_rows == 0:
+ raise ServiceNotFound(
+ f"No events found. The requested sender {sender!r} may not exist or it hasn't emitted any events for "
+ f"question {question_uuid!r} (or any events at all)."
+ )
+
+ df = result.to_dataframe()
# Convert JSON strings to python primitives.
df["event"] = df["event"].map(json.loads)
-
- if "other_attributes" in df:
- df["other_attributes"] = df["other_attributes"].map(json.loads)
+ df["event"].apply(_deserialise_manifest_if_present)
+ df["other_attributes"] = df["other_attributes"].map(json.loads)
if "backend_metadata" in df:
df["backend_metadata"] = df["backend_metadata"].map(json.loads)
- return df.to_dict(orient="records")
+ events = df.to_dict(orient="records")
+ return _unflatten_events(events)
+
+
+def _deserialise_manifest_if_present(event):
+ """If the event is a "question" or "result" event and a manifest is present, deserialise the manifest and replace
+ the serialised manifest with it.
+
+ :param dict event: an Octue service event
+ :return None:
+ """
+ manifest_keys = {"input_manifest", "output_manifest"}
+
+ for key in manifest_keys:
+ if key in event:
+ event[key] = Manifest.deserialise(event[key])
+ # Only one of the manifest types will be in the event, so return if one is found.
+ return
+
+
+def _unflatten_events(events):
+ """Convert the events and attributes from the flat structure of the BigQuery table into the nested structure of the
+ service communication schema.
+
+ :param list(dict) events: flattened events
+ :return list(dict): unflattened events
+ """
+ for event in events:
+ event["event"]["kind"] = event.pop("kind")
+
+ event["attributes"] = {
+ "datetime": event.pop("datetime").isoformat(),
+ "uuid": event.pop("uuid"),
+ "originator": event.pop("originator"),
+ "sender": event.pop("sender"),
+ "sender_type": event.pop("sender_type"),
+ "sender_sdk_version": event.pop("sender_sdk_version"),
+ "recipient": event.pop("recipient"),
+ "order": event.pop("order"),
+ **event.pop("other_attributes"),
+ }
+
+ return events
diff --git a/octue/resources/child.py b/octue/resources/child.py
index b0208472d..c0f30bd67 100644
--- a/octue/resources/child.py
+++ b/octue/resources/child.py
@@ -88,9 +88,9 @@ def ask(
:param float timeout: time in seconds to wait for an answer before raising a timeout error
:param float|int maximum_heartbeat_interval: the maximum amount of time (in seconds) allowed between child heartbeats before an error is raised
:raise TimeoutError: if the timeout is exceeded while waiting for an answer
- :return dict|octue.cloud.pub_sub.subscription.Subscription|None: for a synchronous question, a dictionary containing the keys "output_values" and "output_manifest" from the result; for a question with a push endpoint, the push subscription; for an asynchronous question, `None`
+ :return dict|octue.cloud.pub_sub.subscription.Subscription|None, str: for a synchronous question, a dictionary containing the keys "output_values" and "output_manifest" from the result, and the question UUID; for a question with a push endpoint, the push subscription and the question UUID; for an asynchronous question, `None` and the question UUID
"""
- subscription, _ = self._service.ask(
+ subscription, question_uuid = self._service.ask(
service_id=self.id,
input_values=input_values,
input_manifest=input_manifest,
@@ -105,9 +105,9 @@ def ask(
)
if push_endpoint or asynchronous:
- return subscription
+ return subscription, question_uuid
- return self._service.wait_for_answer(
+ answer = self._service.wait_for_answer(
subscription=subscription,
handle_monitor_message=handle_monitor_message,
record_events=record_events,
@@ -115,6 +115,8 @@ def ask(
maximum_heartbeat_interval=maximum_heartbeat_interval,
)
+ return answer, question_uuid
+
def ask_multiple(self, *questions, raise_errors=True, max_retries=0, prevent_retries_when=None, max_workers=None):
"""Ask the child multiple questions in parallel and wait for the answers. Each question should be provided as a
dictionary of `Child.ask` keyword arguments. If `raise_errors` is `True`, an error is raised and no answers are
@@ -128,7 +130,7 @@ def ask_multiple(self, *questions, raise_errors=True, max_retries=0, prevent_ret
:param int|None max_workers: the maximum number of questions that can be asked at once; defaults to `min(32, os.cpu_count() + 4, len(questions))` (see `concurrent.futures.ThreadPoolExecutor`)
:raise ValueError: if the maximum number of parallel questions is set too high
:raise Exception: if any question raises an error if `raise_errors` is `True`
- :return list: the answers or caught errors of the questions in the same order as asked
+ :return list(dict|Exception, str): the answers or caught errors of the questions, and the question UUIDs (in the same order as asked)
"""
prevent_retries_when = prevent_retries_when or []
diff --git a/octue/templates/template-child-services/parent_service/app.py b/octue/templates/template-child-services/parent_service/app.py
index 426982e87..2d198c25b 100644
--- a/octue/templates/template-child-services/parent_service/app.py
+++ b/octue/templates/template-child-services/parent_service/app.py
@@ -16,8 +16,10 @@ def run(analysis):
# Send input data to children specified in `app_configuration.json` and receive output data. The output comes as a
# dictionary with an `output_values` key and an `output_manifest` key.
- elevations = analysis.children["elevation"].ask(input_values=analysis.input_values, timeout=60)["output_values"]
- wind_speeds = analysis.children["wind_speed"].ask(input_values=analysis.input_values, timeout=60)["output_values"]
+ elevations = analysis.children["elevation"].ask(input_values=analysis.input_values, timeout=60)[0]["output_values"]
+ wind_speeds = analysis.children["wind_speed"].ask(input_values=analysis.input_values, timeout=60)[0][
+ "output_values"
+ ]
logger.info(
"The wind speeds and elevations at %s are %s and %s.",
diff --git a/poetry.lock b/poetry.lock
index e91eec3fe..029a2fccb 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -505,7 +505,7 @@ langdetect = ["langdetect"]
name = "db-dtypes"
version = "1.2.0"
description = "Pandas Data Types for SQL systems (BigQuery, Spanner)"
-optional = false
+optional = true
python-versions = ">=3.7"
files = [
{file = "db-dtypes-1.2.0.tar.gz", hash = "sha256:3531bb1fb8b5fbab33121fe243ccc2ade16ab2524f4c113b05cc702a1908e6ea"},
@@ -577,13 +577,13 @@ dates = ["pytz (>=2019.1)"]
[[package]]
name = "exceptiongroup"
-version = "1.2.0"
+version = "1.2.1"
description = "Backport of PEP 654 (exception groups)"
optional = false
python-versions = ">=3.7"
files = [
- {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"},
- {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"},
+ {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"},
+ {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"},
]
[package.extras]
@@ -679,26 +679,27 @@ google-crc32c = "1.3.0"
[[package]]
name = "google-api-core"
-version = "2.17.1"
+version = "2.18.0"
description = "Google API client core library"
optional = false
python-versions = ">=3.7"
files = [
- {file = "google-api-core-2.17.1.tar.gz", hash = "sha256:9df18a1f87ee0df0bc4eea2770ebc4228392d8cc4066655b320e2cfccb15db95"},
- {file = "google_api_core-2.17.1-py3-none-any.whl", hash = "sha256:610c5b90092c360736baccf17bd3efbcb30dd380e7a6dc28a71059edb8bd0d8e"},
+ {file = "google-api-core-2.18.0.tar.gz", hash = "sha256:62d97417bfc674d6cef251e5c4d639a9655e00c45528c4364fbfebb478ce72a9"},
+ {file = "google_api_core-2.18.0-py3-none-any.whl", hash = "sha256:5a63aa102e0049abe85b5b88cb9409234c1f70afcda21ce1e40b285b9629c1d6"},
]
[package.dependencies]
google-auth = ">=2.14.1,<3.0.dev0"
googleapis-common-protos = ">=1.56.2,<2.0.dev0"
grpcio = [
- {version = ">=1.33.2,<2.0dev", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""},
{version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""},
+ {version = ">=1.33.2,<2.0dev", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""},
]
grpcio-status = [
- {version = ">=1.33.2,<2.0.dev0", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""},
{version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""},
+ {version = ">=1.33.2,<2.0.dev0", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""},
]
+proto-plus = ">=1.22.3,<2.0.0dev"
protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0"
requests = ">=2.18.0,<3.0.0.dev0"
@@ -709,13 +710,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"]
[[package]]
name = "google-auth"
-version = "2.28.1"
+version = "2.29.0"
description = "Google Authentication Library"
optional = false
python-versions = ">=3.7"
files = [
- {file = "google-auth-2.28.1.tar.gz", hash = "sha256:34fc3046c257cedcf1622fc4b31fc2be7923d9b4d44973d481125ecc50d83885"},
- {file = "google_auth-2.28.1-py2.py3-none-any.whl", hash = "sha256:25141e2d7a14bfcba945f5e9827f98092716e99482562f15306e5b026e21aa72"},
+ {file = "google-auth-2.29.0.tar.gz", hash = "sha256:672dff332d073227550ffc7457868ac4218d6c500b155fe6cc17d2b13602c360"},
+ {file = "google_auth-2.29.0-py2.py3-none-any.whl", hash = "sha256:d452ad095688cd52bae0ad6fafe027f6a6d6f560e810fec20914e17a09526415"},
]
[package.dependencies]
@@ -732,17 +733,18 @@ requests = ["requests (>=2.20.0,<3.0.0.dev0)"]
[[package]]
name = "google-cloud-bigquery"
-version = "3.18.0"
+version = "3.21.0"
description = "Google BigQuery API client library"
-optional = false
+optional = true
python-versions = ">=3.7"
files = [
- {file = "google-cloud-bigquery-3.18.0.tar.gz", hash = "sha256:74f0fc6f0ba9477f808d25924dc8a052c55f7ca91064e83e16d3ee5fb7ca77ab"},
- {file = "google_cloud_bigquery-3.18.0-py2.py3-none-any.whl", hash = "sha256:3520552075502c69710d37b1e9600c84e6974ad271914677d16bfafa502857fb"},
+ {file = "google-cloud-bigquery-3.21.0.tar.gz", hash = "sha256:6265c39f9d5bdf50f11cb81a9c2a0605d285df34ac139de0d2333b1250add0ff"},
+ {file = "google_cloud_bigquery-3.21.0-py2.py3-none-any.whl", hash = "sha256:83a090aae16b3a687ef22e7b0a1b551e18da615b1c4855c5f312f198959e7739"},
]
[package.dependencies]
-google-api-core = ">=1.31.5,<2.0.dev0 || >2.3.0,<3.0.0dev"
+google-api-core = {version = ">=1.34.1,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]}
+google-auth = ">=2.14.1,<3.0.0dev"
google-cloud-core = ">=1.6.0,<3.0.0dev"
google-resumable-media = ">=0.6.0,<3.0dev"
packaging = ">=20.0.0"
@@ -781,13 +783,13 @@ grpc = ["grpcio (>=1.38.0,<2.0dev)", "grpcio-status (>=1.38.0,<2.0.dev0)"]
[[package]]
name = "google-cloud-pubsub"
-version = "2.20.0"
+version = "2.21.1"
description = "Google Cloud Pub/Sub API client library"
optional = false
python-versions = ">=3.7"
files = [
- {file = "google-cloud-pubsub-2.20.0.tar.gz", hash = "sha256:48c8e17a8168c43e3188635cbd9e07fbe3004120433712ce84b3a04bbf18c188"},
- {file = "google_cloud_pubsub-2.20.0-py2.py3-none-any.whl", hash = "sha256:8c69ed04800f4f552cdf3b9028f06d9271ac6e60443b2308c984def442e69684"},
+ {file = "google-cloud-pubsub-2.21.1.tar.gz", hash = "sha256:31fcf07444b7f813a616c4b650e1fbf1dc998a088fe0059a76164855ac17f05c"},
+ {file = "google_cloud_pubsub-2.21.1-py2.py3-none-any.whl", hash = "sha256:55a3602ec45bc09626604d712032288a8ee3566145cb83523cff908938f69a4b"},
]
[package.dependencies]
@@ -797,8 +799,8 @@ grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev"
grpcio = ">=1.51.3,<2.0dev"
grpcio-status = ">=1.33.2"
proto-plus = [
- {version = ">=1.22.0,<2.0.0dev", markers = "python_version < \"3.11\""},
{version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""},
+ {version = ">=1.22.0,<2.0.0dev", markers = "python_version < \"3.11\""},
]
protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev"
@@ -807,13 +809,13 @@ libcst = ["libcst (>=0.3.10)"]
[[package]]
name = "google-cloud-secret-manager"
-version = "2.18.3"
+version = "2.20.0"
description = "Google Cloud Secret Manager API client library"
optional = false
python-versions = ">=3.7"
files = [
- {file = "google-cloud-secret-manager-2.18.3.tar.gz", hash = "sha256:1db2f409324536e34f985081d389e3974ca3a3668df7845cad0be03ab8c0fa7d"},
- {file = "google_cloud_secret_manager-2.18.3-py2.py3-none-any.whl", hash = "sha256:4d4af82bddd9099ebdbe79e0c6b68f6c6cabea8323a3c1275bcead8f56310fb7"},
+ {file = "google-cloud-secret-manager-2.20.0.tar.gz", hash = "sha256:a086a7413aaf4fffbd1c4fe9229ef0ce9bcf48f5a8df5b449c4a32deb5a2cfde"},
+ {file = "google_cloud_secret_manager-2.20.0-py2.py3-none-any.whl", hash = "sha256:c20bf22e59d220c51aa84a1db3411b14b83aa71f788fae8d273c03a4bf3e77ed"},
]
[package.dependencies]
@@ -825,13 +827,13 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4
[[package]]
name = "google-cloud-storage"
-version = "2.15.0"
+version = "2.16.0"
description = "Google Cloud Storage API client library"
optional = false
python-versions = ">=3.7"
files = [
- {file = "google-cloud-storage-2.15.0.tar.gz", hash = "sha256:7560a3c48a03d66c553dc55215d35883c680fe0ab44c23aa4832800ccc855c74"},
- {file = "google_cloud_storage-2.15.0-py2.py3-none-any.whl", hash = "sha256:5d9237f88b648e1d724a0f20b5cde65996a37fe51d75d17660b1404097327dd2"},
+ {file = "google-cloud-storage-2.16.0.tar.gz", hash = "sha256:dda485fa503710a828d01246bd16ce9db0823dc51bbca742ce96a6817d58669f"},
+ {file = "google_cloud_storage-2.16.0-py2.py3-none-any.whl", hash = "sha256:91a06b96fb79cf9cdfb4e759f178ce11ea885c79938f89590344d079305f5852"},
]
[package.dependencies]
@@ -920,13 +922,13 @@ requests = ["requests (>=2.18.0,<3.0.0dev)"]
[[package]]
name = "googleapis-common-protos"
-version = "1.62.0"
+version = "1.63.0"
description = "Common protobufs used in Google APIs"
optional = false
python-versions = ">=3.7"
files = [
- {file = "googleapis-common-protos-1.62.0.tar.gz", hash = "sha256:83f0ece9f94e5672cced82f592d2a5edf527a96ed1794f0bab36d5735c996277"},
- {file = "googleapis_common_protos-1.62.0-py2.py3-none-any.whl", hash = "sha256:4750113612205514f9f6aa4cb00d523a94f3e8c06c5ad2fee466387dc4875f07"},
+ {file = "googleapis-common-protos-1.63.0.tar.gz", hash = "sha256:17ad01b11d5f1d0171c06d3ba5c04c54474e883b66b949722b4938ee2694ef4e"},
+ {file = "googleapis_common_protos-1.63.0-py2.py3-none-any.whl", hash = "sha256:ae45f75702f7c08b541f750854a678bd8f534a1a6bace6afe975f1d0a82d6632"},
]
[package.dependencies]
@@ -954,104 +956,106 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4
[[package]]
name = "grpcio"
-version = "1.62.0"
+version = "1.62.2"
description = "HTTP/2-based RPC framework"
optional = false
python-versions = ">=3.7"
files = [
- {file = "grpcio-1.62.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:136ffd79791b1eddda8d827b607a6285474ff8a1a5735c4947b58c481e5e4271"},
- {file = "grpcio-1.62.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:d6a56ba703be6b6267bf19423d888600c3f574ac7c2cc5e6220af90662a4d6b0"},
- {file = "grpcio-1.62.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:4cd356211579043fce9f52acc861e519316fff93980a212c8109cca8f47366b6"},
- {file = "grpcio-1.62.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e803e9b58d8f9b4ff0ea991611a8d51b31c68d2e24572cd1fe85e99e8cc1b4f8"},
- {file = "grpcio-1.62.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4c04fe33039b35b97c02d2901a164bbbb2f21fb9c4e2a45a959f0b044c3512c"},
- {file = "grpcio-1.62.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:95370c71b8c9062f9ea033a0867c4c73d6f0ff35113ebd2618171ec1f1e903e0"},
- {file = "grpcio-1.62.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c912688acc05e4ff012c8891803659d6a8a8b5106f0f66e0aed3fb7e77898fa6"},
- {file = "grpcio-1.62.0-cp310-cp310-win32.whl", hash = "sha256:821a44bd63d0f04e33cf4ddf33c14cae176346486b0df08b41a6132b976de5fc"},
- {file = "grpcio-1.62.0-cp310-cp310-win_amd64.whl", hash = "sha256:81531632f93fece32b2762247c4c169021177e58e725494f9a746ca62c83acaa"},
- {file = "grpcio-1.62.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:3fa15850a6aba230eed06b236287c50d65a98f05054a0f01ccedf8e1cc89d57f"},
- {file = "grpcio-1.62.0-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:36df33080cd7897623feff57831eb83c98b84640b016ce443305977fac7566fb"},
- {file = "grpcio-1.62.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:7a195531828b46ea9c4623c47e1dc45650fc7206f8a71825898dd4c9004b0928"},
- {file = "grpcio-1.62.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ab140a3542bbcea37162bdfc12ce0d47a3cda3f2d91b752a124cc9fe6776a9e2"},
- {file = "grpcio-1.62.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f9d6c3223914abb51ac564dc9c3782d23ca445d2864321b9059d62d47144021"},
- {file = "grpcio-1.62.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:fbe0c20ce9a1cff75cfb828b21f08d0a1ca527b67f2443174af6626798a754a4"},
- {file = "grpcio-1.62.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38f69de9c28c1e7a8fd24e4af4264726637b72f27c2099eaea6e513e7142b47e"},
- {file = "grpcio-1.62.0-cp311-cp311-win32.whl", hash = "sha256:ce1aafdf8d3f58cb67664f42a617af0e34555fe955450d42c19e4a6ad41c84bd"},
- {file = "grpcio-1.62.0-cp311-cp311-win_amd64.whl", hash = "sha256:eef1d16ac26c5325e7d39f5452ea98d6988c700c427c52cbc7ce3201e6d93334"},
- {file = "grpcio-1.62.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:8aab8f90b2a41208c0a071ec39a6e5dbba16fd827455aaa070fec241624ccef8"},
- {file = "grpcio-1.62.0-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:62aa1659d8b6aad7329ede5d5b077e3d71bf488d85795db517118c390358d5f6"},
- {file = "grpcio-1.62.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:0d7ae7fc7dbbf2d78d6323641ded767d9ec6d121aaf931ec4a5c50797b886532"},
- {file = "grpcio-1.62.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f359d635ee9428f0294bea062bb60c478a8ddc44b0b6f8e1f42997e5dc12e2ee"},
- {file = "grpcio-1.62.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d48e5b1f8f4204889f1acf30bb57c30378e17c8d20df5acbe8029e985f735c"},
- {file = "grpcio-1.62.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:662d3df5314ecde3184cf87ddd2c3a66095b3acbb2d57a8cada571747af03873"},
- {file = "grpcio-1.62.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92cdb616be44c8ac23a57cce0243af0137a10aa82234f23cd46e69e115071388"},
- {file = "grpcio-1.62.0-cp312-cp312-win32.whl", hash = "sha256:0b9179478b09ee22f4a36b40ca87ad43376acdccc816ce7c2193a9061bf35701"},
- {file = "grpcio-1.62.0-cp312-cp312-win_amd64.whl", hash = "sha256:614c3ed234208e76991992342bab725f379cc81c7dd5035ee1de2f7e3f7a9842"},
- {file = "grpcio-1.62.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:7e1f51e2a460b7394670fdb615e26d31d3260015154ea4f1501a45047abe06c9"},
- {file = "grpcio-1.62.0-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:bcff647e7fe25495e7719f779cc219bbb90b9e79fbd1ce5bda6aae2567f469f2"},
- {file = "grpcio-1.62.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:56ca7ba0b51ed0de1646f1735154143dcbdf9ec2dbe8cc6645def299bb527ca1"},
- {file = "grpcio-1.62.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e84bfb2a734e4a234b116be208d6f0214e68dcf7804306f97962f93c22a1839"},
- {file = "grpcio-1.62.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c1488b31a521fbba50ae86423f5306668d6f3a46d124f7819c603979fc538c4"},
- {file = "grpcio-1.62.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:98d8f4eb91f1ce0735bf0b67c3b2a4fea68b52b2fd13dc4318583181f9219b4b"},
- {file = "grpcio-1.62.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b3d3d755cfa331d6090e13aac276d4a3fb828bf935449dc16c3d554bf366136b"},
- {file = "grpcio-1.62.0-cp37-cp37m-win_amd64.whl", hash = "sha256:a33f2bfd8a58a02aab93f94f6c61279be0f48f99fcca20ebaee67576cd57307b"},
- {file = "grpcio-1.62.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:5e709f7c8028ce0443bddc290fb9c967c1e0e9159ef7a030e8c21cac1feabd35"},
- {file = "grpcio-1.62.0-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:2f3d9a4d0abb57e5f49ed5039d3ed375826c2635751ab89dcc25932ff683bbb6"},
- {file = "grpcio-1.62.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:62ccb92f594d3d9fcd00064b149a0187c246b11e46ff1b7935191f169227f04c"},
- {file = "grpcio-1.62.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:921148f57c2e4b076af59a815467d399b7447f6e0ee10ef6d2601eb1e9c7f402"},
- {file = "grpcio-1.62.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f897b16190b46bc4d4aaf0a32a4b819d559a37a756d7c6b571e9562c360eed72"},
- {file = "grpcio-1.62.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1bc8449084fe395575ed24809752e1dc4592bb70900a03ca42bf236ed5bf008f"},
- {file = "grpcio-1.62.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:81d444e5e182be4c7856cd33a610154fe9ea1726bd071d07e7ba13fafd202e38"},
- {file = "grpcio-1.62.0-cp38-cp38-win32.whl", hash = "sha256:88f41f33da3840b4a9bbec68079096d4caf629e2c6ed3a72112159d570d98ebe"},
- {file = "grpcio-1.62.0-cp38-cp38-win_amd64.whl", hash = "sha256:fc2836cb829895ee190813446dce63df67e6ed7b9bf76060262c55fcd097d270"},
- {file = "grpcio-1.62.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:fcc98cff4084467839d0a20d16abc2a76005f3d1b38062464d088c07f500d170"},
- {file = "grpcio-1.62.0-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:0d3dee701e48ee76b7d6fbbba18ba8bc142e5b231ef7d3d97065204702224e0e"},
- {file = "grpcio-1.62.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:b7a6be562dd18e5d5bec146ae9537f20ae1253beb971c0164f1e8a2f5a27e829"},
- {file = "grpcio-1.62.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:29cb592c4ce64a023712875368bcae13938c7f03e99f080407e20ffe0a9aa33b"},
- {file = "grpcio-1.62.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1eda79574aec8ec4d00768dcb07daba60ed08ef32583b62b90bbf274b3c279f7"},
- {file = "grpcio-1.62.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7eea57444a354ee217fda23f4b479a4cdfea35fb918ca0d8a0e73c271e52c09c"},
- {file = "grpcio-1.62.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0e97f37a3b7c89f9125b92d22e9c8323f4e76e7993ba7049b9f4ccbe8bae958a"},
- {file = "grpcio-1.62.0-cp39-cp39-win32.whl", hash = "sha256:39cd45bd82a2e510e591ca2ddbe22352e8413378852ae814549c162cf3992a93"},
- {file = "grpcio-1.62.0-cp39-cp39-win_amd64.whl", hash = "sha256:b71c65427bf0ec6a8b48c68c17356cb9fbfc96b1130d20a07cb462f4e4dcdcd5"},
- {file = "grpcio-1.62.0.tar.gz", hash = "sha256:748496af9238ac78dcd98cce65421f1adce28c3979393e3609683fcd7f3880d7"},
+ {file = "grpcio-1.62.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:66344ea741124c38588a664237ac2fa16dfd226964cca23ddc96bd4accccbde5"},
+ {file = "grpcio-1.62.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:5dab7ac2c1e7cb6179c6bfad6b63174851102cbe0682294e6b1d6f0981ad7138"},
+ {file = "grpcio-1.62.2-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:3ad00f3f0718894749d5a8bb0fa125a7980a2f49523731a9b1fabf2b3522aa43"},
+ {file = "grpcio-1.62.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e72ddfee62430ea80133d2cbe788e0d06b12f865765cb24a40009668bd8ea05"},
+ {file = "grpcio-1.62.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53d3a59a10af4c2558a8e563aed9f256259d2992ae0d3037817b2155f0341de1"},
+ {file = "grpcio-1.62.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1511a303f8074f67af4119275b4f954189e8313541da7b88b1b3a71425cdb10"},
+ {file = "grpcio-1.62.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b94d41b7412ef149743fbc3178e59d95228a7064c5ab4760ae82b562bdffb199"},
+ {file = "grpcio-1.62.2-cp310-cp310-win32.whl", hash = "sha256:a75af2fc7cb1fe25785be7bed1ab18cef959a376cdae7c6870184307614caa3f"},
+ {file = "grpcio-1.62.2-cp310-cp310-win_amd64.whl", hash = "sha256:80407bc007754f108dc2061e37480238b0dc1952c855e86a4fc283501ee6bb5d"},
+ {file = "grpcio-1.62.2-cp311-cp311-linux_armv7l.whl", hash = "sha256:c1624aa686d4b36790ed1c2e2306cc3498778dffaf7b8dd47066cf819028c3ad"},
+ {file = "grpcio-1.62.2-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:1c1bb80299bdef33309dff03932264636450c8fdb142ea39f47e06a7153d3063"},
+ {file = "grpcio-1.62.2-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:db068bbc9b1fa16479a82e1ecf172a93874540cb84be69f0b9cb9b7ac3c82670"},
+ {file = "grpcio-1.62.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2cc8a308780edbe2c4913d6a49dbdb5befacdf72d489a368566be44cadaef1a"},
+ {file = "grpcio-1.62.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0695ae31a89f1a8fc8256050329a91a9995b549a88619263a594ca31b76d756"},
+ {file = "grpcio-1.62.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:88b4f9ee77191dcdd8810241e89340a12cbe050be3e0d5f2f091c15571cd3930"},
+ {file = "grpcio-1.62.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2a0204532aa2f1afd467024b02b4069246320405bc18abec7babab03e2644e75"},
+ {file = "grpcio-1.62.2-cp311-cp311-win32.whl", hash = "sha256:6e784f60e575a0de554ef9251cbc2ceb8790914fe324f11e28450047f264ee6f"},
+ {file = "grpcio-1.62.2-cp311-cp311-win_amd64.whl", hash = "sha256:112eaa7865dd9e6d7c0556c8b04ae3c3a2dc35d62ad3373ab7f6a562d8199200"},
+ {file = "grpcio-1.62.2-cp312-cp312-linux_armv7l.whl", hash = "sha256:65034473fc09628a02fb85f26e73885cf1ed39ebd9cf270247b38689ff5942c5"},
+ {file = "grpcio-1.62.2-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:d2c1771d0ee3cf72d69bb5e82c6a82f27fbd504c8c782575eddb7839729fbaad"},
+ {file = "grpcio-1.62.2-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:3abe6838196da518863b5d549938ce3159d809218936851b395b09cad9b5d64a"},
+ {file = "grpcio-1.62.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5ffeb269f10cedb4f33142b89a061acda9f672fd1357331dbfd043422c94e9e"},
+ {file = "grpcio-1.62.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:404d3b4b6b142b99ba1cff0b2177d26b623101ea2ce51c25ef6e53d9d0d87bcc"},
+ {file = "grpcio-1.62.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:262cda97efdabb20853d3b5a4c546a535347c14b64c017f628ca0cc7fa780cc6"},
+ {file = "grpcio-1.62.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:17708db5b11b966373e21519c4c73e5a750555f02fde82276ea2a267077c68ad"},
+ {file = "grpcio-1.62.2-cp312-cp312-win32.whl", hash = "sha256:b7ec9e2f8ffc8436f6b642a10019fc513722858f295f7efc28de135d336ac189"},
+ {file = "grpcio-1.62.2-cp312-cp312-win_amd64.whl", hash = "sha256:aa787b83a3cd5e482e5c79be030e2b4a122ecc6c5c6c4c42a023a2b581fdf17b"},
+ {file = "grpcio-1.62.2-cp37-cp37m-linux_armv7l.whl", hash = "sha256:cfd23ad29bfa13fd4188433b0e250f84ec2c8ba66b14a9877e8bce05b524cf54"},
+ {file = "grpcio-1.62.2-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:af15e9efa4d776dfcecd1d083f3ccfb04f876d613e90ef8432432efbeeac689d"},
+ {file = "grpcio-1.62.2-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:f4aa94361bb5141a45ca9187464ae81a92a2a135ce2800b2203134f7a1a1d479"},
+ {file = "grpcio-1.62.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82af3613a219512a28ee5c95578eb38d44dd03bca02fd918aa05603c41018051"},
+ {file = "grpcio-1.62.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55ddaf53474e8caeb29eb03e3202f9d827ad3110475a21245f3c7712022882a9"},
+ {file = "grpcio-1.62.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c79b518c56dddeec79e5500a53d8a4db90da995dfe1738c3ac57fe46348be049"},
+ {file = "grpcio-1.62.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a5eb4844e5e60bf2c446ef38c5b40d7752c6effdee882f716eb57ae87255d20a"},
+ {file = "grpcio-1.62.2-cp37-cp37m-win_amd64.whl", hash = "sha256:aaae70364a2d1fb238afd6cc9fcb10442b66e397fd559d3f0968d28cc3ac929c"},
+ {file = "grpcio-1.62.2-cp38-cp38-linux_armv7l.whl", hash = "sha256:1bcfe5070e4406f489e39325b76caeadab28c32bf9252d3ae960c79935a4cc36"},
+ {file = "grpcio-1.62.2-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:da6a7b6b938c15fa0f0568e482efaae9c3af31963eec2da4ff13a6d8ec2888e4"},
+ {file = "grpcio-1.62.2-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:41955b641c34db7d84db8d306937b72bc4968eef1c401bea73081a8d6c3d8033"},
+ {file = "grpcio-1.62.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c772f225483905f675cb36a025969eef9712f4698364ecd3a63093760deea1bc"},
+ {file = "grpcio-1.62.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07ce1f775d37ca18c7a141300e5b71539690efa1f51fe17f812ca85b5e73262f"},
+ {file = "grpcio-1.62.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:26f415f40f4a93579fd648f48dca1c13dfacdfd0290f4a30f9b9aeb745026811"},
+ {file = "grpcio-1.62.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:db707e3685ff16fc1eccad68527d072ac8bdd2e390f6daa97bc394ea7de4acea"},
+ {file = "grpcio-1.62.2-cp38-cp38-win32.whl", hash = "sha256:589ea8e75de5fd6df387de53af6c9189c5231e212b9aa306b6b0d4f07520fbb9"},
+ {file = "grpcio-1.62.2-cp38-cp38-win_amd64.whl", hash = "sha256:3c3ed41f4d7a3aabf0f01ecc70d6b5d00ce1800d4af652a549de3f7cf35c4abd"},
+ {file = "grpcio-1.62.2-cp39-cp39-linux_armv7l.whl", hash = "sha256:162ccf61499c893831b8437120600290a99c0bc1ce7b51f2c8d21ec87ff6af8b"},
+ {file = "grpcio-1.62.2-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:f27246d7da7d7e3bd8612f63785a7b0c39a244cf14b8dd9dd2f2fab939f2d7f1"},
+ {file = "grpcio-1.62.2-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:2507006c8a478f19e99b6fe36a2464696b89d40d88f34e4b709abe57e1337467"},
+ {file = "grpcio-1.62.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a90ac47a8ce934e2c8d71e317d2f9e7e6aaceb2d199de940ce2c2eb611b8c0f4"},
+ {file = "grpcio-1.62.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99701979bcaaa7de8d5f60476487c5df8f27483624f1f7e300ff4669ee44d1f2"},
+ {file = "grpcio-1.62.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:af7dc3f7a44f10863b1b0ecab4078f0a00f561aae1edbd01fd03ad4dcf61c9e9"},
+ {file = "grpcio-1.62.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:fa63245271920786f4cb44dcada4983a3516be8f470924528cf658731864c14b"},
+ {file = "grpcio-1.62.2-cp39-cp39-win32.whl", hash = "sha256:c6ad9c39704256ed91a1cffc1379d63f7d0278d6a0bad06b0330f5d30291e3a3"},
+ {file = "grpcio-1.62.2-cp39-cp39-win_amd64.whl", hash = "sha256:16da954692fd61aa4941fbeda405a756cd96b97b5d95ca58a92547bba2c1624f"},
+ {file = "grpcio-1.62.2.tar.gz", hash = "sha256:c77618071d96b7a8be2c10701a98537823b9c65ba256c0b9067e0594cdbd954d"},
]
[package.extras]
-protobuf = ["grpcio-tools (>=1.62.0)"]
+protobuf = ["grpcio-tools (>=1.62.2)"]
[[package]]
name = "grpcio-status"
-version = "1.62.0"
+version = "1.62.2"
description = "Status proto mapping for gRPC"
optional = false
python-versions = ">=3.6"
files = [
- {file = "grpcio-status-1.62.0.tar.gz", hash = "sha256:0d693e9c09880daeaac060d0c3dba1ae470a43c99e5d20dfeafd62cf7e08a85d"},
- {file = "grpcio_status-1.62.0-py3-none-any.whl", hash = "sha256:3baac03fcd737310e67758c4082a188107f771d32855bce203331cd4c9aa687a"},
+ {file = "grpcio-status-1.62.2.tar.gz", hash = "sha256:62e1bfcb02025a1cd73732a2d33672d3e9d0df4d21c12c51e0bbcaf09bab742a"},
+ {file = "grpcio_status-1.62.2-py3-none-any.whl", hash = "sha256:206ddf0eb36bc99b033f03b2c8e95d319f0044defae9b41ae21408e7e0cda48f"},
]
[package.dependencies]
googleapis-common-protos = ">=1.5.5"
-grpcio = ">=1.62.0"
+grpcio = ">=1.62.2"
protobuf = ">=4.21.6"
[[package]]
name = "gunicorn"
-version = "20.1.0"
+version = "22.0.0"
description = "WSGI HTTP Server for UNIX"
optional = false
-python-versions = ">=3.5"
+python-versions = ">=3.7"
files = [
- {file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"},
- {file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"},
+ {file = "gunicorn-22.0.0-py3-none-any.whl", hash = "sha256:350679f91b24062c86e386e198a15438d53a7a8207235a78ba1b53df4c4378d9"},
+ {file = "gunicorn-22.0.0.tar.gz", hash = "sha256:4a0b436239ff76fb33f11c07a16482c521a7e09c1ce3cc293c2330afe01bec63"},
]
[package.dependencies]
-setuptools = ">=3.0"
+importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
+packaging = "*"
[package.extras]
-eventlet = ["eventlet (>=0.24.1)"]
+eventlet = ["eventlet (>=0.24.1,!=0.36.0)"]
gevent = ["gevent (>=1.4.0)"]
setproctitle = ["setproctitle"]
+testing = ["coverage", "eventlet", "gevent", "pytest", "pytest-cov"]
tornado = ["tornado (>=0.2)"]
[[package]]
@@ -1128,13 +1132,13 @@ license = ["ukkonen"]
[[package]]
name = "idna"
-version = "3.6"
+version = "3.7"
description = "Internationalized Domain Names in Applications (IDNA)"
optional = false
python-versions = ">=3.5"
files = [
- {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"},
- {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"},
+ {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"},
+ {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"},
]
[[package]]
@@ -1469,50 +1473,50 @@ setuptools = "*"
[[package]]
name = "numpy"
-version = "1.21.0"
+version = "1.21.1"
description = "NumPy is the fundamental package for array computing with Python."
optional = false
python-versions = ">=3.7"
files = [
- {file = "numpy-1.21.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d5caa946a9f55511e76446e170bdad1d12d6b54e17a2afe7b189112ed4412bb8"},
- {file = "numpy-1.21.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ac4fd578322842dbda8d968e3962e9f22e862b6ec6e3378e7415625915e2da4d"},
- {file = "numpy-1.21.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:598fe100b2948465cf3ed64b1a326424b5e4be2670552066e17dfaa67246011d"},
- {file = "numpy-1.21.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c55407f739f0bfcec67d0df49103f9333edc870061358ac8a8c9e37ea02fcd2"},
- {file = "numpy-1.21.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:75579acbadbf74e3afd1153da6177f846212ea2a0cc77de53523ae02c9256513"},
- {file = "numpy-1.21.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cc367c86eb87e5b7c9592935620f22d13b090c609f1b27e49600cd033b529f54"},
- {file = "numpy-1.21.0-cp37-cp37m-win32.whl", hash = "sha256:d89b0dc7f005090e32bb4f9bf796e1dcca6b52243caf1803fdd2b748d8561f63"},
- {file = "numpy-1.21.0-cp37-cp37m-win_amd64.whl", hash = "sha256:eda2829af498946c59d8585a9fd74da3f810866e05f8df03a86f70079c7531dd"},
- {file = "numpy-1.21.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:1a784e8ff7ea2a32e393cc53eb0003eca1597c7ca628227e34ce34eb11645a0e"},
- {file = "numpy-1.21.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bba474a87496d96e61461f7306fba2ebba127bed7836212c360f144d1e72ac54"},
- {file = "numpy-1.21.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fd0a359c1c17f00cb37de2969984a74320970e0ceef4808c32e00773b06649d9"},
- {file = "numpy-1.21.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e4d5a86a5257843a18fb1220c5f1c199532bc5d24e849ed4b0289fb59fbd4d8f"},
- {file = "numpy-1.21.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:620732f42259eb2c4642761bd324462a01cdd13dd111740ce3d344992dd8492f"},
- {file = "numpy-1.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9205711e5440954f861ceeea8f1b415d7dd15214add2e878b4d1cf2bcb1a914"},
- {file = "numpy-1.21.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ad09f55cc95ed8d80d8ab2052f78cc21cb231764de73e229140d81ff49d8145e"},
- {file = "numpy-1.21.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a1f2fb2da242568af0271455b89aee0f71e4e032086ee2b4c5098945d0e11cf6"},
- {file = "numpy-1.21.0-cp38-cp38-win32.whl", hash = "sha256:e58ddb53a7b4959932f5582ac455ff90dcb05fac3f8dcc8079498d43afbbde6c"},
- {file = "numpy-1.21.0-cp38-cp38-win_amd64.whl", hash = "sha256:d2910d0a075caed95de1a605df00ee03b599de5419d0b95d55342e9a33ad1fb3"},
- {file = "numpy-1.21.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a290989cd671cd0605e9c91a70e6df660f73ae87484218e8285c6522d29f6e38"},
- {file = "numpy-1.21.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3537b967b350ad17633b35c2f4b1a1bbd258c018910b518c30b48c8e41272717"},
- {file = "numpy-1.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccc6c650f8700ce1e3a77668bb7c43e45c20ac06ae00d22bdf6760b38958c883"},
- {file = "numpy-1.21.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:709884863def34d72b183d074d8ba5cfe042bc3ff8898f1ffad0209161caaa99"},
- {file = "numpy-1.21.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bebab3eaf0641bba26039fb0b2c5bf9b99407924b53b1ea86e03c32c64ef5aef"},
- {file = "numpy-1.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf680682ad0a3bef56dae200dbcbac2d57294a73e5b0f9864955e7dd7c2c2491"},
- {file = "numpy-1.21.0-cp39-cp39-win32.whl", hash = "sha256:d95d16204cd51ff1a1c8d5f9958ce90ae190be81d348b514f9be39f878b8044a"},
- {file = "numpy-1.21.0-cp39-cp39-win_amd64.whl", hash = "sha256:2ba579dde0563f47021dcd652253103d6fd66165b18011dce1a0609215b2791e"},
- {file = "numpy-1.21.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3c40e6b860220ed862e8097b8f81c9af6d7405b723f4a7af24a267b46f90e461"},
- {file = "numpy-1.21.0.zip", hash = "sha256:e80fe25cba41c124d04c662f33f6364909b985f2eb5998aaa5ae4b9587242cce"},
+ {file = "numpy-1.21.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:38e8648f9449a549a7dfe8d8755a5979b45b3538520d1e735637ef28e8c2dc50"},
+ {file = "numpy-1.21.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fd7d7409fa643a91d0a05c7554dd68aa9c9bb16e186f6ccfe40d6e003156e33a"},
+ {file = "numpy-1.21.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a75b4498b1e93d8b700282dc8e655b8bd559c0904b3910b144646dbbbc03e062"},
+ {file = "numpy-1.21.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1412aa0aec3e00bc23fbb8664d76552b4efde98fb71f60737c83efbac24112f1"},
+ {file = "numpy-1.21.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e46ceaff65609b5399163de5893d8f2a82d3c77d5e56d976c8b5fb01faa6b671"},
+ {file = "numpy-1.21.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c6a2324085dd52f96498419ba95b5777e40b6bcbc20088fddb9e8cbb58885e8e"},
+ {file = "numpy-1.21.1-cp37-cp37m-win32.whl", hash = "sha256:73101b2a1fef16602696d133db402a7e7586654682244344b8329cdcbbb82172"},
+ {file = "numpy-1.21.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7a708a79c9a9d26904d1cca8d383bf869edf6f8e7650d85dbc77b041e8c5a0f8"},
+ {file = "numpy-1.21.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95b995d0c413f5d0428b3f880e8fe1660ff9396dcd1f9eedbc311f37b5652e16"},
+ {file = "numpy-1.21.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:635e6bd31c9fb3d475c8f44a089569070d10a9ef18ed13738b03049280281267"},
+ {file = "numpy-1.21.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4a3d5fb89bfe21be2ef47c0614b9c9c707b7362386c9a3ff1feae63e0267ccb6"},
+ {file = "numpy-1.21.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8a326af80e86d0e9ce92bcc1e65c8ff88297de4fa14ee936cb2293d414c9ec63"},
+ {file = "numpy-1.21.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:791492091744b0fe390a6ce85cc1bf5149968ac7d5f0477288f78c89b385d9af"},
+ {file = "numpy-1.21.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0318c465786c1f63ac05d7c4dbcecd4d2d7e13f0959b01b534ea1e92202235c5"},
+ {file = "numpy-1.21.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a513bd9c1551894ee3d31369f9b07460ef223694098cf27d399513415855b68"},
+ {file = "numpy-1.21.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:91c6f5fc58df1e0a3cc0c3a717bb3308ff850abdaa6d2d802573ee2b11f674a8"},
+ {file = "numpy-1.21.1-cp38-cp38-win32.whl", hash = "sha256:978010b68e17150db8765355d1ccdd450f9fc916824e8c4e35ee620590e234cd"},
+ {file = "numpy-1.21.1-cp38-cp38-win_amd64.whl", hash = "sha256:9749a40a5b22333467f02fe11edc98f022133ee1bfa8ab99bda5e5437b831214"},
+ {file = "numpy-1.21.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d7a4aeac3b94af92a9373d6e77b37691b86411f9745190d2c351f410ab3a791f"},
+ {file = "numpy-1.21.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d9e7912a56108aba9b31df688a4c4f5cb0d9d3787386b87d504762b6754fbb1b"},
+ {file = "numpy-1.21.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:25b40b98ebdd272bc3020935427a4530b7d60dfbe1ab9381a39147834e985eac"},
+ {file = "numpy-1.21.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8a92c5aea763d14ba9d6475803fc7904bda7decc2a0a68153f587ad82941fec1"},
+ {file = "numpy-1.21.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05a0f648eb28bae4bcb204e6fd14603de2908de982e761a2fc78efe0f19e96e1"},
+ {file = "numpy-1.21.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f01f28075a92eede918b965e86e8f0ba7b7797a95aa8d35e1cc8821f5fc3ad6a"},
+ {file = "numpy-1.21.1-cp39-cp39-win32.whl", hash = "sha256:88c0b89ad1cc24a5efbb99ff9ab5db0f9a86e9cc50240177a571fbe9c2860ac2"},
+ {file = "numpy-1.21.1-cp39-cp39-win_amd64.whl", hash = "sha256:01721eefe70544d548425a07c80be8377096a54118070b8a62476866d5208e33"},
+ {file = "numpy-1.21.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2d4d1de6e6fb3d28781c73fbde702ac97f03d79e4ffd6598b880b2d95d62ead4"},
+ {file = "numpy-1.21.1.zip", hash = "sha256:dff4af63638afcc57a3dfb9e4b26d434a7a602d225b42d746ea7fe2edf1342fd"},
]
[[package]]
name = "packaging"
-version = "23.2"
+version = "24.0"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.7"
files = [
- {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
- {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
+ {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
+ {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
]
[[package]]
@@ -1551,10 +1555,10 @@ files = [
[package.dependencies]
numpy = [
+ {version = ">=1.21.0", markers = "python_version >= \"3.10\""},
{version = ">=1.17.3", markers = "(platform_machine != \"aarch64\" and platform_machine != \"arm64\") and python_version < \"3.10\""},
{version = ">=1.19.2", markers = "platform_machine == \"aarch64\" and python_version < \"3.10\""},
{version = ">=1.20.0", markers = "platform_machine == \"arm64\" and python_version < \"3.10\""},
- {version = ">=1.21.0", markers = "python_version >= \"3.10\""},
]
python-dateutil = ">=2.7.3"
pytz = ">=2017.3"
@@ -1682,7 +1686,7 @@ files = [
name = "pyarrow"
version = "12.0.1"
description = "Python library for Apache Arrow"
-optional = false
+optional = true
python-versions = ">=3.7"
files = [
{file = "pyarrow-12.0.1-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:6d288029a94a9bb5407ceebdd7110ba398a00412c5b0155ee9813a40d246c5df"},
@@ -2637,13 +2641,13 @@ zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "virtualenv"
-version = "20.25.1"
+version = "20.25.3"
description = "Virtual Python Environment builder"
optional = false
python-versions = ">=3.7"
files = [
- {file = "virtualenv-20.25.1-py3-none-any.whl", hash = "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a"},
- {file = "virtualenv-20.25.1.tar.gz", hash = "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197"},
+ {file = "virtualenv-20.25.3-py3-none-any.whl", hash = "sha256:8aac4332f2ea6ef519c648d0bc48a5b1d324994753519919bddbb1aff25a104e"},
+ {file = "virtualenv-20.25.3.tar.gz", hash = "sha256:7bb554bbdfeaacc3349fa614ea5bff6ac300fc7c335e9facf3a3bcfc703f45be"},
]
[package.dependencies]
@@ -2653,7 +2657,7 @@ importlib-metadata = {version = ">=6.6", markers = "python_version < \"3.8\""}
platformdirs = ">=3.9.1,<5"
[package.extras]
-docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
+docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
[[package]]
@@ -2700,9 +2704,10 @@ docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker
testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
[extras]
+bigquery = ["db-dtypes", "google-cloud-bigquery"]
hdf5 = ["h5py"]
[metadata]
lock-version = "2.0"
python-versions = "^3.7.1"
-content-hash = "1572a843b2a77b5376349ff6cd212d39644931ab91b8e7b61c12709eda55617e"
+content-hash = "ae8933728fc5d778c4437e1869d340f8adab557187b500bb99e6d1f23099cb44"
diff --git a/pyproject.toml b/pyproject.toml
index 1d24ee7d1..e122881e5 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "octue"
-version = "0.53.0"
+version = "0.54.0"
description = "A package providing template applications for data services, and a python SDK to the Octue API."
readme = "README.md"
authors = ["Marcus Lugg ", "Thomas Clark "]
@@ -29,17 +29,18 @@ google-cloud-pubsub = "^2.5"
google-cloud-secret-manager = "^2.3"
google-cloud-storage = ">=1.35.1, <3"
google-crc32c = "^1.1"
-gunicorn = "^20.1"
+gunicorn = "^22"
python-dateutil = "^2.8"
pyyaml = "^6"
h5py = { version = "^3.6", optional = true }
twined = "^0.5.1"
packaging = ">=20.4"
-google-cloud-bigquery = "^3.18.0"
-db-dtypes = "^1.2.0"
+google-cloud-bigquery = { version = "^3.18.0", optional = true }
+db-dtypes = { version = "^1.2.0", optional = true }
[tool.poetry.extras]
hdf5 = ["h5py"]
+bigquery = ["google-cloud-bigquery", "db-dtypes"]
[tool.poetry.scripts]
octue = "octue.cli:octue_cli"
@@ -55,7 +56,7 @@ black = "22.6.0"
pre-commit = "^2.17"
coverage = "^5"
# Template app dependencies
-numpy = "1.21.0"
+numpy = "^1"
dateparser = "1.1.1"
stringcase = "1.2.0"
pandas = "^1.3"
diff --git a/terraform/bigquery.tf b/terraform/bigquery.tf
index 63abffe09..4c0034b89 100644
--- a/terraform/bigquery.tf
+++ b/terraform/bigquery.tf
@@ -25,6 +25,11 @@ resource "google_bigquery_table" "test_table" {
"type": "STRING",
"mode": "REQUIRED"
},
+ {
+ "name": "kind",
+ "type": "STRING",
+ "mode": "REQUIRED"
+ },
{
"name": "event",
"type": "JSON",
diff --git a/terraform/functions.tf b/terraform/functions.tf
index c0be67fe8..250ca2882 100644
--- a/terraform/functions.tf
+++ b/terraform/functions.tf
@@ -9,7 +9,7 @@ resource "google_cloudfunctions2_function" "event_handler" {
source {
storage_source {
bucket = "twined-gcp"
- object = "event_handler/0.4.0.zip"
+ object = "event_handler/0.5.0.zip"
}
}
}
diff --git a/terraform/main.tf b/terraform/main.tf
index 89e7e5561..e858236dd 100644
--- a/terraform/main.tf
+++ b/terraform/main.tf
@@ -24,6 +24,7 @@ resource "google_project_service" "pub_sub" {
}
}
+
resource "google_project_service" "cloud_resource_manager" {
project = var.project
service = "cloudresourcemanager.googleapis.com"
@@ -68,9 +69,32 @@ resource "google_project_service" "cloud_run" {
}
+resource "google_project_service" "cloud_functions" {
+ project = var.project
+ service = "cloudfunctions.googleapis.com"
+}
+
+
+resource "google_project_service" "eventarc" {
+ project = var.project
+ service = "eventarc.googleapis.com"
+}
+
+
+resource "google_project_service" "cloud_build" {
+ project = var.project
+ service = "cloudbuild.googleapis.com"
+}
+
+
+resource "google_project_service" "bigquery" {
+ project = var.project
+ service = "bigquery.googleapis.com"
+}
+
+
provider "google" {
credentials = file(var.credentials_file)
project = var.project
region = var.region
-# zone = var.zone
}
diff --git a/tests/cloud/deployment/google/cloud_run/test_cloud_run_deployment.py b/tests/cloud/deployment/google/cloud_run/test_cloud_run_deployment.py
index bb6e12903..76f60e304 100644
--- a/tests/cloud/deployment/google/cloud_run/test_cloud_run_deployment.py
+++ b/tests/cloud/deployment/google/cloud_run/test_cloud_run_deployment.py
@@ -1,11 +1,16 @@
import os
+import time
import unittest
from unittest import TestCase
import twined.exceptions
+from octue.cloud.pub_sub.bigquery import get_events
from octue.resources import Child
+EXAMPLE_SERVICE_SRUID = "octue/example-service-cloud-run:0.4.2"
+
+
@unittest.skipUnless(
condition=os.getenv("RUN_CLOUD_RUN_DEPLOYMENT_TEST", "0").lower() == "1",
reason="'RUN_CLOUD_RUN_DEPLOYMENT_TEST' environment variable is False or not present.",
@@ -13,7 +18,7 @@
class TestCloudRunDeployment(TestCase):
# This is the service ID of the example service deployed to Google Cloud Run.
child = Child(
- id="octue/example-service-cloud-run:0.4.0",
+ id=EXAMPLE_SERVICE_SRUID,
backend={"name": "GCPPubSubBackend", "project_name": os.environ["TEST_PROJECT_NAME"]},
)
@@ -26,7 +31,7 @@ def test_synchronous_question(self):
"""Test that the Google Cloud Run example deployment works, providing a service that can be asked questions and
send responses.
"""
- answer = self.child.ask(input_values={"n_iterations": 3})
+ answer, _ = self.child.ask(input_values={"n_iterations": 3})
# Check the output values.
self.assertEqual(answer["output_values"], [1, 2, 3, 4, 5])
@@ -36,6 +41,23 @@ def test_synchronous_question(self):
self.assertEqual(f.read(), "This is some example service output.")
def test_asynchronous_question(self):
- """Test asking an asynchronous question."""
- answer = self.child.ask(input_values={"n_iterations": 3}, asynchronous=True)
+ """Test asking an asynchronous question and retrieving the resulting events from the event store."""
+ answer, question_uuid = self.child.ask(input_values={"n_iterations": 3}, asynchronous=True)
self.assertIsNone(answer)
+
+ # Wait for question to complete.
+ time.sleep(10)
+
+ events = get_events(
+ table_id="octue_sdk_python_test_dataset.service-events",
+ sender=EXAMPLE_SERVICE_SRUID,
+ question_uuid=question_uuid,
+ kind="result",
+ )
+
+ # Check the output values.
+ self.assertEqual(events[0]["event"]["output_values"], [1, 2, 3, 4, 5])
+
+ # Check that the output dataset and its files can be accessed.
+ with events[0]["event"]["output_manifest"].datasets["example_dataset"].files.one() as (datafile, f):
+ self.assertEqual(f.read(), "This is some example service output.")
diff --git a/tests/cloud/emulators/test_child_emulator.py b/tests/cloud/emulators/test_child_emulator.py
index 755058f95..22e334176 100644
--- a/tests/cloud/emulators/test_child_emulator.py
+++ b/tests/cloud/emulators/test_child_emulator.py
@@ -84,7 +84,7 @@ def test_ask_with_result_event(self):
child_emulator = ChildEmulator(backend=self.BACKEND, events=events)
- result = child_emulator.ask(input_values={"hello": "world"})
+ result, _ = child_emulator.ask(input_values={"hello": "world"})
self.assertEqual(result["output_values"], [1, 2, 3, 4])
self.assertEqual(result["output_manifest"].id, output_manifest.id)
@@ -102,13 +102,13 @@ def test_ask_with_input_manifest(self):
child_emulator = ChildEmulator(backend=self.BACKEND, events=events)
- result = child_emulator.ask(input_values={"hello": "world"}, input_manifest=input_manifest)
+ result, _ = child_emulator.ask(input_values={"hello": "world"}, input_manifest=input_manifest)
self.assertEqual(result["output_values"], [1, 2, 3, 4])
def test_empty_output_returned_by_ask_if_no_result_present_in_events(self):
"""Test that an empty output is returned if no result event is present in the given events."""
child_emulator = ChildEmulator(backend=self.BACKEND, events=[])
- result = child_emulator.ask(input_values={"hello": "world"})
+ result, _ = child_emulator.ask(input_values={"hello": "world"})
self.assertEqual(result, {"output_values": None, "output_manifest": None})
def test_ask_with_log_record_with_missing_log_record_key(self):
@@ -288,8 +288,8 @@ def test_ask_more_than_one_question(self):
]
child_emulator = ChildEmulator(backend=self.BACKEND, events=events)
- result_0 = child_emulator.ask(input_values={"hello": "world"})
- result_1 = child_emulator.ask(input_values={"hello": "planet"})
+ result_0, _ = child_emulator.ask(input_values={"hello": "world"})
+ result_1, _ = child_emulator.ask(input_values={"hello": "planet"})
self.assertEqual(result_0, result_1)
@@ -302,7 +302,7 @@ def test_with_empty_file(self):
object in), asked a question, and produce a trivial result.
"""
child_emulator = ChildEmulator.from_file(os.path.join(self.TEST_FILES_DIRECTORY, "empty_file.json"))
- result = child_emulator.ask(input_values={"hello": "world"})
+ result, _ = child_emulator.ask(input_values={"hello": "world"})
self.assertEqual(result, {"output_values": None, "output_manifest": None})
def test_with_only_events(self):
@@ -316,7 +316,7 @@ def test_with_only_events(self):
monitor_messages = []
with self.assertLogs(level=logging.INFO) as logging_context:
- result = child_emulator.ask(
+ result, _ = child_emulator.ask(
input_values={"hello": "world"},
handle_monitor_message=lambda value: monitor_messages.append(value),
)
@@ -343,7 +343,7 @@ def test_with_full_file(self):
monitor_messages = []
with self.assertLogs(level=logging.INFO) as logging_context:
- result = child_emulator.ask(
+ result, _ = child_emulator.ask(
input_values={"hello": "world"},
handle_monitor_message=lambda value: monitor_messages.append(value),
)
@@ -399,7 +399,7 @@ def test_with_output_manifest(self):
},
)
- result = child_emulator.ask(input_values={"hello": "world"})
+ result, _ = child_emulator.ask(input_values={"hello": "world"})
# Check that the output manifest has been produced correctly.
self.assertEqual(result["output_manifest"].id, expected_output_manifest.id)
diff --git a/tests/cloud/pub_sub/test_bigquery.py b/tests/cloud/pub_sub/test_bigquery.py
index 0209e339a..0057dd420 100644
--- a/tests/cloud/pub_sub/test_bigquery.py
+++ b/tests/cloud/pub_sub/test_bigquery.py
@@ -1,7 +1,22 @@
from unittest import TestCase
-from unittest.mock import patch
+from unittest.mock import MagicMock, patch
from octue.cloud.pub_sub.bigquery import get_events
+from octue.exceptions import ServiceNotFound
+
+
+class MockEmptyResult:
+ """A mock empty query result."""
+
+ def result(self):
+ return MagicMock(total_rows=0)
+
+
+class MockEmptyBigQueryClient:
+ """A mock BigQuery client that returns a mock empty query result."""
+
+ def query(self, *args, **kwargs):
+ return MockEmptyResult()
class TestGetEvents(TestCase):
@@ -15,6 +30,12 @@ def test_error_raised_if_event_kind_invalid(self):
kind="frisbee_tournament",
)
+ def test_error_raised_if_no_events_found(self):
+ """Test that an error is raised if no events are found."""
+ with patch("octue.cloud.pub_sub.bigquery.Client", MockEmptyBigQueryClient):
+ with self.assertRaises(ServiceNotFound):
+ get_events(table_id="blah", sender="octue/test-service:1.0.0", question_uuid="blah")
+
def test_without_kind(self):
"""Test the query used to retrieve events of all kinds."""
with patch("octue.cloud.pub_sub.bigquery.Client") as mock_client:
@@ -22,8 +43,9 @@ def test_without_kind(self):
self.assertEqual(
mock_client.mock_calls[1].args[0],
- "SELECT `event` FROM `blah`\nWHERE sender=@sender\nAND question_uuid=@question_uuid\n"
- "ORDER BY `order`\nLIMIT @limit",
+ "SELECT `event`, `kind`, `datetime`, `uuid`, `originator`, `sender`, `sender_type`, `sender_sdk_version`, "
+ "`recipient`, `order`, `other_attributes` FROM `blah`\nWHERE sender=@sender\n"
+ "AND question_uuid=@question_uuid\nORDER BY `order`\nLIMIT @limit",
)
def test_with_kind(self):
@@ -33,24 +55,9 @@ def test_with_kind(self):
self.assertEqual(
mock_client.mock_calls[1].args[0],
- "SELECT `event` FROM `blah`\nWHERE sender=@sender\nAND question_uuid=@question_uuid\n"
- 'AND JSON_EXTRACT_SCALAR(event, "$.kind") = "result"\nORDER BY `order`\nLIMIT @limit',
- )
-
- def test_with_attributes(self):
- """Test the query used to retrieve attributes in addition to events."""
- with patch("octue.cloud.pub_sub.bigquery.Client") as mock_client:
- get_events(
- table_id="blah",
- sender="octue/test-service:1.0.0",
- question_uuid="blah",
- include_attributes=True,
- )
-
- self.assertEqual(
- mock_client.mock_calls[1].args[0],
- "SELECT `event`, `datetime`, `uuid`, `originator`, `sender`, `sender_type`, `sender_sdk_version`, `recipient`, `order`, `other_attributes` FROM `blah`\n"
- "WHERE sender=@sender\nAND question_uuid=@question_uuid\nORDER BY `order`\nLIMIT @limit",
+ "SELECT `event`, `kind`, `datetime`, `uuid`, `originator`, `sender`, `sender_type`, `sender_sdk_version`, "
+ "`recipient`, `order`, `other_attributes` FROM `blah`\nWHERE sender=@sender\n"
+ "AND question_uuid=@question_uuid\nAND kind='result'\nORDER BY `order`\nLIMIT @limit",
)
def test_with_backend_metadata(self):
@@ -65,6 +72,7 @@ def test_with_backend_metadata(self):
self.assertEqual(
mock_client.mock_calls[1].args[0],
- "SELECT `event`, `backend`, `backend_metadata` FROM `blah`\n"
+ "SELECT `event`, `kind`, `datetime`, `uuid`, `originator`, `sender`, `sender_type`, `sender_sdk_version`, "
+ "`recipient`, `order`, `other_attributes`, `backend`, `backend_metadata` FROM `blah`\n"
"WHERE sender=@sender\nAND question_uuid=@question_uuid\nORDER BY `order`\nLIMIT @limit",
)
diff --git a/tests/cloud/pub_sub/test_service.py b/tests/cloud/pub_sub/test_service.py
index 8e6526acb..6900565a9 100644
--- a/tests/cloud/pub_sub/test_service.py
+++ b/tests/cloud/pub_sub/test_service.py
@@ -801,7 +801,9 @@ def test_providing_dynamic_children(self):
def mock_child_app(analysis):
analysis.children["expected_child"]._service = child
- analysis.output_values = analysis.children["expected_child"].ask(input_values=[1, 2, 3, 4])["output_values"]
+ analysis.output_values = analysis.children["expected_child"].ask(input_values=[1, 2, 3, 4])[0][
+ "output_values"
+ ]
static_children = [
{
diff --git a/tests/resources/test_child.py b/tests/resources/test_child.py
index c0e866736..4acbea5ef 100644
--- a/tests/resources/test_child.py
+++ b/tests/resources/test_child.py
@@ -96,8 +96,8 @@ def mock_run_function(analysis_id, input_values, *args, **kwargs):
# Make sure the child's underlying mock service knows how to access the mock responding service.
child._service.children[responding_service.id] = responding_service
- self.assertEqual(child.ask([1, 2, 3, 4])["output_values"], [1, 2, 3, 4])
- self.assertEqual(child.ask([5, 6, 7, 8])["output_values"], [5, 6, 7, 8])
+ self.assertEqual(child.ask([1, 2, 3, 4])[0]["output_values"], [1, 2, 3, 4])
+ self.assertEqual(child.ask([5, 6, 7, 8])[0]["output_values"], [5, 6, 7, 8])
def test_ask_multiple(self):
"""Test that a child can be asked multiple questions in parallel and return the answers in the correct order."""
@@ -122,7 +122,7 @@ def mock_run_function(analysis_id, input_values, *args, **kwargs):
)
self.assertEqual(
- answers,
+ [answer[0] for answer in answers],
[
{"output_values": [1, 2, 3, 4], "output_manifest": None},
{"output_values": [5, 6, 7, 8], "output_manifest": None},
@@ -231,7 +231,7 @@ def test_ask_multiple_with_failed_question_retry(self):
# Check that both questions succeeded.
self.assertEqual(
- answers,
+ [answer[0] for answer in answers],
[
{"output_manifest": None, "output_values": [1, 2, 3, 4]},
{"output_manifest": None, "output_values": [5, 6, 7, 8]},
@@ -275,7 +275,7 @@ def test_ask_multiple_with_multiple_failed_question_retries(self):
# Check that all four questions succeeded.
self.assertEqual(
- answers,
+ [answer[0] for answer in answers],
[
{"output_manifest": None, "output_values": [1, 2, 3, 4]},
{"output_manifest": None, "output_values": [5, 6, 7, 8]},
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 153e2eff0..97c02da0c 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -231,7 +231,7 @@ def test_start_command_with_revision_tag_override_when_revision_tag_environment_
]
):
with ServicePatcher():
- with self.assertLogs(level=logging.WARNING) as logging_context:
+ with self.assertLogs() as logging_context:
result = CliRunner().invoke(octue_cli, ["start", "--revision-tag=hello", "--timeout=0"])
self.assertEqual(