From af58cdd8ce4ee1fde0d025c6a81355e4ab9d3692 Mon Sep 17 00:00:00 2001
From: Mehdi BEN ABDALLAH <@mbenabda>
Date: Fri, 24 May 2024 21:57:04 +0200
Subject: [PATCH 01/15] feat(cosmosdb) : add support for the CosmosDB emulator
---
index.rst | 82 +++------
modules/cosmosdb/README.rst | 2 +
.../testcontainers/cosmosdb/__init__.py | 158 ++++++++++++++++++
modules/cosmosdb/tests/test_cosmosdb.py | 6 +
pyproject.toml | 3 +
5 files changed, 190 insertions(+), 61 deletions(-)
create mode 100644 modules/cosmosdb/README.rst
create mode 100644 modules/cosmosdb/testcontainers/cosmosdb/__init__.py
create mode 100644 modules/cosmosdb/tests/test_cosmosdb.py
diff --git a/index.rst b/index.rst
index ead699b2a..8c02832fe 100644
--- a/index.rst
+++ b/index.rst
@@ -13,7 +13,6 @@ testcontainers-python
testcontainers-python facilitates the use of Docker containers for functional and integration testing. The collection of packages currently supports the following features.
.. toctree::
- :maxdepth: 1
core/README
modules/index
@@ -60,15 +59,12 @@ Installation
------------
The suite of testcontainers packages is available on `PyPI `_,
-and the package can be installed using :code:`pip`.
+and individual packages can be installed using :code:`pip`.
-Version `4.0.0` onwards we do not support the `testcontainers-*` packages as it is unsustainable to maintain ownership.
+Version `4.0.0` onwards we do not support the `testcontainers-*` packages as it is unsutainable to maintain ownership.
Instead packages can be installed by specifying `extras `__, e.g., :code:`pip install testcontainers[postgres]`.
-Please note, that community modules are supported on a best-effort basis and breaking changes DO NOT create major versions in the package.
-Therefore, only the package core is strictly following SemVer. If your workflow is broken by a minor update, please look at the changelogs for guidance.
-
Custom Containers
-----------------
@@ -84,75 +80,40 @@ For common use cases, you can also use the generic containers provided by the `t
Docker in Docker (DinD)
-----------------------
-When trying to launch Testcontainers from within a Docker container, e.g., in continuous integration testing, two things have to be provided:
+When trying to launch a testcontainer from within a Docker container, e.g., in continuous integration testing, two things have to be provided:
1. The container has to provide a docker client installation. Either use an image that has docker pre-installed (e.g. the `official docker images `_) or install the client from within the `Dockerfile` specification.
2. The container has to have access to the docker daemon which can be achieved by mounting `/var/run/docker.sock` or setting the `DOCKER_HOST` environment variable as part of your `docker run` command.
-Private Docker registry
------------------------
-
-Using a private docker registry requires the `DOCKER_AUTH_CONFIG` environment variable to be set.
-`official documentation `_
-
-The value of this variable should be a JSON string containing the authentication information for the registry.
-
-Example:
-
-.. code-block:: bash
-
- DOCKER_AUTH_CONFIG='{"auths": {"https://myregistry.com": {"auth": "dXNlcm5hbWU6cGFzc3dvcmQ="}}}'
-
-In order to generate the JSON string, you can use the following command:
-
-.. code-block:: bash
-
- echo -n '{"auths": {"": {"auth": "'$(echo -n ":" | base64 -w 0)'"}}}'
-
-Fetching passwords from cloud providers:
-
-.. code-block:: bash
-
- ECR_PASSWORD = $(aws ecr get-login-password --region eu-west-1)
- GCP_PASSWORD = $(gcloud auth print-access-token)
- AZURE_PASSWORD = $(az acr login --name --expose-token --output tsv)
-
-
Configuration
-------------
-+-------------------------------------------+---------------------------------------------------+------------------------------------------+
-| Env Variable | Example | Description |
-+===========================================+===================================================+==========================================+
-| ``TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE`` | ``/var/run/docker.sock`` | Path to Docker's socket used by ryuk |
-+-------------------------------------------+---------------------------------------------------+------------------------------------------+
-| ``TESTCONTAINERS_RYUK_PRIVILEGED`` | ``false`` | Run ryuk as a privileged container |
-+-------------------------------------------+---------------------------------------------------+------------------------------------------+
-| ``TESTCONTAINERS_RYUK_DISABLED`` | ``false`` | Disable ryuk |
-+-------------------------------------------+---------------------------------------------------+------------------------------------------+
-| ``RYUK_CONTAINER_IMAGE`` | ``testcontainers/ryuk:0.7.0`` | Custom image for ryuk |
-+-------------------------------------------+---------------------------------------------------+------------------------------------------+
-| ``DOCKER_AUTH_CONFIG`` | ``{"auths": {"": {"auth": ""}}}`` | Custom registry auth config |
-+-------------------------------------------+---------------------------------------------------+------------------------------------------+
++-------------------------------------------+-------------------------------+------------------------------------------+
+| Env Variable | Example | Description |
++===========================================+===============================+==========================================+
+| ``TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE`` | ``/var/run/docker.sock`` | Path to Docker's socket used by ryuk |
++-------------------------------------------+-------------------------------+------------------------------------------+
+| ``TESTCONTAINERS_RYUK_PRIVILEGED`` | ``false`` | Run ryuk as a privileged container |
++-------------------------------------------+-------------------------------+------------------------------------------+
+| ``TESTCONTAINERS_RYUK_DISABLED`` | ``false`` | Disable ryuk |
++-------------------------------------------+-------------------------------+------------------------------------------+
+| ``RYUK_CONTAINER_IMAGE`` | ``testcontainers/ryuk:0.7.0`` | Custom image for ryuk |
++-------------------------------------------+-------------------------------+------------------------------------------+
Development and Contributing
----------------------------
-We recommend you use a `Poetry `_ for development.
-After having installed `poetry`, you can run the following snippet to set up your local dev environment.
+We recommend you use a `virtual environment `_ for development (:code:`python>=3.7` is required). After setting up your virtual environment, you can install all dependencies and test the installation by running the following snippet.
.. code-block:: bash
- make install
+ poetry install --all-extras
+ make /tests
Package Structure
^^^^^^^^^^^^^^^^^
-Testcontainers is a collection of `implicit namespace packages `__
-to decouple the development of different extensions,
-e.g., :code:`testcontainers[mysql]` and :code:`testcontainers[postgres]` for MySQL and PostgreSQL database containers, respectively.
-
-The folder structure is as follows:
+Testcontainers is a collection of `implicit namespace packages `__ to decouple the development of different extensions, e.g., :code:`testcontainers-mysql` and :code:`testcontainers-postgres` for MySQL and PostgreSQL database containers, respectively. The folder structure is as follows.
.. code-block:: bash
@@ -172,11 +133,10 @@ The folder structure is as follows:
...
# README for this feature.
README.rst
+ # Setup script for this feature.
+ setup.py
Contributing a New Feature
^^^^^^^^^^^^^^^^^^^^^^^^^^
-You want to contribute a new feature or container? Great!
-- We recommend you first `open an issue `_
-- Then follow the suggestions from the team
-- We also have a Pull Request `template `_ for new containers!
+You want to contribute a new feature or container? Great! You can do that in six steps as outlined `here __`.
diff --git a/modules/cosmosdb/README.rst b/modules/cosmosdb/README.rst
new file mode 100644
index 000000000..bf97ac105
--- /dev/null
+++ b/modules/cosmosdb/README.rst
@@ -0,0 +1,2 @@
+.. autoclass:: testcontainers.cosmosdb.CosmosDBEmulatorContainer
+.. title:: testcontainers.cosmosdb.CosmosDBEmulatorContainer
diff --git a/modules/cosmosdb/testcontainers/cosmosdb/__init__.py b/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
new file mode 100644
index 000000000..259ea68de
--- /dev/null
+++ b/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
@@ -0,0 +1,158 @@
+from testcontainers.core.container import DockerContainer
+from testcontainers.core.waiting_utils import wait_for_logs, wait_container_is_ready
+import os
+import ssl
+import socket
+from typing import Iterable, Callable
+from typing_extensions import Self
+from azure.cosmos import CosmosClient as SyncCosmosClient
+from azure.cosmos.aio import CosmosClient as AsyncCosmosClient
+from azure.core.exceptions import ServiceRequestError
+
+from urllib.request import urlopen
+from urllib.error import HTTPError, URLError
+
+from enum import Enum, auto
+
+__all__ = ["CosmosDBEmulatorContainer", "Endpoints"]
+
+class Endpoints(Enum):
+ Direct = auto()
+ Gremlin = auto()
+ Table = auto()
+ MongoDB = auto()
+ Cassandra = auto()
+
+ALL_ENDPOINTS = { e for e in Endpoints }
+
+# Ports mostly derived from https://docs.microsoft.com/en-us/azure/cosmos-db/emulator-command-line-parameters
+EMULATOR_PORT = 8081
+endpoint_ports = {
+ Endpoints.Direct : frozenset([10251, 10252, 10253, 10254]),
+ Endpoints.Gremlin : frozenset([8901]),
+ Endpoints.Table : frozenset([8902]),
+ Endpoints.MongoDB : frozenset([10255]),
+ Endpoints.Cassandra: frozenset([10350]),
+}
+
+def is_truthy_string(s: str):
+ return s.lower().strip() in {"true", "yes", "y", "1"}
+
+class CosmosDBEmulatorContainer(DockerContainer):
+ """
+ CosmosDB Emulator container.
+
+ Example:
+
+ .. doctest::
+ >>> from testcontainers.cosmosdb import CosmosDBEmulatorContainer
+ >>> with CosmosDBEmulatorContainer() as cosmosdb:
+ ... db = cosmosdb.sync_client().create_database_if_not_exists("test")
+
+ .. doctest::
+ >>> from testcontainers.cosmosdb import CosmosDBEmulatorContainer
+ >>> with CosmosDBEmulatorContainer() as emulator:
+ ... cosmosdb = CosmosClient(url=emulator.url, credential=emulator.key, connection_verify=False)
+ ... db = cosmosdb.create_database_if_not_exists("test")
+
+ .. doctest::
+ >>> from testcontainers.cosmosdb import CosmosDBEmulatorContainer, Endpoints
+ >>> with CosmosDBEmulatorContainer(endpoints=[Endpoints.MongoDB]) as emulator:
+ ... print(f"Point yout MongoDB client to {emulator.host}:{emulator.ports(Endpoints.MongoDB)[0]}")
+ """
+ def __init__(
+ self,
+ image: str = os.getenv("AZURE_COSMOS_EMULATOR_IMAGE", "mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest"),
+ partition_count: int = os.getenv("AZURE_COSMOS_EMULATOR_PARTITION_COUNT", None),
+ enable_data_persistence: bool = is_truthy_string(os.getenv("AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE", "false")),
+ bind_ports: bool = is_truthy_string(os.getenv("AZURE_COSMOS_EMULATOR_BIND_PORTS", "true")),
+ key: str = os.getenv("AZURE_COSMOS_EMULATOR_KEY", "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="),
+ endpoints: Iterable[Endpoints] = ALL_ENDPOINTS, # the emulator image does not support host-container port mapping
+ **docker_client_kw,
+ ):
+ super().__init__(image=image, **docker_client_kw)
+
+ self.partition_count = partition_count
+ self.key = key
+ self.enable_data_persistence = enable_data_persistence
+ self.endpoints = frozenset(endpoints)
+
+ self.with_bind_ports(EMULATOR_PORT, EMULATOR_PORT)
+
+ endpoints_ports = []
+ for endpoint in self.endpoints:
+ endpoints_ports.extend(endpoint_ports[endpoint])
+
+ if bind_ports:
+ [ self.with_bind_ports(port, port) for port in endpoints_ports ]
+ else:
+ self.with_exposed_ports(*endpoints_ports)
+
+ def start(self) -> Self:
+ self._configure()
+ super().start()
+ self._wait_until_ready()
+ return self
+
+ @property
+ def url(self) -> str:
+ """
+ Returns the url to interact with the emulator
+ """
+ return f"https://{self.host}:{self.get_exposed_port(EMULATOR_PORT)}"
+
+ @property
+ def host(self) -> str:
+ return self.get_container_host_ip()
+
+ def ports(self, endpoint: Endpoints) -> Iterable[int]:
+ assert endpoint in self.endpoints, f"Endpoint {endpoint} is not exposed"
+ return { self.get_exposed_port(p) for p in endpoint_ports[endpoint] }
+
+ def async_client(self) -> AsyncCosmosClient:
+ """
+ Returns an asynchronous CosmosClient instance to interact with the CosmosDB server
+ """
+ return AsyncCosmosClient(url=self.url, credential=self.key, connection_verify=False)
+
+ def sync_client(self) -> SyncCosmosClient:
+ """
+ Returns a synchronous CosmosClient instance to interact with the CosmosDB server
+ """
+ return SyncCosmosClient(url=self.url, credential=self.key, connection_verify=False)
+
+ def _configure(self) -> None:
+ (
+ self
+ .with_env("AZURE_COSMOS_EMULATOR_PARTITION_COUNT", str(self.partition_count))
+ .with_env("AZURE_COSMOS_EMULATOR_IP_ADDRESS_OVERRIDE", socket.gethostbyname(socket.gethostname()))
+ .with_env("AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE", str(self.enable_data_persistence))
+ .with_env("AZURE_COSMOS_EMULATOR_KEY", str(self.key))
+ )
+
+ @wait_container_is_ready(HTTPError, URLError, ServiceRequestError)
+ def _wait_until_ready(self) -> Self:
+ """
+ Waits until the CosmosDB Emulator image is ready to be used.
+ """
+ (
+ self
+ ._wait_for_logs(container=self, predicate="Started\\s*$")
+ ._wait_for_url(f"{self.url}/_explorer/index.html")
+ ._wait_for_query_success(lambda sync_client: list(sync_client.list_databases()))
+ )
+ return self
+
+ def _wait_for_url(self, url: str) -> Self:
+ with urlopen(url, context=ssl._create_unverified_context()) as response:
+ response.read()
+ return self
+
+ def _wait_for_logs(self, *args, **kwargs) -> Self:
+ wait_for_logs(*args, **kwargs)
+ return self
+
+ def _wait_for_query_success(self, query: Callable[[SyncCosmosClient], None]) -> Self:
+ with self.sync_client() as c:
+ query(c)
+ return self
diff --git a/modules/cosmosdb/tests/test_cosmosdb.py b/modules/cosmosdb/tests/test_cosmosdb.py
new file mode 100644
index 000000000..17cfe257a
--- /dev/null
+++ b/modules/cosmosdb/tests/test_cosmosdb.py
@@ -0,0 +1,6 @@
+import pytest
+from testcontainers.cosmosdb import CosmosDBEmulatorContainer
+
+def test_docker_run():
+ with CosmosDBEmulatorContainer(partition_count=1) as cosmosdb:
+ list(cosmosdb.sync_client().list_databases())
diff --git a/pyproject.toml b/pyproject.toml
index 0b6088954..c7a398d7b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -34,6 +34,7 @@ packages = [
{ include = "testcontainers", from = "modules/chroma" },
{ include = "testcontainers", from = "modules/clickhouse" },
{ include = "testcontainers", from = "modules/cockroachdb" },
+ { include = "testcontainers", from = "modules/cosmosdb" },
{ include = "testcontainers", from = "modules/elasticsearch" },
{ include = "testcontainers", from = "modules/generic" },
{ include = "testcontainers", from = "modules/testmoduleimport"},
@@ -106,12 +107,14 @@ chromadb-client = { version = "*", optional = true }
qdrant-client = { version = "*", optional = true }
bcrypt = { version = "*", optional = true }
httpx = { version = "*", optional = true }
+azure-cosmos = { version = "*", optional = true }
[tool.poetry.extras]
arangodb = ["python-arango"]
azurite = ["azure-storage-blob"]
cassandra = []
clickhouse = ["clickhouse-driver"]
+cosmosdb = ["azure-cosmos"]
cockroachdb = []
elasticsearch = []
generic = ["httpx"]
From aa2a42026f77b27835c81fae4ecdbbe11ac8e518 Mon Sep 17 00:00:00 2001
From: Mehdi BEN ABDALLAH <@mbenabda>
Date: Fri, 24 May 2024 22:58:43 +0200
Subject: [PATCH 02/15] less verbose starting
---
modules/cosmosdb/testcontainers/cosmosdb/__init__.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/modules/cosmosdb/testcontainers/cosmosdb/__init__.py b/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
index 259ea68de..22dd902b7 100644
--- a/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
+++ b/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
@@ -130,7 +130,6 @@ def _configure(self) -> None:
.with_env("AZURE_COSMOS_EMULATOR_KEY", str(self.key))
)
- @wait_container_is_ready(HTTPError, URLError, ServiceRequestError)
def _wait_until_ready(self) -> Self:
"""
Waits until the CosmosDB Emulator image is ready to be used.
@@ -143,6 +142,7 @@ def _wait_until_ready(self) -> Self:
)
return self
+ @wait_container_is_ready(HTTPError, URLError)
def _wait_for_url(self, url: str) -> Self:
with urlopen(url, context=ssl._create_unverified_context()) as response:
response.read()
@@ -152,6 +152,7 @@ def _wait_for_logs(self, *args, **kwargs) -> Self:
wait_for_logs(*args, **kwargs)
return self
+ @wait_container_is_ready(ServiceRequestError)
def _wait_for_query_success(self, query: Callable[[SyncCosmosClient], None]) -> Self:
with self.sync_client() as c:
query(c)
From e3aafdd8db32cad8d3f18664380916a62bdea7d0 Mon Sep 17 00:00:00 2001
From: Mehdi BEN ABDALLAH <@mbenabda>
Date: Fri, 24 May 2024 22:59:20 +0200
Subject: [PATCH 03/15] bind the minimal set of ports by default
---
modules/cosmosdb/testcontainers/cosmosdb/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/modules/cosmosdb/testcontainers/cosmosdb/__init__.py b/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
index 22dd902b7..bc8528c3b 100644
--- a/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
+++ b/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
@@ -67,7 +67,7 @@ def __init__(
enable_data_persistence: bool = is_truthy_string(os.getenv("AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE", "false")),
bind_ports: bool = is_truthy_string(os.getenv("AZURE_COSMOS_EMULATOR_BIND_PORTS", "true")),
key: str = os.getenv("AZURE_COSMOS_EMULATOR_KEY", "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="),
- endpoints: Iterable[Endpoints] = ALL_ENDPOINTS, # the emulator image does not support host-container port mapping
+ endpoints: Iterable[Endpoints] = [], # the emulator image does not support host-container port mapping
**docker_client_kw,
):
super().__init__(image=image, **docker_client_kw)
From c01d02f5705c182f6b5f199e9b48273102882eb8 Mon Sep 17 00:00:00 2001
From: Mehdi BEN ABDALLAH <@mbenabda>
Date: Fri, 24 May 2024 23:05:01 +0200
Subject: [PATCH 04/15] run poetry lock --no-update
---
poetry.lock | 314 ++++------------------------------------------------
1 file changed, 24 insertions(+), 290 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index aa5fdc29b..edb5d0374 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -175,6 +175,21 @@ typing-extensions = ">=4.6.0"
[package.extras]
aio = ["aiohttp (>=3.0)"]
+[[package]]
+name = "azure-cosmos"
+version = "4.7.0"
+description = "Microsoft Azure Cosmos Client Library for Python"
+optional = true
+python-versions = ">=3.8"
+files = [
+ {file = "azure-cosmos-4.7.0.tar.gz", hash = "sha256:72d714033134656302a2e8957c4b93590673bd288b0ca60cb123e348ae99a241"},
+ {file = "azure_cosmos-4.7.0-py3-none-any.whl", hash = "sha256:03d8c7740ddc2906fb16e07b136acc0fe6a6a02656db46c5dd6f1b127b58cc96"},
+]
+
+[package.dependencies]
+azure-core = ">=1.25.1"
+typing-extensions = ">=4.6.0"
+
[[package]]
name = "azure-storage-blob"
version = "12.19.1"
@@ -931,27 +946,6 @@ files = [
{file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"},
]
-[[package]]
-name = "environs"
-version = "9.5.0"
-description = "simplified environment variable parsing"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "environs-9.5.0-py2.py3-none-any.whl", hash = "sha256:1e549569a3de49c05f856f40bce86979e7d5ffbbc4398e7f338574c220189124"},
- {file = "environs-9.5.0.tar.gz", hash = "sha256:a76307b36fbe856bdca7ee9161e6c466fd7fcffc297109a118c59b54e27e30c9"},
-]
-
-[package.dependencies]
-marshmallow = ">=3.0.0"
-python-dotenv = "*"
-
-[package.extras]
-dev = ["dj-database-url", "dj-email-url", "django-cache-url", "flake8 (==4.0.1)", "flake8-bugbear (==21.9.2)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)", "pytest", "tox"]
-django = ["dj-database-url", "dj-email-url", "django-cache-url"]
-lint = ["flake8 (==4.0.1)", "flake8-bugbear (==21.9.2)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)"]
-tests = ["dj-database-url", "dj-email-url", "django-cache-url", "pytest"]
-
[[package]]
name = "exceptiongroup"
version = "1.2.0"
@@ -1226,7 +1220,7 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4
name = "grpcio"
version = "1.62.1"
description = "HTTP/2-based RPC framework"
-optional = false
+optional = true
python-versions = ">=3.7"
files = [
{file = "grpcio-1.62.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:179bee6f5ed7b5f618844f760b6acf7e910988de77a4f75b95bbfaa8106f3c1e"},
@@ -1391,7 +1385,7 @@ setuptools = "*"
name = "h11"
version = "0.14.0"
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
-optional = false
+optional = true
python-versions = ">=3.7"
files = [
{file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
@@ -1428,7 +1422,7 @@ files = [
name = "httpcore"
version = "1.0.5"
description = "A minimal low-level HTTP client."
-optional = false
+optional = true
python-versions = ">=3.8"
files = [
{file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"},
@@ -1449,7 +1443,7 @@ trio = ["trio (>=0.22.0,<0.26.0)"]
name = "httpx"
version = "0.27.0"
description = "The next generation HTTP client."
-optional = false
+optional = true
python-versions = ">=3.8"
files = [
{file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"},
@@ -1859,25 +1853,6 @@ files = [
{file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"},
]
-[[package]]
-name = "marshmallow"
-version = "3.21.3"
-description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "marshmallow-3.21.3-py3-none-any.whl", hash = "sha256:86ce7fb914aa865001a4b2092c4c2872d13bc347f3d42673272cabfdbad386f1"},
- {file = "marshmallow-3.21.3.tar.gz", hash = "sha256:4f57c5e050a54d66361e826f94fba213eb10b67b2fdb02c3e0343ce207ba1662"},
-]
-
-[package.dependencies]
-packaging = ">=17.0"
-
-[package.extras]
-dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"]
-docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.3.7)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"]
-tests = ["pytest", "pytz", "simplejson"]
-
[[package]]
name = "mdurl"
version = "0.1.2"
@@ -1889,18 +1864,6 @@ files = [
{file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
]
-[[package]]
-name = "milvus-lite"
-version = "2.4.7"
-description = "A lightweight version of Milvus wrapped with Python."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "milvus_lite-2.4.7-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:c828190118b104b05b8c8e0b5a4147811c86b54b8fb67bc2e726ad10fc0b544e"},
- {file = "milvus_lite-2.4.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e1537633c39879714fb15082be56a4b97f74c905a6e98e302ec01320561081af"},
- {file = "milvus_lite-2.4.7-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f016474d663045787dddf1c3aad13b7d8b61fd329220318f858184918143dcbf"},
-]
-
[[package]]
name = "minio"
version = "7.2.5"
@@ -2140,7 +2103,7 @@ setuptools = "*"
name = "numpy"
version = "1.26.4"
description = "Fundamental package for array computing in Python"
-optional = false
+optional = true
python-versions = ">=3.9"
files = [
{file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"},
@@ -2439,93 +2402,6 @@ files = [
{file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
]
-[[package]]
-name = "paho-mqtt"
-version = "2.1.0"
-description = "MQTT version 5.0/3.1.1 client class"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "paho_mqtt-2.1.0-py3-none-any.whl", hash = "sha256:6db9ba9b34ed5bc6b6e3812718c7e06e2fd7444540df2455d2c51bd58808feee"},
- {file = "paho_mqtt-2.1.0.tar.gz", hash = "sha256:12d6e7511d4137555a3f6ea167ae846af2c7357b10bc6fa4f7c3968fc1723834"},
-]
-
-[package.extras]
-proxy = ["pysocks"]
-
-[[package]]
-name = "pandas"
-version = "2.2.2"
-description = "Powerful data structures for data analysis, time series, and statistics"
-optional = false
-python-versions = ">=3.9"
-files = [
- {file = "pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce"},
- {file = "pandas-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7adfc142dac335d8c1e0dcbd37eb8617eac386596eb9e1a1b77791cf2498238"},
- {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08"},
- {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0"},
- {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51"},
- {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e5a0b00e1e56a842f922e7fae8ae4077aee4af0acb5ae3622bd4b4c30aedf99"},
- {file = "pandas-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:ddf818e4e6c7c6f4f7c8a12709696d193976b591cc7dc50588d3d1a6b5dc8772"},
- {file = "pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288"},
- {file = "pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151"},
- {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b"},
- {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee"},
- {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db"},
- {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"},
- {file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"},
- {file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"},
- {file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"},
- {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"},
- {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"},
- {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"},
- {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"},
- {file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"},
- {file = "pandas-2.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2"},
- {file = "pandas-2.2.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9057e6aa78a584bc93a13f0a9bf7e753a5e9770a30b4d758b8d5f2a62a9433cd"},
- {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863"},
- {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921"},
- {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a"},
- {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92fd6b027924a7e178ac202cfbe25e53368db90d56872d20ffae94b96c7acc57"},
- {file = "pandas-2.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:640cef9aa381b60e296db324337a554aeeb883ead99dc8f6c18e81a93942f5f4"},
- {file = "pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54"},
-]
-
-[package.dependencies]
-numpy = [
- {version = ">=1.23.2", markers = "python_version == \"3.11\""},
- {version = ">=1.22.4", markers = "python_version < \"3.11\""},
- {version = ">=1.26.0", markers = "python_version >= \"3.12\""},
-]
-python-dateutil = ">=2.8.2"
-pytz = ">=2020.1"
-tzdata = ">=2022.7"
-
-[package.extras]
-all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"]
-aws = ["s3fs (>=2022.11.0)"]
-clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"]
-compression = ["zstandard (>=0.19.0)"]
-computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"]
-consortium-standard = ["dataframe-api-compat (>=0.1.7)"]
-excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"]
-feather = ["pyarrow (>=10.0.1)"]
-fss = ["fsspec (>=2022.11.0)"]
-gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"]
-hdf5 = ["tables (>=3.8.0)"]
-html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"]
-mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"]
-output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"]
-parquet = ["pyarrow (>=10.0.1)"]
-performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"]
-plot = ["matplotlib (>=3.6.3)"]
-postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"]
-pyarrow = ["pyarrow (>=10.0.1)"]
-spss = ["pyreadstat (>=1.2.0)"]
-sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"]
-test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"]
-xml = ["lxml (>=4.9.2)"]
-
[[package]]
name = "pg8000"
version = "1.30.5"
@@ -2682,7 +2558,7 @@ testing = ["google-api-core[grpc] (>=1.31.5)"]
name = "protobuf"
version = "4.25.3"
description = ""
-optional = false
+optional = true
python-versions = ">=3.8"
files = [
{file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"},
@@ -3021,31 +2897,6 @@ dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pyte
docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"]
tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
-[[package]]
-name = "pymilvus"
-version = "2.4.3"
-description = "Python Sdk for Milvus"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "pymilvus-2.4.3-py3-none-any.whl", hash = "sha256:38239e89f8d739f665141d0b80908990b5f59681e889e135c234a4a45669a5c8"},
- {file = "pymilvus-2.4.3.tar.gz", hash = "sha256:703ac29296cdce03d6dc2aaebbe959e57745c141a94150e371dc36c61c226cc1"},
-]
-
-[package.dependencies]
-environs = "<=9.5.0"
-grpcio = ">=1.49.1,<=1.63.0"
-milvus-lite = ">=2.4.0,<2.5.0"
-pandas = ">=1.2.4"
-protobuf = ">=3.20.0"
-setuptools = ">=67"
-ujson = ">=2.0.0"
-
-[package.extras]
-bulk-writer = ["azure-storage-blob", "minio (>=7.0.0)", "pyarrow (>=12.0.0)", "requests"]
-dev = ["black", "grpcio (==1.62.2)", "grpcio-testing (==1.62.2)", "grpcio-tools (==1.62.2)", "pytest (>=5.3.4)", "pytest-cov (>=2.8.1)", "pytest-timeout (>=1.3.4)", "ruff (>0.4.0)"]
-model = ["milvus-model (>=0.1.0)"]
-
[[package]]
name = "pymongo"
version = "4.6.2"
@@ -3351,20 +3202,6 @@ files = [
[package.dependencies]
six = ">=1.5"
-[[package]]
-name = "python-dotenv"
-version = "1.0.1"
-description = "Read key-value pairs from a .env file and set them as environment variables"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"},
- {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"},
-]
-
-[package.extras]
-cli = ["click (>=5.0)"]
-
[[package]]
name = "python-keycloak"
version = "3.9.1"
@@ -3389,7 +3226,7 @@ docs = ["Sphinx (>=6.1.0,<7.0.0)", "alabaster (>=0.7.12,<0.8.0)", "commonmark (>
name = "pytz"
version = "2024.1"
description = "World timezone definitions, modern and historical"
-optional = false
+optional = true
python-versions = "*"
files = [
{file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"},
@@ -4006,20 +3843,6 @@ postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"]
pymysql = ["pymysql"]
sqlcipher = ["sqlcipher3_binary"]
-[[package]]
-name = "sqlalchemy-cockroachdb"
-version = "2.0.2"
-description = "CockroachDB dialect for SQLAlchemy"
-optional = false
-python-versions = "*"
-files = [
- {file = "sqlalchemy-cockroachdb-2.0.2.tar.gz", hash = "sha256:119756eb905855d6a11345b99cfe853031a3fe598a9c4bf35a8ddac9f89fe8cc"},
- {file = "sqlalchemy_cockroachdb-2.0.2-py3-none-any.whl", hash = "sha256:0d5d50e805b024cb2ccd85423a5c1a367d1a56a5cd0ea47765233fd47665070d"},
-]
-
-[package.dependencies]
-SQLAlchemy = "*"
-
[[package]]
name = "tenacity"
version = "8.2.3"
@@ -4142,93 +3965,6 @@ tzdata = {version = "*", markers = "platform_system == \"Windows\""}
[package.extras]
devenv = ["check-manifest", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3)", "zest.releaser"]
-[[package]]
-name = "ujson"
-version = "5.10.0"
-description = "Ultra fast JSON encoder and decoder for Python"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "ujson-5.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2601aa9ecdbee1118a1c2065323bda35e2c5a2cf0797ef4522d485f9d3ef65bd"},
- {file = "ujson-5.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:348898dd702fc1c4f1051bc3aacbf894caa0927fe2c53e68679c073375f732cf"},
- {file = "ujson-5.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22cffecf73391e8abd65ef5f4e4dd523162a3399d5e84faa6aebbf9583df86d6"},
- {file = "ujson-5.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26b0e2d2366543c1bb4fbd457446f00b0187a2bddf93148ac2da07a53fe51569"},
- {file = "ujson-5.10.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:caf270c6dba1be7a41125cd1e4fc7ba384bf564650beef0df2dd21a00b7f5770"},
- {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a245d59f2ffe750446292b0094244df163c3dc96b3ce152a2c837a44e7cda9d1"},
- {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:94a87f6e151c5f483d7d54ceef83b45d3a9cca7a9cb453dbdbb3f5a6f64033f5"},
- {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:29b443c4c0a113bcbb792c88bea67b675c7ca3ca80c3474784e08bba01c18d51"},
- {file = "ujson-5.10.0-cp310-cp310-win32.whl", hash = "sha256:c18610b9ccd2874950faf474692deee4223a994251bc0a083c114671b64e6518"},
- {file = "ujson-5.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:924f7318c31874d6bb44d9ee1900167ca32aa9b69389b98ecbde34c1698a250f"},
- {file = "ujson-5.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a5b366812c90e69d0f379a53648be10a5db38f9d4ad212b60af00bd4048d0f00"},
- {file = "ujson-5.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:502bf475781e8167f0f9d0e41cd32879d120a524b22358e7f205294224c71126"},
- {file = "ujson-5.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b91b5d0d9d283e085e821651184a647699430705b15bf274c7896f23fe9c9d8"},
- {file = "ujson-5.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:129e39af3a6d85b9c26d5577169c21d53821d8cf68e079060602e861c6e5da1b"},
- {file = "ujson-5.10.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f77b74475c462cb8b88680471193064d3e715c7c6074b1c8c412cb526466efe9"},
- {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7ec0ca8c415e81aa4123501fee7f761abf4b7f386aad348501a26940beb1860f"},
- {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab13a2a9e0b2865a6c6db9271f4b46af1c7476bfd51af1f64585e919b7c07fd4"},
- {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:57aaf98b92d72fc70886b5a0e1a1ca52c2320377360341715dd3933a18e827b1"},
- {file = "ujson-5.10.0-cp311-cp311-win32.whl", hash = "sha256:2987713a490ceb27edff77fb184ed09acdc565db700ee852823c3dc3cffe455f"},
- {file = "ujson-5.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:f00ea7e00447918ee0eff2422c4add4c5752b1b60e88fcb3c067d4a21049a720"},
- {file = "ujson-5.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98ba15d8cbc481ce55695beee9f063189dce91a4b08bc1d03e7f0152cd4bbdd5"},
- {file = "ujson-5.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9d2edbf1556e4f56e50fab7d8ff993dbad7f54bac68eacdd27a8f55f433578e"},
- {file = "ujson-5.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6627029ae4f52d0e1a2451768c2c37c0c814ffc04f796eb36244cf16b8e57043"},
- {file = "ujson-5.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8ccb77b3e40b151e20519c6ae6d89bfe3f4c14e8e210d910287f778368bb3d1"},
- {file = "ujson-5.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3caf9cd64abfeb11a3b661329085c5e167abbe15256b3b68cb5d914ba7396f3"},
- {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6e32abdce572e3a8c3d02c886c704a38a1b015a1fb858004e03d20ca7cecbb21"},
- {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a65b6af4d903103ee7b6f4f5b85f1bfd0c90ba4eeac6421aae436c9988aa64a2"},
- {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:604a046d966457b6cdcacc5aa2ec5314f0e8c42bae52842c1e6fa02ea4bda42e"},
- {file = "ujson-5.10.0-cp312-cp312-win32.whl", hash = "sha256:6dea1c8b4fc921bf78a8ff00bbd2bfe166345f5536c510671bccececb187c80e"},
- {file = "ujson-5.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:38665e7d8290188b1e0d57d584eb8110951a9591363316dd41cf8686ab1d0abc"},
- {file = "ujson-5.10.0-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:618efd84dc1acbd6bff8eaa736bb6c074bfa8b8a98f55b61c38d4ca2c1f7f287"},
- {file = "ujson-5.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38d5d36b4aedfe81dfe251f76c0467399d575d1395a1755de391e58985ab1c2e"},
- {file = "ujson-5.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67079b1f9fb29ed9a2914acf4ef6c02844b3153913eb735d4bf287ee1db6e557"},
- {file = "ujson-5.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7d0e0ceeb8fe2468c70ec0c37b439dd554e2aa539a8a56365fd761edb418988"},
- {file = "ujson-5.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:59e02cd37bc7c44d587a0ba45347cc815fb7a5fe48de16bf05caa5f7d0d2e816"},
- {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2a890b706b64e0065f02577bf6d8ca3b66c11a5e81fb75d757233a38c07a1f20"},
- {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:621e34b4632c740ecb491efc7f1fcb4f74b48ddb55e65221995e74e2d00bbff0"},
- {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b9500e61fce0cfc86168b248104e954fead61f9be213087153d272e817ec7b4f"},
- {file = "ujson-5.10.0-cp313-cp313-win32.whl", hash = "sha256:4c4fc16f11ac1612f05b6f5781b384716719547e142cfd67b65d035bd85af165"},
- {file = "ujson-5.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:4573fd1695932d4f619928fd09d5d03d917274381649ade4328091ceca175539"},
- {file = "ujson-5.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a984a3131da7f07563057db1c3020b1350a3e27a8ec46ccbfbf21e5928a43050"},
- {file = "ujson-5.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73814cd1b9db6fc3270e9d8fe3b19f9f89e78ee9d71e8bd6c9a626aeaeaf16bd"},
- {file = "ujson-5.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61e1591ed9376e5eddda202ec229eddc56c612b61ac6ad07f96b91460bb6c2fb"},
- {file = "ujson-5.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2c75269f8205b2690db4572a4a36fe47cd1338e4368bc73a7a0e48789e2e35a"},
- {file = "ujson-5.10.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7223f41e5bf1f919cd8d073e35b229295aa8e0f7b5de07ed1c8fddac63a6bc5d"},
- {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc2fd6b3067c0782e7002ac3b38cf48608ee6366ff176bbd02cf969c9c20fe"},
- {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:232cc85f8ee3c454c115455195a205074a56ff42608fd6b942aa4c378ac14dd7"},
- {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:cc6139531f13148055d691e442e4bc6601f6dba1e6d521b1585d4788ab0bfad4"},
- {file = "ujson-5.10.0-cp38-cp38-win32.whl", hash = "sha256:e7ce306a42b6b93ca47ac4a3b96683ca554f6d35dd8adc5acfcd55096c8dfcb8"},
- {file = "ujson-5.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:e82d4bb2138ab05e18f089a83b6564fee28048771eb63cdecf4b9b549de8a2cc"},
- {file = "ujson-5.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dfef2814c6b3291c3c5f10065f745a1307d86019dbd7ea50e83504950136ed5b"},
- {file = "ujson-5.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4734ee0745d5928d0ba3a213647f1c4a74a2a28edc6d27b2d6d5bd9fa4319e27"},
- {file = "ujson-5.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d47ebb01bd865fdea43da56254a3930a413f0c5590372a1241514abae8aa7c76"},
- {file = "ujson-5.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dee5e97c2496874acbf1d3e37b521dd1f307349ed955e62d1d2f05382bc36dd5"},
- {file = "ujson-5.10.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7490655a2272a2d0b072ef16b0b58ee462f4973a8f6bbe64917ce5e0a256f9c0"},
- {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ba17799fcddaddf5c1f75a4ba3fd6441f6a4f1e9173f8a786b42450851bd74f1"},
- {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2aff2985cef314f21d0fecc56027505804bc78802c0121343874741650a4d3d1"},
- {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ad88ac75c432674d05b61184178635d44901eb749786c8eb08c102330e6e8996"},
- {file = "ujson-5.10.0-cp39-cp39-win32.whl", hash = "sha256:2544912a71da4ff8c4f7ab5606f947d7299971bdd25a45e008e467ca638d13c9"},
- {file = "ujson-5.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:3ff201d62b1b177a46f113bb43ad300b424b7847f9c5d38b1b4ad8f75d4a282a"},
- {file = "ujson-5.10.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5b6fee72fa77dc172a28f21693f64d93166534c263adb3f96c413ccc85ef6e64"},
- {file = "ujson-5.10.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:61d0af13a9af01d9f26d2331ce49bb5ac1fb9c814964018ac8df605b5422dcb3"},
- {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecb24f0bdd899d368b715c9e6664166cf694d1e57be73f17759573a6986dd95a"},
- {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fbd8fd427f57a03cff3ad6574b5e299131585d9727c8c366da4624a9069ed746"},
- {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beeaf1c48e32f07d8820c705ff8e645f8afa690cca1544adba4ebfa067efdc88"},
- {file = "ujson-5.10.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:baed37ea46d756aca2955e99525cc02d9181de67f25515c468856c38d52b5f3b"},
- {file = "ujson-5.10.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7663960f08cd5a2bb152f5ee3992e1af7690a64c0e26d31ba7b3ff5b2ee66337"},
- {file = "ujson-5.10.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:d8640fb4072d36b08e95a3a380ba65779d356b2fee8696afeb7794cf0902d0a1"},
- {file = "ujson-5.10.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78778a3aa7aafb11e7ddca4e29f46bc5139131037ad628cc10936764282d6753"},
- {file = "ujson-5.10.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0111b27f2d5c820e7f2dbad7d48e3338c824e7ac4d2a12da3dc6061cc39c8e6"},
- {file = "ujson-5.10.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:c66962ca7565605b355a9ed478292da628b8f18c0f2793021ca4425abf8b01e5"},
- {file = "ujson-5.10.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ba43cc34cce49cf2d4bc76401a754a81202d8aa926d0e2b79f0ee258cb15d3a4"},
- {file = "ujson-5.10.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:ac56eb983edce27e7f51d05bc8dd820586c6e6be1c5216a6809b0c668bb312b8"},
- {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44bd4b23a0e723bf8b10628288c2c7c335161d6840013d4d5de20e48551773b"},
- {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c10f4654e5326ec14a46bcdeb2b685d4ada6911050aa8baaf3501e57024b804"},
- {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0de4971a89a762398006e844ae394bd46991f7c385d7a6a3b93ba229e6dac17e"},
- {file = "ujson-5.10.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e1402f0564a97d2a52310ae10a64d25bcef94f8dd643fcf5d310219d915484f7"},
- {file = "ujson-5.10.0.tar.gz", hash = "sha256:b3cd8f3c5d8c7738257f1018880444f7b7d9b66232c64649f562d7ba86ad4bc1"},
-]
-
[[package]]
name = "urllib3"
version = "1.26.18"
@@ -4462,6 +4198,7 @@ cassandra = []
chroma = ["chromadb-client"]
clickhouse = ["clickhouse-driver"]
cockroachdb = []
+cosmosdb = ["azure-cosmos"]
elasticsearch = []
generic = ["httpx"]
google = ["google-cloud-datastore", "google-cloud-pubsub"]
@@ -4471,16 +4208,13 @@ kafka = []
keycloak = ["python-keycloak"]
localstack = ["boto3"]
memcached = []
-milvus = []
minio = ["minio"]
mongodb = ["pymongo"]
-mqtt = []
mssql = ["pymssql", "sqlalchemy"]
mysql = ["pymysql", "sqlalchemy"]
nats = ["nats-py"]
neo4j = ["neo4j"]
nginx = []
-ollama = []
opensearch = ["opensearch-py"]
oracle = ["oracledb", "sqlalchemy"]
oracle-free = ["oracledb", "sqlalchemy"]
@@ -4497,4 +4231,4 @@ weaviate = ["weaviate-client"]
[metadata]
lock-version = "2.0"
python-versions = ">=3.9,<4.0"
-content-hash = "e07f8edf8cefba872bbf48dcfa187163cefb00a60122daa62de8891b61fc55de"
+content-hash = "4936c8e58fd7f323d1e4d085607f39e418393fc704bcf3d92f33b1fc15385a3f"
From f9508c884fca47d37533cba77cbe6fde3dd26367 Mon Sep 17 00:00:00 2001
From: Mehdi BEN ABDALLAH <@mbenabda>
Date: Fri, 24 May 2024 23:09:38 +0200
Subject: [PATCH 05/15] move configuration steps to _configure
---
.../testcontainers/cosmosdb/__init__.py | 24 +++++++++----------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/modules/cosmosdb/testcontainers/cosmosdb/__init__.py b/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
index bc8528c3b..746b84040 100644
--- a/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
+++ b/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
@@ -71,22 +71,11 @@ def __init__(
**docker_client_kw,
):
super().__init__(image=image, **docker_client_kw)
-
self.partition_count = partition_count
self.key = key
self.enable_data_persistence = enable_data_persistence
self.endpoints = frozenset(endpoints)
-
- self.with_bind_ports(EMULATOR_PORT, EMULATOR_PORT)
-
- endpoints_ports = []
- for endpoint in self.endpoints:
- endpoints_ports.extend(endpoint_ports[endpoint])
-
- if bind_ports:
- [ self.with_bind_ports(port, port) for port in endpoints_ports ]
- else:
- self.with_exposed_ports(*endpoints_ports)
+ self.bind_ports = bind_ports
def start(self) -> Self:
self._configure()
@@ -122,6 +111,17 @@ def sync_client(self) -> SyncCosmosClient:
return SyncCosmosClient(url=self.url, credential=self.key, connection_verify=False)
def _configure(self) -> None:
+ self.with_bind_ports(EMULATOR_PORT, EMULATOR_PORT)
+
+ endpoints_ports = []
+ for endpoint in self.endpoints:
+ endpoints_ports.extend(endpoint_ports[endpoint])
+
+ if self.bind_ports:
+ [ self.with_bind_ports(port, port) for port in endpoints_ports ]
+ else:
+ self.with_exposed_ports(*endpoints_ports)
+
(
self
.with_env("AZURE_COSMOS_EMULATOR_PARTITION_COUNT", str(self.partition_count))
From a2ce740335a3d218a4a2b6bb616729f994ca487f Mon Sep 17 00:00:00 2001
From: David Ankin
Date: Fri, 24 May 2024 17:40:40 -0400
Subject: [PATCH 06/15] linting
---
.../testcontainers/cosmosdb/__init__.py | 284 +++++++++---------
modules/cosmosdb/tests/test_cosmosdb.py | 5 +-
2 files changed, 150 insertions(+), 139 deletions(-)
diff --git a/modules/cosmosdb/testcontainers/cosmosdb/__init__.py b/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
index 746b84040..f43fb6dff 100644
--- a/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
+++ b/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
@@ -1,159 +1,169 @@
-from testcontainers.core.container import DockerContainer
-from testcontainers.core.waiting_utils import wait_for_logs, wait_container_is_ready
import os
-import ssl
import socket
-from typing import Iterable, Callable
-from typing_extensions import Self
+import ssl
+from collections.abc import Iterable
+from enum import Enum, auto
+from typing import Callable
+from urllib.error import HTTPError, URLError
+from urllib.request import urlopen
+
+from azure.core.exceptions import ServiceRequestError
from azure.cosmos import CosmosClient as SyncCosmosClient
from azure.cosmos.aio import CosmosClient as AsyncCosmosClient
-from azure.core.exceptions import ServiceRequestError
-
-from urllib.request import urlopen
-from urllib.error import HTTPError, URLError
+from typing_extensions import Self
-from enum import Enum, auto
+from testcontainers.core.container import DockerContainer
+from testcontainers.core.waiting_utils import wait_container_is_ready, wait_for_logs
__all__ = ["CosmosDBEmulatorContainer", "Endpoints"]
+
class Endpoints(Enum):
- Direct = auto()
- Gremlin = auto()
- Table = auto()
- MongoDB = auto()
- Cassandra = auto()
+ Direct = auto()
+ Gremlin = auto()
+ Table = auto()
+ MongoDB = auto()
+ Cassandra = auto()
+
-ALL_ENDPOINTS = { e for e in Endpoints }
+ALL_ENDPOINTS = set(Endpoints)
# Ports mostly derived from https://docs.microsoft.com/en-us/azure/cosmos-db/emulator-command-line-parameters
EMULATOR_PORT = 8081
endpoint_ports = {
- Endpoints.Direct : frozenset([10251, 10252, 10253, 10254]),
- Endpoints.Gremlin : frozenset([8901]),
- Endpoints.Table : frozenset([8902]),
- Endpoints.MongoDB : frozenset([10255]),
- Endpoints.Cassandra: frozenset([10350]),
+ Endpoints.Direct: frozenset([10251, 10252, 10253, 10254]),
+ Endpoints.Gremlin: frozenset([8901]),
+ Endpoints.Table: frozenset([8902]),
+ Endpoints.MongoDB: frozenset([10255]),
+ Endpoints.Cassandra: frozenset([10350]),
}
+
def is_truthy_string(s: str):
- return s.lower().strip() in {"true", "yes", "y", "1"}
+ return s.lower().strip() in {"true", "yes", "y", "1"}
+
class CosmosDBEmulatorContainer(DockerContainer):
- """
+ """
CosmosDB Emulator container.
Example:
-
- .. doctest::
- >>> from testcontainers.cosmosdb import CosmosDBEmulatorContainer
- >>> with CosmosDBEmulatorContainer() as cosmosdb:
- ... db = cosmosdb.sync_client().create_database_if_not_exists("test")
-
- .. doctest::
- >>> from testcontainers.cosmosdb import CosmosDBEmulatorContainer
- >>> with CosmosDBEmulatorContainer() as emulator:
- ... cosmosdb = CosmosClient(url=emulator.url, credential=emulator.key, connection_verify=False)
- ... db = cosmosdb.create_database_if_not_exists("test")
-
- .. doctest::
- >>> from testcontainers.cosmosdb import CosmosDBEmulatorContainer, Endpoints
- >>> with CosmosDBEmulatorContainer(endpoints=[Endpoints.MongoDB]) as emulator:
- ... print(f"Point yout MongoDB client to {emulator.host}:{emulator.ports(Endpoints.MongoDB)[0]}")
- """
- def __init__(
- self,
- image: str = os.getenv("AZURE_COSMOS_EMULATOR_IMAGE", "mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest"),
- partition_count: int = os.getenv("AZURE_COSMOS_EMULATOR_PARTITION_COUNT", None),
- enable_data_persistence: bool = is_truthy_string(os.getenv("AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE", "false")),
- bind_ports: bool = is_truthy_string(os.getenv("AZURE_COSMOS_EMULATOR_BIND_PORTS", "true")),
- key: str = os.getenv("AZURE_COSMOS_EMULATOR_KEY", "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="),
- endpoints: Iterable[Endpoints] = [], # the emulator image does not support host-container port mapping
- **docker_client_kw,
- ):
- super().__init__(image=image, **docker_client_kw)
- self.partition_count = partition_count
- self.key = key
- self.enable_data_persistence = enable_data_persistence
- self.endpoints = frozenset(endpoints)
- self.bind_ports = bind_ports
-
- def start(self) -> Self:
- self._configure()
- super().start()
- self._wait_until_ready()
- return self
-
- @property
- def url(self) -> str:
- """
- Returns the url to interact with the emulator
- """
- return f"https://{self.host}:{self.get_exposed_port(EMULATOR_PORT)}"
-
- @property
- def host(self) -> str:
- return self.get_container_host_ip()
-
- def ports(self, endpoint: Endpoints) -> Iterable[int]:
- assert endpoint in self.endpoints, f"Endpoint {endpoint} is not exposed"
- return { self.get_exposed_port(p) for p in endpoint_ports[endpoint] }
-
- def async_client(self) -> AsyncCosmosClient:
- """
- Returns an asynchronous CosmosClient instance to interact with the CosmosDB server
- """
- return AsyncCosmosClient(url=self.url, credential=self.key, connection_verify=False)
-
- def sync_client(self) -> SyncCosmosClient:
- """
- Returns a synchronous CosmosClient instance to interact with the CosmosDB server
- """
- return SyncCosmosClient(url=self.url, credential=self.key, connection_verify=False)
-
- def _configure(self) -> None:
- self.with_bind_ports(EMULATOR_PORT, EMULATOR_PORT)
-
- endpoints_ports = []
- for endpoint in self.endpoints:
- endpoints_ports.extend(endpoint_ports[endpoint])
-
- if self.bind_ports:
- [ self.with_bind_ports(port, port) for port in endpoints_ports ]
- else:
- self.with_exposed_ports(*endpoints_ports)
-
- (
- self
- .with_env("AZURE_COSMOS_EMULATOR_PARTITION_COUNT", str(self.partition_count))
- .with_env("AZURE_COSMOS_EMULATOR_IP_ADDRESS_OVERRIDE", socket.gethostbyname(socket.gethostname()))
- .with_env("AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE", str(self.enable_data_persistence))
- .with_env("AZURE_COSMOS_EMULATOR_KEY", str(self.key))
- )
-
- def _wait_until_ready(self) -> Self:
- """
- Waits until the CosmosDB Emulator image is ready to be used.
- """
- (
- self
- ._wait_for_logs(container=self, predicate="Started\\s*$")
- ._wait_for_url(f"{self.url}/_explorer/index.html")
- ._wait_for_query_success(lambda sync_client: list(sync_client.list_databases()))
- )
- return self
-
- @wait_container_is_ready(HTTPError, URLError)
- def _wait_for_url(self, url: str) -> Self:
- with urlopen(url, context=ssl._create_unverified_context()) as response:
- response.read()
- return self
-
- def _wait_for_logs(self, *args, **kwargs) -> Self:
- wait_for_logs(*args, **kwargs)
- return self
-
- @wait_container_is_ready(ServiceRequestError)
- def _wait_for_query_success(self, query: Callable[[SyncCosmosClient], None]) -> Self:
- with self.sync_client() as c:
- query(c)
- return self
+ .. doctest::
+ >>> from testcontainers.cosmosdb import CosmosDBEmulatorContainer
+ >>> with CosmosDBEmulatorContainer() as cosmosdb:
+ ... db = cosmosdb.sync_client().create_database_if_not_exists("test")
+
+ .. doctest::
+ >>> from testcontainers.cosmosdb import CosmosDBEmulatorContainer
+ >>> with CosmosDBEmulatorContainer() as emulator:
+ ... cosmosdb = CosmosClient(url=emulator.url, credential=emulator.key, connection_verify=False)
+ ... db = cosmosdb.create_database_if_not_exists("test")
+
+ .. doctest::
+ >>> from testcontainers.cosmosdb import CosmosDBEmulatorContainer, Endpoints
+ >>> with CosmosDBEmulatorContainer(endpoints=[Endpoints.MongoDB]) as emulator:
+ ... print(f"Point yout MongoDB client to {emulator.host}:{emulator.ports(Endpoints.MongoDB)[0]}")
+ """
+
+ def __init__(
+ self,
+ image: str = os.getenv(
+ "AZURE_COSMOS_EMULATOR_IMAGE", "mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest"
+ ),
+ partition_count: int = os.getenv("AZURE_COSMOS_EMULATOR_PARTITION_COUNT", None),
+ enable_data_persistence: bool = is_truthy_string(
+ os.getenv("AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE", "false")
+ ),
+ bind_ports: bool = is_truthy_string(os.getenv("AZURE_COSMOS_EMULATOR_BIND_PORTS", "true")),
+ key: str = os.getenv(
+ "AZURE_COSMOS_EMULATOR_KEY",
+ "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
+ ),
+ endpoints: Iterable[Endpoints] = [], # the emulator image does not support host-container port mapping
+ **docker_client_kw,
+ ):
+ super().__init__(image=image, **docker_client_kw)
+ self.partition_count = partition_count
+ self.key = key
+ self.enable_data_persistence = enable_data_persistence
+ self.endpoints = frozenset(endpoints)
+ self.bind_ports = bind_ports
+
+ def start(self) -> Self:
+ self._configure()
+ super().start()
+ self._wait_until_ready()
+ return self
+
+ @property
+ def url(self) -> str:
+ """
+ Returns the url to interact with the emulator
+ """
+ return f"https://{self.host}:{self.get_exposed_port(EMULATOR_PORT)}"
+
+ @property
+ def host(self) -> str:
+ return self.get_container_host_ip()
+
+ def ports(self, endpoint: Endpoints) -> Iterable[int]:
+ assert endpoint in self.endpoints, f"Endpoint {endpoint} is not exposed"
+ return {self.get_exposed_port(p) for p in endpoint_ports[endpoint]}
+
+ def async_client(self) -> AsyncCosmosClient:
+ """
+ Returns an asynchronous CosmosClient instance to interact with the CosmosDB server
+ """
+ return AsyncCosmosClient(url=self.url, credential=self.key, connection_verify=False)
+
+ def sync_client(self) -> SyncCosmosClient:
+ """
+ Returns a synchronous CosmosClient instance to interact with the CosmosDB server
+ """
+ return SyncCosmosClient(url=self.url, credential=self.key, connection_verify=False)
+
+ def _configure(self) -> None:
+ self.with_bind_ports(EMULATOR_PORT, EMULATOR_PORT)
+
+ endpoints_ports = []
+ for endpoint in self.endpoints:
+ endpoints_ports.extend(endpoint_ports[endpoint])
+
+ if self.bind_ports:
+ [self.with_bind_ports(port, port) for port in endpoints_ports]
+ else:
+ self.with_exposed_ports(*endpoints_ports)
+
+ (
+ self.with_env("AZURE_COSMOS_EMULATOR_PARTITION_COUNT", str(self.partition_count))
+ .with_env("AZURE_COSMOS_EMULATOR_IP_ADDRESS_OVERRIDE", socket.gethostbyname(socket.gethostname()))
+ .with_env("AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE", str(self.enable_data_persistence))
+ .with_env("AZURE_COSMOS_EMULATOR_KEY", str(self.key))
+ )
+
+ def _wait_until_ready(self) -> Self:
+ """
+ Waits until the CosmosDB Emulator image is ready to be used.
+ """
+ (
+ self._wait_for_logs(container=self, predicate="Started\\s*$")
+ ._wait_for_url(f"{self.url}/_explorer/index.html")
+ ._wait_for_query_success(lambda sync_client: list(sync_client.list_databases()))
+ )
+ return self
+
+ @wait_container_is_ready(HTTPError, URLError)
+ def _wait_for_url(self, url: str) -> Self:
+ with urlopen(url, context=ssl._create_unverified_context()) as response:
+ response.read()
+ return self
+
+ def _wait_for_logs(self, *args, **kwargs) -> Self:
+ wait_for_logs(*args, **kwargs)
+ return self
+
+ @wait_container_is_ready(ServiceRequestError)
+ def _wait_for_query_success(self, query: Callable[[SyncCosmosClient], None]) -> Self:
+ with self.sync_client() as c:
+ query(c)
+ return self
diff --git a/modules/cosmosdb/tests/test_cosmosdb.py b/modules/cosmosdb/tests/test_cosmosdb.py
index 17cfe257a..36df5fb7c 100644
--- a/modules/cosmosdb/tests/test_cosmosdb.py
+++ b/modules/cosmosdb/tests/test_cosmosdb.py
@@ -1,6 +1,7 @@
import pytest
from testcontainers.cosmosdb import CosmosDBEmulatorContainer
+
def test_docker_run():
- with CosmosDBEmulatorContainer(partition_count=1) as cosmosdb:
- list(cosmosdb.sync_client().list_databases())
+ with CosmosDBEmulatorContainer(partition_count=1) as cosmosdb:
+ list(cosmosdb.sync_client().list_databases())
From 9e5a3670c92d0440d819a314e738e2722912ba2c Mon Sep 17 00:00:00 2001
From: David Ankin
Date: Fri, 24 May 2024 17:52:12 -0400
Subject: [PATCH 07/15] fix make docs - needs to refer to autoclass for enum
since its in docstring
---
modules/cosmosdb/README.rst | 1 +
1 file changed, 1 insertion(+)
diff --git a/modules/cosmosdb/README.rst b/modules/cosmosdb/README.rst
index bf97ac105..aebdabc39 100644
--- a/modules/cosmosdb/README.rst
+++ b/modules/cosmosdb/README.rst
@@ -1,2 +1,3 @@
.. autoclass:: testcontainers.cosmosdb.CosmosDBEmulatorContainer
+.. autoclass:: testcontainers.cosmosdb.Endpoints
.. title:: testcontainers.cosmosdb.CosmosDBEmulatorContainer
From 04f5f5d15a3290d6680b28a9010603322b7baef5 Mon Sep 17 00:00:00 2001
From: Mehdi BEN ABDALLAH <@mbenabda>
Date: Sat, 25 May 2024 15:13:49 +0200
Subject: [PATCH 08/15] actually expose the MongoDB endpoint when requested
---
.../cosmosdb/testcontainers/cosmosdb/__init__.py | 16 +++++++++++-----
modules/cosmosdb/tests/test_cosmosdb.py | 16 +++++++++++++++-
2 files changed, 26 insertions(+), 6 deletions(-)
diff --git a/modules/cosmosdb/testcontainers/cosmosdb/__init__.py b/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
index f43fb6dff..ffed5439f 100644
--- a/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
+++ b/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
@@ -3,7 +3,7 @@
import ssl
from collections.abc import Iterable
from enum import Enum, auto
-from typing import Callable
+from typing import Callable, Optional
from urllib.error import HTTPError, URLError
from urllib.request import urlopen
@@ -26,8 +26,6 @@ class Endpoints(Enum):
Cassandra = auto()
-ALL_ENDPOINTS = set(Endpoints)
-
# Ports mostly derived from https://docs.microsoft.com/en-us/azure/cosmos-db/emulator-command-line-parameters
EMULATOR_PORT = 8081
endpoint_ports = {
@@ -61,8 +59,8 @@ class CosmosDBEmulatorContainer(DockerContainer):
.. doctest::
>>> from testcontainers.cosmosdb import CosmosDBEmulatorContainer, Endpoints
- >>> with CosmosDBEmulatorContainer(endpoints=[Endpoints.MongoDB]) as emulator:
- ... print(f"Point yout MongoDB client to {emulator.host}:{emulator.ports(Endpoints.MongoDB)[0]}")
+ >>> with CosmosDBEmulatorContainer(endpoints=[Endpoints.MongoDB], mongodb_version="4.0") as emulator:
+ ... print(f"Point yout MongoDB client to {emulator.host}:{next(iter(emulator.ports(Endpoints.MongoDB)))}")
"""
def __init__(
@@ -80,6 +78,7 @@ def __init__(
"C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
),
endpoints: Iterable[Endpoints] = [], # the emulator image does not support host-container port mapping
+ mongodb_version: Optional[str] = None,
**docker_client_kw,
):
super().__init__(image=image, **docker_client_kw)
@@ -88,6 +87,10 @@ def __init__(
self.enable_data_persistence = enable_data_persistence
self.endpoints = frozenset(endpoints)
self.bind_ports = bind_ports
+ assert (Endpoints.MongoDB not in self.endpoints) or (
+ mongodb_version is not None
+ ), "A MongoDB version is required to use the MongoDB Endpoint"
+ self.mongodb_version = mongodb_version
def start(self) -> Self:
self._configure()
@@ -141,6 +144,9 @@ def _configure(self) -> None:
.with_env("AZURE_COSMOS_EMULATOR_KEY", str(self.key))
)
+ if Endpoints.MongoDB in self.endpoints:
+ self.with_env("AZURE_COSMOS_EMULATOR_ENABLE_MONGODB_ENDPOINT", self.mongodb_version)
+
def _wait_until_ready(self) -> Self:
"""
Waits until the CosmosDB Emulator image is ready to be used.
diff --git a/modules/cosmosdb/tests/test_cosmosdb.py b/modules/cosmosdb/tests/test_cosmosdb.py
index 36df5fb7c..4a2710ffa 100644
--- a/modules/cosmosdb/tests/test_cosmosdb.py
+++ b/modules/cosmosdb/tests/test_cosmosdb.py
@@ -1,7 +1,21 @@
import pytest
-from testcontainers.cosmosdb import CosmosDBEmulatorContainer
+from testcontainers.cosmosdb import CosmosDBEmulatorContainer, Endpoints
def test_docker_run():
with CosmosDBEmulatorContainer(partition_count=1) as cosmosdb:
list(cosmosdb.sync_client().list_databases())
+
+
+def test_enabling_mondogb_endpoint_requires_a_version():
+ with pytest.raises(AssertionError, match="A MongoDB version is required to use the MongoDB Endpoint"):
+ CosmosDBEmulatorContainer(endpoints=[Endpoints.MongoDB])
+
+ # instanciates
+ CosmosDBEmulatorContainer(endpoints=[Endpoints.MongoDB], mongodb_version="4.0")
+
+
+def test_enables_mondogb_endpoint():
+ with CosmosDBEmulatorContainer(partition_count=1, endpoints=[Endpoints.MongoDB], mongodb_version="4.0") as emulator:
+ assert emulator.env["AZURE_COSMOS_EMULATOR_ENABLE_MONGODB_ENDPOINT"] == "4.0"
+ assert emulator.get_exposed_port(10255) is not None, "The MongoDB endpoint's port should be exposed"
From 09a6b0c542a68423453efa1dc77effe22dcb4df3 Mon Sep 17 00:00:00 2001
From: Mehdi BEN ABDALLAH <@mbenabda>
Date: Sat, 25 May 2024 15:16:27 +0200
Subject: [PATCH 09/15] remove support for endpoints that are not yet supported
in the container release of the emulator
---
modules/cosmosdb/testcontainers/cosmosdb/__init__.py | 8 --------
1 file changed, 8 deletions(-)
diff --git a/modules/cosmosdb/testcontainers/cosmosdb/__init__.py b/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
index ffed5439f..c5b188fa1 100644
--- a/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
+++ b/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
@@ -19,21 +19,13 @@
class Endpoints(Enum):
- Direct = auto()
- Gremlin = auto()
- Table = auto()
MongoDB = auto()
- Cassandra = auto()
# Ports mostly derived from https://docs.microsoft.com/en-us/azure/cosmos-db/emulator-command-line-parameters
EMULATOR_PORT = 8081
endpoint_ports = {
- Endpoints.Direct: frozenset([10251, 10252, 10253, 10254]),
- Endpoints.Gremlin: frozenset([8901]),
- Endpoints.Table: frozenset([8902]),
Endpoints.MongoDB: frozenset([10255]),
- Endpoints.Cassandra: frozenset([10350]),
}
From a7986c533556c9ba9006e204bc792e6f2194b78e Mon Sep 17 00:00:00 2001
From: Mehdi BEN ABDALLAH <@mbenabda>
Date: Sat, 25 May 2024 18:42:59 +0200
Subject: [PATCH 10/15] expose the PEM encoded self signed server certificate
---
.../testcontainers/cosmosdb/__init__.py | 42 +++++++++++++------
modules/cosmosdb/tests/test_cosmosdb.py | 3 +-
2 files changed, 31 insertions(+), 14 deletions(-)
diff --git a/modules/cosmosdb/testcontainers/cosmosdb/__init__.py b/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
index c5b188fa1..7ea41e41f 100644
--- a/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
+++ b/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
@@ -41,7 +41,7 @@ class CosmosDBEmulatorContainer(DockerContainer):
.. doctest::
>>> from testcontainers.cosmosdb import CosmosDBEmulatorContainer
>>> with CosmosDBEmulatorContainer() as cosmosdb:
- ... db = cosmosdb.sync_client().create_database_if_not_exists("test")
+ ... db = cosmosdb.insecure_sync_client().create_database_if_not_exists("test")
.. doctest::
>>> from testcontainers.cosmosdb import CosmosDBEmulatorContainer
@@ -84,16 +84,10 @@ def __init__(
), "A MongoDB version is required to use the MongoDB Endpoint"
self.mongodb_version = mongodb_version
- def start(self) -> Self:
- self._configure()
- super().start()
- self._wait_until_ready()
- return self
-
@property
def url(self) -> str:
"""
- Returns the url to interact with the emulator
+ The url to the CosmosDB server
"""
return f"https://{self.host}:{self.get_exposed_port(EMULATOR_PORT)}"
@@ -101,22 +95,40 @@ def url(self) -> str:
def host(self) -> str:
return self.get_container_host_ip()
+ @property
+ def certificate_pem(self) -> bytes:
+ """
+ PEM-encoded certificate of the CosmosDB server
+ """
+ return self._cert_pem_bytes
+
def ports(self, endpoint: Endpoints) -> Iterable[int]:
+ """
+ Returns the set of exposed ports for a given endpoint.
+ If bind_ports is True, the returned ports will be the NAT-ed ports reachable from the host.
+ """
assert endpoint in self.endpoints, f"Endpoint {endpoint} is not exposed"
return {self.get_exposed_port(p) for p in endpoint_ports[endpoint]}
- def async_client(self) -> AsyncCosmosClient:
+ def insecure_async_client(self) -> AsyncCosmosClient:
"""
- Returns an asynchronous CosmosClient instance to interact with the CosmosDB server
+ Returns an asynchronous CosmosClient instance
"""
return AsyncCosmosClient(url=self.url, credential=self.key, connection_verify=False)
- def sync_client(self) -> SyncCosmosClient:
+ def insecure_sync_client(self) -> SyncCosmosClient:
"""
- Returns a synchronous CosmosClient instance to interact with the CosmosDB server
+ Returns a synchronous CosmosClient instance
"""
return SyncCosmosClient(url=self.url, credential=self.key, connection_verify=False)
+ def start(self) -> Self:
+ self._configure()
+ super().start()
+ self._wait_until_ready()
+ self._cert_pem_bytes = self._download_cert()
+ return self
+
def _configure(self) -> None:
self.with_bind_ports(EMULATOR_PORT, EMULATOR_PORT)
@@ -162,6 +174,10 @@ def _wait_for_logs(self, *args, **kwargs) -> Self:
@wait_container_is_ready(ServiceRequestError)
def _wait_for_query_success(self, query: Callable[[SyncCosmosClient], None]) -> Self:
- with self.sync_client() as c:
+ with self.insecure_sync_client() as c:
query(c)
return self
+
+ def _download_cert(self) -> bytes:
+ with urlopen(f"{self.url}/_explorer/emulator.pem", context=ssl._create_unverified_context()) as response:
+ return response.read()
diff --git a/modules/cosmosdb/tests/test_cosmosdb.py b/modules/cosmosdb/tests/test_cosmosdb.py
index 4a2710ffa..cd9b93472 100644
--- a/modules/cosmosdb/tests/test_cosmosdb.py
+++ b/modules/cosmosdb/tests/test_cosmosdb.py
@@ -4,7 +4,8 @@
def test_docker_run():
with CosmosDBEmulatorContainer(partition_count=1) as cosmosdb:
- list(cosmosdb.sync_client().list_databases())
+ list(cosmosdb.insecure_sync_client().list_databases())
+ assert cosmosdb.certificate_pem is not None
def test_enabling_mondogb_endpoint_requires_a_version():
From e462cbfcbf1212c8c3f9eb9718bc07d868321e1b Mon Sep 17 00:00:00 2001
From: Mehdi BEN ABDALLAH <@mbenabda>
Date: Sat, 25 May 2024 23:16:38 +0200
Subject: [PATCH 11/15] wip
---
modules/cosmosdb/README.rst | 4 +-
.../testcontainers/cosmosdb/__init__.py | 185 +-----------------
.../testcontainers/cosmosdb/_emulator.py | 102 ++++++++++
.../cosmosdb/testcontainers/cosmosdb/_grab.py | 25 +++
.../testcontainers/cosmosdb/mongodb.py | 38 ++++
.../cosmosdb/testcontainers/cosmosdb/nosql.py | 61 ++++++
modules/cosmosdb/tests/test_cosmosdb.py | 22 ---
modules/cosmosdb/tests/test_emulator.py | 7 +
modules/cosmosdb/tests/test_mongodb.py | 14 ++
modules/cosmosdb/tests/test_nosql.py | 6 +
10 files changed, 258 insertions(+), 206 deletions(-)
create mode 100644 modules/cosmosdb/testcontainers/cosmosdb/_emulator.py
create mode 100644 modules/cosmosdb/testcontainers/cosmosdb/_grab.py
create mode 100644 modules/cosmosdb/testcontainers/cosmosdb/mongodb.py
create mode 100644 modules/cosmosdb/testcontainers/cosmosdb/nosql.py
delete mode 100644 modules/cosmosdb/tests/test_cosmosdb.py
create mode 100644 modules/cosmosdb/tests/test_emulator.py
create mode 100644 modules/cosmosdb/tests/test_mongodb.py
create mode 100644 modules/cosmosdb/tests/test_nosql.py
diff --git a/modules/cosmosdb/README.rst b/modules/cosmosdb/README.rst
index aebdabc39..94e0134fa 100644
--- a/modules/cosmosdb/README.rst
+++ b/modules/cosmosdb/README.rst
@@ -1,3 +1,3 @@
-.. autoclass:: testcontainers.cosmosdb.CosmosDBEmulatorContainer
-.. autoclass:: testcontainers.cosmosdb.Endpoints
+.. autoclass:: testcontainers.cosmosdb.MongoDBEmulatorContainer
+.. autoclass:: testcontainers.cosmosdb.NoSQLEmulatorContainer
.. title:: testcontainers.cosmosdb.CosmosDBEmulatorContainer
diff --git a/modules/cosmosdb/testcontainers/cosmosdb/__init__.py b/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
index 7ea41e41f..ab27eaa1a 100644
--- a/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
+++ b/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
@@ -1,183 +1,4 @@
-import os
-import socket
-import ssl
-from collections.abc import Iterable
-from enum import Enum, auto
-from typing import Callable, Optional
-from urllib.error import HTTPError, URLError
-from urllib.request import urlopen
+from .mongodb import MongoDBEmulatorContainer
+from .nosql import NoSQLEmulatorContainer
-from azure.core.exceptions import ServiceRequestError
-from azure.cosmos import CosmosClient as SyncCosmosClient
-from azure.cosmos.aio import CosmosClient as AsyncCosmosClient
-from typing_extensions import Self
-
-from testcontainers.core.container import DockerContainer
-from testcontainers.core.waiting_utils import wait_container_is_ready, wait_for_logs
-
-__all__ = ["CosmosDBEmulatorContainer", "Endpoints"]
-
-
-class Endpoints(Enum):
- MongoDB = auto()
-
-
-# Ports mostly derived from https://docs.microsoft.com/en-us/azure/cosmos-db/emulator-command-line-parameters
-EMULATOR_PORT = 8081
-endpoint_ports = {
- Endpoints.MongoDB: frozenset([10255]),
-}
-
-
-def is_truthy_string(s: str):
- return s.lower().strip() in {"true", "yes", "y", "1"}
-
-
-class CosmosDBEmulatorContainer(DockerContainer):
- """
- CosmosDB Emulator container.
-
- Example:
- .. doctest::
- >>> from testcontainers.cosmosdb import CosmosDBEmulatorContainer
- >>> with CosmosDBEmulatorContainer() as cosmosdb:
- ... db = cosmosdb.insecure_sync_client().create_database_if_not_exists("test")
-
- .. doctest::
- >>> from testcontainers.cosmosdb import CosmosDBEmulatorContainer
- >>> with CosmosDBEmulatorContainer() as emulator:
- ... cosmosdb = CosmosClient(url=emulator.url, credential=emulator.key, connection_verify=False)
- ... db = cosmosdb.create_database_if_not_exists("test")
-
- .. doctest::
- >>> from testcontainers.cosmosdb import CosmosDBEmulatorContainer, Endpoints
- >>> with CosmosDBEmulatorContainer(endpoints=[Endpoints.MongoDB], mongodb_version="4.0") as emulator:
- ... print(f"Point yout MongoDB client to {emulator.host}:{next(iter(emulator.ports(Endpoints.MongoDB)))}")
- """
-
- def __init__(
- self,
- image: str = os.getenv(
- "AZURE_COSMOS_EMULATOR_IMAGE", "mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest"
- ),
- partition_count: int = os.getenv("AZURE_COSMOS_EMULATOR_PARTITION_COUNT", None),
- enable_data_persistence: bool = is_truthy_string(
- os.getenv("AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE", "false")
- ),
- bind_ports: bool = is_truthy_string(os.getenv("AZURE_COSMOS_EMULATOR_BIND_PORTS", "true")),
- key: str = os.getenv(
- "AZURE_COSMOS_EMULATOR_KEY",
- "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
- ),
- endpoints: Iterable[Endpoints] = [], # the emulator image does not support host-container port mapping
- mongodb_version: Optional[str] = None,
- **docker_client_kw,
- ):
- super().__init__(image=image, **docker_client_kw)
- self.partition_count = partition_count
- self.key = key
- self.enable_data_persistence = enable_data_persistence
- self.endpoints = frozenset(endpoints)
- self.bind_ports = bind_ports
- assert (Endpoints.MongoDB not in self.endpoints) or (
- mongodb_version is not None
- ), "A MongoDB version is required to use the MongoDB Endpoint"
- self.mongodb_version = mongodb_version
-
- @property
- def url(self) -> str:
- """
- The url to the CosmosDB server
- """
- return f"https://{self.host}:{self.get_exposed_port(EMULATOR_PORT)}"
-
- @property
- def host(self) -> str:
- return self.get_container_host_ip()
-
- @property
- def certificate_pem(self) -> bytes:
- """
- PEM-encoded certificate of the CosmosDB server
- """
- return self._cert_pem_bytes
-
- def ports(self, endpoint: Endpoints) -> Iterable[int]:
- """
- Returns the set of exposed ports for a given endpoint.
- If bind_ports is True, the returned ports will be the NAT-ed ports reachable from the host.
- """
- assert endpoint in self.endpoints, f"Endpoint {endpoint} is not exposed"
- return {self.get_exposed_port(p) for p in endpoint_ports[endpoint]}
-
- def insecure_async_client(self) -> AsyncCosmosClient:
- """
- Returns an asynchronous CosmosClient instance
- """
- return AsyncCosmosClient(url=self.url, credential=self.key, connection_verify=False)
-
- def insecure_sync_client(self) -> SyncCosmosClient:
- """
- Returns a synchronous CosmosClient instance
- """
- return SyncCosmosClient(url=self.url, credential=self.key, connection_verify=False)
-
- def start(self) -> Self:
- self._configure()
- super().start()
- self._wait_until_ready()
- self._cert_pem_bytes = self._download_cert()
- return self
-
- def _configure(self) -> None:
- self.with_bind_ports(EMULATOR_PORT, EMULATOR_PORT)
-
- endpoints_ports = []
- for endpoint in self.endpoints:
- endpoints_ports.extend(endpoint_ports[endpoint])
-
- if self.bind_ports:
- [self.with_bind_ports(port, port) for port in endpoints_ports]
- else:
- self.with_exposed_ports(*endpoints_ports)
-
- (
- self.with_env("AZURE_COSMOS_EMULATOR_PARTITION_COUNT", str(self.partition_count))
- .with_env("AZURE_COSMOS_EMULATOR_IP_ADDRESS_OVERRIDE", socket.gethostbyname(socket.gethostname()))
- .with_env("AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE", str(self.enable_data_persistence))
- .with_env("AZURE_COSMOS_EMULATOR_KEY", str(self.key))
- )
-
- if Endpoints.MongoDB in self.endpoints:
- self.with_env("AZURE_COSMOS_EMULATOR_ENABLE_MONGODB_ENDPOINT", self.mongodb_version)
-
- def _wait_until_ready(self) -> Self:
- """
- Waits until the CosmosDB Emulator image is ready to be used.
- """
- (
- self._wait_for_logs(container=self, predicate="Started\\s*$")
- ._wait_for_url(f"{self.url}/_explorer/index.html")
- ._wait_for_query_success(lambda sync_client: list(sync_client.list_databases()))
- )
- return self
-
- @wait_container_is_ready(HTTPError, URLError)
- def _wait_for_url(self, url: str) -> Self:
- with urlopen(url, context=ssl._create_unverified_context()) as response:
- response.read()
- return self
-
- def _wait_for_logs(self, *args, **kwargs) -> Self:
- wait_for_logs(*args, **kwargs)
- return self
-
- @wait_container_is_ready(ServiceRequestError)
- def _wait_for_query_success(self, query: Callable[[SyncCosmosClient], None]) -> Self:
- with self.insecure_sync_client() as c:
- query(c)
- return self
-
- def _download_cert(self) -> bytes:
- with urlopen(f"{self.url}/_explorer/emulator.pem", context=ssl._create_unverified_context()) as response:
- return response.read()
+__all__ = ["MongoDBEmulatorContainer", "NoSQLEmulatorContainer"]
diff --git a/modules/cosmosdb/testcontainers/cosmosdb/_emulator.py b/modules/cosmosdb/testcontainers/cosmosdb/_emulator.py
new file mode 100644
index 000000000..a6608b814
--- /dev/null
+++ b/modules/cosmosdb/testcontainers/cosmosdb/_emulator.py
@@ -0,0 +1,102 @@
+import os
+import socket
+import ssl
+from collections.abc import Iterable
+from typing_extensions import Self
+from testcontainers.core.container import DockerContainer
+from testcontainers.core.waiting_utils import wait_container_is_ready, wait_for_logs
+from . import _grab as grab
+from distutils.util import strtobool
+from urllib.error import HTTPError, URLError
+from urllib.request import urlopen
+
+__all__ = ["CosmosDBEmulatorContainer"]
+
+EMULATOR_PORT = 8081
+
+class CosmosDBEmulatorContainer(DockerContainer):
+ """
+ CosmosDB Emulator container.
+ """
+
+ def __init__(
+ self,
+ image: str = os.getenv(
+ "AZURE_COSMOS_EMULATOR_IMAGE", "mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest"
+ ),
+ partition_count: int = os.getenv("AZURE_COSMOS_EMULATOR_PARTITION_COUNT", None),
+ enable_data_persistence: bool = strtobool(
+ os.getenv("AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE", "false")
+ ),
+ key: str = os.getenv(
+ "AZURE_COSMOS_EMULATOR_KEY",
+ "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
+ ),
+ bind_ports: bool = strtobool(os.getenv("AZURE_COSMOS_EMULATOR_BIND_PORTS", "true")),
+ endpoint_ports: Iterable[int] = [],
+ **other_kwargs,
+ ):
+ super().__init__(image=image, **other_kwargs)
+ self.endpoint_ports = endpoint_ports
+ self.partition_count = partition_count
+ self.key = key
+ self.enable_data_persistence = enable_data_persistence
+ self.bind_ports = bind_ports
+
+ @property
+ def host(self) -> str:
+ return self.get_container_host_ip()
+
+ @property
+ def server_certificate_pem(self) -> bytes:
+ """
+ PEM-encoded server certificate
+ """
+ return self._cert_pem_bytes
+
+ def start(self) -> Self:
+ self._configure()
+ super().start()
+ self._wait_until_ready()
+ self._cert_pem_bytes = self._download_cert()
+ return self
+
+ def _configure(self) -> None:
+ all_ports = set([EMULATOR_PORT] + self.endpoint_ports)
+ if self.bind_ports:
+ for port in all_ports:
+ self.with_bind_ports(port, port)
+ else:
+ self.with_exposed_ports(*all_ports)
+
+ (
+ self
+ .with_env("AZURE_COSMOS_EMULATOR_PARTITION_COUNT", str(self.partition_count))
+ .with_env("AZURE_COSMOS_EMULATOR_IP_ADDRESS_OVERRIDE", socket.gethostbyname(socket.gethostname()))
+ .with_env("AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE", str(self.enable_data_persistence))
+ .with_env("AZURE_COSMOS_EMULATOR_KEY", str(self.key))
+ )
+
+ def _wait_until_ready(self) -> Self:
+ wait_for_logs(container=self, predicate="Started\\s*$")
+
+ if self.bind_ports:
+ self._wait_for_url(f"https://{self.host}:{EMULATOR_PORT}/_explorer/index.html")
+ self._wait_for_query_success()
+
+ return self
+
+ def _download_cert(self) -> bytes:
+ with grab.file(
+ self._container, "/tmp/cosmos/appdata/.system/profiles/Client/AppData/Local/CosmosDBEmulator/emulator.pem"
+ ) as cert:
+ return cert.read()
+
+ @wait_container_is_ready(HTTPError, URLError)
+ def _wait_for_url(self, url: str) -> Self:
+ with urlopen(url, context=ssl._create_unverified_context()) as response:
+ response.read()
+ return self
+
+ def _wait_for_query_success(self) -> None:
+ pass
diff --git a/modules/cosmosdb/testcontainers/cosmosdb/_grab.py b/modules/cosmosdb/testcontainers/cosmosdb/_grab.py
new file mode 100644
index 000000000..cbf74a06b
--- /dev/null
+++ b/modules/cosmosdb/testcontainers/cosmosdb/_grab.py
@@ -0,0 +1,25 @@
+
+from pathlib import Path
+from os import path
+import tarfile
+import tempfile
+from contextlib import contextmanager
+
+@contextmanager
+def file(container, target):
+ target_path = Path(target)
+ assert target_path.is_absolute(), "target must be an absolute path"
+
+ with tempfile.TemporaryDirectory() as tmpdirname:
+ archive = Path(tmpdirname) / 'grabbed.tar'
+
+ # download from container as tar archive
+ with open(archive, 'wb') as f:
+ tar_bits, _ = container.get_archive(target)
+ for chunk in tar_bits:
+ f.write(chunk)
+
+ # extract target file from tar archive
+ with tarfile.TarFile(archive) as tar:
+ yield tar.extractfile(path.basename(target))
+
diff --git a/modules/cosmosdb/testcontainers/cosmosdb/mongodb.py b/modules/cosmosdb/testcontainers/cosmosdb/mongodb.py
new file mode 100644
index 000000000..3351604f1
--- /dev/null
+++ b/modules/cosmosdb/testcontainers/cosmosdb/mongodb.py
@@ -0,0 +1,38 @@
+import os
+from ._emulator import CosmosDBEmulatorContainer
+
+__all__ = ["MongoDBEmulatorContainer"]
+
+ENDPOINT_PORT = 10255
+
+class MongoDBEmulatorContainer(CosmosDBEmulatorContainer):
+ """
+ CosmosDB MongoDB enpoint Emulator.
+
+ Example:
+
+ .. doctest::
+ >>> from testcontainers.cosmosdb import MongoDBEmulatorContainer
+ >>> with CosmosDBEmulatorContainer(mongodb_version="4.0") as emulator:
+ ... print(f"Point yout MongoDB client to {emulator.host}:{emulator.port}}")
+ """
+
+ def __init__(
+ self,
+ mongodb_version: str = None,
+ image: str = os.getenv(
+ "AZURE_COSMOS_EMULATOR_IMAGE", "mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:mongodb"
+ ),
+ **other_kwargs,
+ ):
+ super().__init__(image=image, endpoint_ports=[ENDPOINT_PORT], **other_kwargs)
+ assert mongodb_version is not None, "A MongoDB version is required to use the MongoDB Endpoint"
+ self.mongodb_version = mongodb_version
+
+ @property
+ def port(self) -> str:
+ return self.get_exposed_port(ENDPOINT_PORT)
+
+ def _configure(self) -> None:
+ super()._configure()
+ self.with_env("AZURE_COSMOS_EMULATOR_ENABLE_MONGODB_ENDPOINT", self.mongodb_version)
diff --git a/modules/cosmosdb/testcontainers/cosmosdb/nosql.py b/modules/cosmosdb/testcontainers/cosmosdb/nosql.py
new file mode 100644
index 000000000..b8cb8cd51
--- /dev/null
+++ b/modules/cosmosdb/testcontainers/cosmosdb/nosql.py
@@ -0,0 +1,61 @@
+from azure.core.exceptions import ServiceRequestError
+from azure.cosmos import CosmosClient as SyncCosmosClient
+from azure.cosmos.aio import CosmosClient as AsyncCosmosClient
+from testcontainers.core.waiting_utils import wait_container_is_ready
+
+from ._emulator import CosmosDBEmulatorContainer
+__all__ = ["NoSQLEmulatorContainer"]
+
+NOSQL_PORT = 8081
+
+class NoSQLEmulatorContainer(CosmosDBEmulatorContainer):
+ """
+ CosmosDB NoSQL enpoint Emulator.
+
+ Example:
+ .. doctest::
+ >>> from testcontainers.cosmosdb import NoSQLEmulatorContainer
+ >>> with NoSQLEmulatorContainer() as emulator:
+ ... db = emulator.insecure_sync_client().create_database_if_not_exists("test")
+
+ .. doctest::
+ >>> from testcontainers.cosmosdb import NoSQLEmulatorContainer
+ >>> with NoSQLEmulatorContainer() as emulator:
+ ... client = CosmosClient(url=emulator.url, credential=emulator.key, connection_verify=False)
+ ... db = client.create_database_if_not_exists("test")
+
+ """
+
+ def __init__(self, **kwargs):
+ super().__init__(endpoint_ports=[NOSQL_PORT], **kwargs)
+
+ @property
+ def port(self) -> str:
+ """
+ The exposed port to the NoSQL endpoint
+ """
+ return self.get_exposed_port(NOSQL_PORT)
+
+ @property
+ def url(self) -> str:
+ """
+ The url to the NoSQL endpoint
+ """
+ return f"https://{self.host}:{self.port}"
+
+ def insecure_async_client(self) -> AsyncCosmosClient:
+ """
+ Returns an asynchronous CosmosClient instance
+ """
+ return AsyncCosmosClient(url=self.url, credential=self.key, connection_verify=False)
+
+ def insecure_sync_client(self) -> SyncCosmosClient:
+ """
+ Returns a synchronous CosmosClient instance
+ """
+ return SyncCosmosClient(url=self.url, credential=self.key, connection_verify=False)
+
+ @wait_container_is_ready(ServiceRequestError)
+ def _wait_for_query_success(self) -> None:
+ with self.insecure_sync_client() as c:
+ list(c.list_databases())
diff --git a/modules/cosmosdb/tests/test_cosmosdb.py b/modules/cosmosdb/tests/test_cosmosdb.py
deleted file mode 100644
index cd9b93472..000000000
--- a/modules/cosmosdb/tests/test_cosmosdb.py
+++ /dev/null
@@ -1,22 +0,0 @@
-import pytest
-from testcontainers.cosmosdb import CosmosDBEmulatorContainer, Endpoints
-
-
-def test_docker_run():
- with CosmosDBEmulatorContainer(partition_count=1) as cosmosdb:
- list(cosmosdb.insecure_sync_client().list_databases())
- assert cosmosdb.certificate_pem is not None
-
-
-def test_enabling_mondogb_endpoint_requires_a_version():
- with pytest.raises(AssertionError, match="A MongoDB version is required to use the MongoDB Endpoint"):
- CosmosDBEmulatorContainer(endpoints=[Endpoints.MongoDB])
-
- # instanciates
- CosmosDBEmulatorContainer(endpoints=[Endpoints.MongoDB], mongodb_version="4.0")
-
-
-def test_enables_mondogb_endpoint():
- with CosmosDBEmulatorContainer(partition_count=1, endpoints=[Endpoints.MongoDB], mongodb_version="4.0") as emulator:
- assert emulator.env["AZURE_COSMOS_EMULATOR_ENABLE_MONGODB_ENDPOINT"] == "4.0"
- assert emulator.get_exposed_port(10255) is not None, "The MongoDB endpoint's port should be exposed"
diff --git a/modules/cosmosdb/tests/test_emulator.py b/modules/cosmosdb/tests/test_emulator.py
new file mode 100644
index 000000000..6ace57a07
--- /dev/null
+++ b/modules/cosmosdb/tests/test_emulator.py
@@ -0,0 +1,7 @@
+import pytest
+from testcontainers.cosmosdb._emulator import CosmosDBEmulatorContainer
+
+def test_runs():
+ with CosmosDBEmulatorContainer(partition_count=1, bind_ports=False) as emulator:
+ assert emulator.server_certificate_pem is not None
+ assert emulator.get_exposed_port(8081) is not None
diff --git a/modules/cosmosdb/tests/test_mongodb.py b/modules/cosmosdb/tests/test_mongodb.py
new file mode 100644
index 000000000..a321d128f
--- /dev/null
+++ b/modules/cosmosdb/tests/test_mongodb.py
@@ -0,0 +1,14 @@
+import pytest
+from testcontainers.cosmosdb import MongoDBEmulatorContainer
+
+def test_requires_a_version():
+ with pytest.raises(AssertionError, match="A MongoDB version is required"):
+ MongoDBEmulatorContainer()
+
+ # instanciates
+ MongoDBEmulatorContainer(mongodb_version="4.0")
+
+def test_runs():
+ with MongoDBEmulatorContainer(mongodb_version="4.0", partition_count=1, bind_ports=False) as emulator:
+ assert emulator.env["AZURE_COSMOS_EMULATOR_ENABLE_MONGODB_ENDPOINT"] == "4.0"
+ assert emulator.get_exposed_port(10255) is not None, "The MongoDB endpoint's port should be exposed"
diff --git a/modules/cosmosdb/tests/test_nosql.py b/modules/cosmosdb/tests/test_nosql.py
new file mode 100644
index 000000000..ce5053a12
--- /dev/null
+++ b/modules/cosmosdb/tests/test_nosql.py
@@ -0,0 +1,6 @@
+import pytest
+from testcontainers.cosmosdb import NoSQLEmulatorContainer
+
+def test_runs():
+ with NoSQLEmulatorContainer(partition_count=1, bind_ports=False) as emulator:
+ assert emulator.get_exposed_port(8081) is not None, "The NoSQL endpoint's port should be exposed"
From 831cbcaf7af3f4cffa6ad9fd042cec47e8292bf5 Mon Sep 17 00:00:00 2001
From: Mehdi BEN ABDALLAH <@mbenabda>
Date: Sun, 26 May 2024 15:25:24 +0200
Subject: [PATCH 12/15] docs: rename classes as to not have the Mongo emulator
mistaken for the MongoDB testcontainer
---
modules/cosmosdb/README.rst | 8 +++---
.../testcontainers/cosmosdb/__init__.py | 6 ++---
.../testcontainers/cosmosdb/_emulator.py | 10 +++++--
.../cosmosdb/testcontainers/cosmosdb/_grab.py | 7 ++---
.../testcontainers/cosmosdb/mongodb.py | 15 +++++++----
.../cosmosdb/testcontainers/cosmosdb/nosql.py | 27 +++++++++++--------
modules/cosmosdb/tests/test_mongodb.py | 8 +++---
modules/cosmosdb/tests/test_nosql.py | 4 +--
8 files changed, 52 insertions(+), 33 deletions(-)
diff --git a/modules/cosmosdb/README.rst b/modules/cosmosdb/README.rst
index 94e0134fa..802cffa4e 100644
--- a/modules/cosmosdb/README.rst
+++ b/modules/cosmosdb/README.rst
@@ -1,3 +1,5 @@
-.. autoclass:: testcontainers.cosmosdb.MongoDBEmulatorContainer
-.. autoclass:: testcontainers.cosmosdb.NoSQLEmulatorContainer
-.. title:: testcontainers.cosmosdb.CosmosDBEmulatorContainer
+.. autoclass:: testcontainers.cosmosdb.CosmosDBMongoEndpointContainer
+.. title:: testcontainers.cosmosdb.CosmosDBMongoEndpointContainer
+
+.. autoclass:: testcontainers.cosmosdb.CosmosDBNoSQLEndpointContainer
+.. title:: testcontainers.cosmosdb.CosmosDBNoSQLEndpointContainer
diff --git a/modules/cosmosdb/testcontainers/cosmosdb/__init__.py b/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
index ab27eaa1a..619ddb3b4 100644
--- a/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
+++ b/modules/cosmosdb/testcontainers/cosmosdb/__init__.py
@@ -1,4 +1,4 @@
-from .mongodb import MongoDBEmulatorContainer
-from .nosql import NoSQLEmulatorContainer
+from .mongodb import CosmosDBMongoEndpointContainer
+from .nosql import CosmosDBNoSQLEndpointContainer
-__all__ = ["MongoDBEmulatorContainer", "NoSQLEmulatorContainer"]
+__all__ = ["CosmosDBMongoEndpointContainer", "CosmosDBNoSQLEndpointContainer"]
diff --git a/modules/cosmosdb/testcontainers/cosmosdb/_emulator.py b/modules/cosmosdb/testcontainers/cosmosdb/_emulator.py
index a6608b814..c5ce241c9 100644
--- a/modules/cosmosdb/testcontainers/cosmosdb/_emulator.py
+++ b/modules/cosmosdb/testcontainers/cosmosdb/_emulator.py
@@ -16,7 +16,10 @@
class CosmosDBEmulatorContainer(DockerContainer):
"""
- CosmosDB Emulator container.
+ Abstract class for CosmosDB Emulator endpoints.
+
+ Concrete implementations for each endpoint is provided by a separate class:
+ NoSQLEmulatorContainer and MongoDBEmulatorContainer.
"""
def __init__(
@@ -45,6 +48,9 @@ def __init__(
@property
def host(self) -> str:
+ """
+ Emulator host
+ """
return self.get_container_host_ip()
@property
@@ -88,7 +94,7 @@ def _wait_until_ready(self) -> Self:
def _download_cert(self) -> bytes:
with grab.file(
- self._container, "/tmp/cosmos/appdata/.system/profiles/Client/AppData/Local/CosmosDBEmulator/emulator.pem"
+ self.get_wrapped_container(), "/tmp/cosmos/appdata/.system/profiles/Client/AppData/Local/CosmosDBEmulator/emulator.pem"
) as cert:
return cert.read()
diff --git a/modules/cosmosdb/testcontainers/cosmosdb/_grab.py b/modules/cosmosdb/testcontainers/cosmosdb/_grab.py
index cbf74a06b..86ac59247 100644
--- a/modules/cosmosdb/testcontainers/cosmosdb/_grab.py
+++ b/modules/cosmosdb/testcontainers/cosmosdb/_grab.py
@@ -4,14 +4,15 @@
import tarfile
import tempfile
from contextlib import contextmanager
+from docker.models.containers import Container
@contextmanager
-def file(container, target):
+def file(container: Container, target: str):
target_path = Path(target)
assert target_path.is_absolute(), "target must be an absolute path"
- with tempfile.TemporaryDirectory() as tmpdirname:
- archive = Path(tmpdirname) / 'grabbed.tar'
+ with tempfile.TemporaryDirectory() as tmp:
+ archive = Path(tmp) / 'grabbed.tar'
# download from container as tar archive
with open(archive, 'wb') as f:
diff --git a/modules/cosmosdb/testcontainers/cosmosdb/mongodb.py b/modules/cosmosdb/testcontainers/cosmosdb/mongodb.py
index 3351604f1..c36d1e9f5 100644
--- a/modules/cosmosdb/testcontainers/cosmosdb/mongodb.py
+++ b/modules/cosmosdb/testcontainers/cosmosdb/mongodb.py
@@ -1,20 +1,22 @@
import os
from ._emulator import CosmosDBEmulatorContainer
-__all__ = ["MongoDBEmulatorContainer"]
+__all__ = ["CosmosDBMongoEndpointContainer"]
ENDPOINT_PORT = 10255
-class MongoDBEmulatorContainer(CosmosDBEmulatorContainer):
+class CosmosDBMongoEndpointContainer(CosmosDBEmulatorContainer):
"""
CosmosDB MongoDB enpoint Emulator.
Example:
.. doctest::
- >>> from testcontainers.cosmosdb import MongoDBEmulatorContainer
- >>> with CosmosDBEmulatorContainer(mongodb_version="4.0") as emulator:
- ... print(f"Point yout MongoDB client to {emulator.host}:{emulator.port}}")
+
+ >>> from testcontainers.cosmosdb import CosmosDBMongoEndpointContainer
+
+ >>> with CosmosDBMongoEndpointContainer(mongodb_version="4.0") as emulator:
+ ... print(f"Point your MongoDB client at {emulator.host}:{emulator.port}}")
"""
def __init__(
@@ -31,6 +33,9 @@ def __init__(
@property
def port(self) -> str:
+ """
+ The exposed port to the MongoDB endpoint
+ """
return self.get_exposed_port(ENDPOINT_PORT)
def _configure(self) -> None:
diff --git a/modules/cosmosdb/testcontainers/cosmosdb/nosql.py b/modules/cosmosdb/testcontainers/cosmosdb/nosql.py
index b8cb8cd51..4030e2abc 100644
--- a/modules/cosmosdb/testcontainers/cosmosdb/nosql.py
+++ b/modules/cosmosdb/testcontainers/cosmosdb/nosql.py
@@ -4,25 +4,30 @@
from testcontainers.core.waiting_utils import wait_container_is_ready
from ._emulator import CosmosDBEmulatorContainer
-__all__ = ["NoSQLEmulatorContainer"]
+__all__ = ["CosmosDBNoSQLEndpointContainer"]
NOSQL_PORT = 8081
-class NoSQLEmulatorContainer(CosmosDBEmulatorContainer):
+class CosmosDBNoSQLEndpointContainer(CosmosDBEmulatorContainer):
"""
CosmosDB NoSQL enpoint Emulator.
Example:
+
.. doctest::
- >>> from testcontainers.cosmosdb import NoSQLEmulatorContainer
- >>> with NoSQLEmulatorContainer() as emulator:
- ... db = emulator.insecure_sync_client().create_database_if_not_exists("test")
+
+ >>> from testcontainers.cosmosdb import CosmosDBNoSQLEndpointContainer
+ >>> with CosmosDBNoSQLEndpointContainer() as emulator:
+ ... db = emulator.insecure_sync_client().create_database_if_not_exists("test")
.. doctest::
- >>> from testcontainers.cosmosdb import NoSQLEmulatorContainer
- >>> with NoSQLEmulatorContainer() as emulator:
- ... client = CosmosClient(url=emulator.url, credential=emulator.key, connection_verify=False)
- ... db = client.create_database_if_not_exists("test")
+
+ >>> from testcontainers.cosmosdb import CosmosDBNoSQLEndpointContainer
+ >>> from azure.cosmos import CosmosClient
+
+ >>> with CosmosDBNoSQLEndpointContainer() as emulator:
+ ... client = CosmosClient(url=emulator.url, credential=emulator.key, connection_verify=False)
+ ... db = client.create_database_if_not_exists("test")
"""
@@ -43,13 +48,13 @@ def url(self) -> str:
"""
return f"https://{self.host}:{self.port}"
- def insecure_async_client(self) -> AsyncCosmosClient:
+ def insecure_async_client(self):
"""
Returns an asynchronous CosmosClient instance
"""
return AsyncCosmosClient(url=self.url, credential=self.key, connection_verify=False)
- def insecure_sync_client(self) -> SyncCosmosClient:
+ def insecure_sync_client(self):
"""
Returns a synchronous CosmosClient instance
"""
diff --git a/modules/cosmosdb/tests/test_mongodb.py b/modules/cosmosdb/tests/test_mongodb.py
index a321d128f..b80f8c7c8 100644
--- a/modules/cosmosdb/tests/test_mongodb.py
+++ b/modules/cosmosdb/tests/test_mongodb.py
@@ -1,14 +1,14 @@
import pytest
-from testcontainers.cosmosdb import MongoDBEmulatorContainer
+from testcontainers.cosmosdb import CosmosDBMongoEndpointContainer
def test_requires_a_version():
with pytest.raises(AssertionError, match="A MongoDB version is required"):
- MongoDBEmulatorContainer()
+ CosmosDBMongoEndpointContainer()
# instanciates
- MongoDBEmulatorContainer(mongodb_version="4.0")
+ CosmosDBMongoEndpointContainer(mongodb_version="4.0")
def test_runs():
- with MongoDBEmulatorContainer(mongodb_version="4.0", partition_count=1, bind_ports=False) as emulator:
+ with CosmosDBMongoEndpointContainer(mongodb_version="4.0", partition_count=1, bind_ports=False) as emulator:
assert emulator.env["AZURE_COSMOS_EMULATOR_ENABLE_MONGODB_ENDPOINT"] == "4.0"
assert emulator.get_exposed_port(10255) is not None, "The MongoDB endpoint's port should be exposed"
diff --git a/modules/cosmosdb/tests/test_nosql.py b/modules/cosmosdb/tests/test_nosql.py
index ce5053a12..ed91e8672 100644
--- a/modules/cosmosdb/tests/test_nosql.py
+++ b/modules/cosmosdb/tests/test_nosql.py
@@ -1,6 +1,6 @@
import pytest
-from testcontainers.cosmosdb import NoSQLEmulatorContainer
+from testcontainers.cosmosdb import CosmosDBNoSQLEndpointContainer
def test_runs():
- with NoSQLEmulatorContainer(partition_count=1, bind_ports=False) as emulator:
+ with CosmosDBNoSQLEndpointContainer(partition_count=1, bind_ports=False) as emulator:
assert emulator.get_exposed_port(8081) is not None, "The NoSQL endpoint's port should be exposed"
From fe48bf48111caa98f1583a5209ce8ed02ba4dd6c Mon Sep 17 00:00:00 2001
From: Mehdi BEN ABDALLAH <@mbenabda>
Date: Sun, 26 May 2024 15:39:21 +0200
Subject: [PATCH 13/15] linting
---
.../testcontainers/cosmosdb/_emulator.py | 30 +++++++++--------
.../cosmosdb/testcontainers/cosmosdb/_grab.py | 32 +++++++++----------
.../testcontainers/cosmosdb/mongodb.py | 6 ++--
.../cosmosdb/testcontainers/cosmosdb/nosql.py | 3 ++
modules/cosmosdb/tests/test_emulator.py | 1 +
modules/cosmosdb/tests/test_mongodb.py | 4 ++-
modules/cosmosdb/tests/test_nosql.py | 1 +
7 files changed, 44 insertions(+), 33 deletions(-)
diff --git a/modules/cosmosdb/testcontainers/cosmosdb/_emulator.py b/modules/cosmosdb/testcontainers/cosmosdb/_emulator.py
index c5ce241c9..161a01c29 100644
--- a/modules/cosmosdb/testcontainers/cosmosdb/_emulator.py
+++ b/modules/cosmosdb/testcontainers/cosmosdb/_emulator.py
@@ -2,18 +2,22 @@
import socket
import ssl
from collections.abc import Iterable
+from distutils.util import strtobool
+from urllib.error import HTTPError, URLError
+from urllib.request import urlopen
+
from typing_extensions import Self
+
from testcontainers.core.container import DockerContainer
from testcontainers.core.waiting_utils import wait_container_is_ready, wait_for_logs
+
from . import _grab as grab
-from distutils.util import strtobool
-from urllib.error import HTTPError, URLError
-from urllib.request import urlopen
__all__ = ["CosmosDBEmulatorContainer"]
EMULATOR_PORT = 8081
+
class CosmosDBEmulatorContainer(DockerContainer):
"""
Abstract class for CosmosDB Emulator endpoints.
@@ -28,9 +32,7 @@ def __init__(
"AZURE_COSMOS_EMULATOR_IMAGE", "mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest"
),
partition_count: int = os.getenv("AZURE_COSMOS_EMULATOR_PARTITION_COUNT", None),
- enable_data_persistence: bool = strtobool(
- os.getenv("AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE", "false")
- ),
+ enable_data_persistence: bool = strtobool(os.getenv("AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE", "false")),
key: str = os.getenv(
"AZURE_COSMOS_EMULATOR_KEY",
"C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
@@ -52,7 +54,7 @@ def host(self) -> str:
Emulator host
"""
return self.get_container_host_ip()
-
+
@property
def server_certificate_pem(self) -> bytes:
"""
@@ -66,9 +68,9 @@ def start(self) -> Self:
self._wait_until_ready()
self._cert_pem_bytes = self._download_cert()
return self
-
+
def _configure(self) -> None:
- all_ports = set([EMULATOR_PORT] + self.endpoint_ports)
+ all_ports = {EMULATOR_PORT, *self.endpoint_ports}
if self.bind_ports:
for port in all_ports:
self.with_bind_ports(port, port)
@@ -76,8 +78,7 @@ def _configure(self) -> None:
self.with_exposed_ports(*all_ports)
(
- self
- .with_env("AZURE_COSMOS_EMULATOR_PARTITION_COUNT", str(self.partition_count))
+ self.with_env("AZURE_COSMOS_EMULATOR_PARTITION_COUNT", str(self.partition_count))
.with_env("AZURE_COSMOS_EMULATOR_IP_ADDRESS_OVERRIDE", socket.gethostbyname(socket.gethostname()))
.with_env("AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE", str(self.enable_data_persistence))
.with_env("AZURE_COSMOS_EMULATOR_KEY", str(self.key))
@@ -89,12 +90,13 @@ def _wait_until_ready(self) -> Self:
if self.bind_ports:
self._wait_for_url(f"https://{self.host}:{EMULATOR_PORT}/_explorer/index.html")
self._wait_for_query_success()
-
+
return self
def _download_cert(self) -> bytes:
with grab.file(
- self.get_wrapped_container(), "/tmp/cosmos/appdata/.system/profiles/Client/AppData/Local/CosmosDBEmulator/emulator.pem"
+ self.get_wrapped_container(),
+ "/tmp/cosmos/appdata/.system/profiles/Client/AppData/Local/CosmosDBEmulator/emulator.pem",
) as cert:
return cert.read()
@@ -103,6 +105,6 @@ def _wait_for_url(self, url: str) -> Self:
with urlopen(url, context=ssl._create_unverified_context()) as response:
response.read()
return self
-
+
def _wait_for_query_success(self) -> None:
pass
diff --git a/modules/cosmosdb/testcontainers/cosmosdb/_grab.py b/modules/cosmosdb/testcontainers/cosmosdb/_grab.py
index 86ac59247..e1895019a 100644
--- a/modules/cosmosdb/testcontainers/cosmosdb/_grab.py
+++ b/modules/cosmosdb/testcontainers/cosmosdb/_grab.py
@@ -1,26 +1,26 @@
-
-from pathlib import Path
-from os import path
import tarfile
import tempfile
from contextlib import contextmanager
+from os import path
+from pathlib import Path
+
from docker.models.containers import Container
+
@contextmanager
def file(container: Container, target: str):
- target_path = Path(target)
- assert target_path.is_absolute(), "target must be an absolute path"
-
- with tempfile.TemporaryDirectory() as tmp:
- archive = Path(tmp) / 'grabbed.tar'
+ target_path = Path(target)
+ assert target_path.is_absolute(), "target must be an absolute path"
- # download from container as tar archive
- with open(archive, 'wb') as f:
- tar_bits, _ = container.get_archive(target)
- for chunk in tar_bits:
- f.write(chunk)
+ with tempfile.TemporaryDirectory() as tmp:
+ archive = Path(tmp) / "grabbed.tar"
- # extract target file from tar archive
- with tarfile.TarFile(archive) as tar:
- yield tar.extractfile(path.basename(target))
+ # download from container as tar archive
+ with open(archive, "wb") as f:
+ tar_bits, _ = container.get_archive(target)
+ for chunk in tar_bits:
+ f.write(chunk)
+ # extract target file from tar archive
+ with tarfile.TarFile(archive) as tar:
+ yield tar.extractfile(path.basename(target))
diff --git a/modules/cosmosdb/testcontainers/cosmosdb/mongodb.py b/modules/cosmosdb/testcontainers/cosmosdb/mongodb.py
index c36d1e9f5..b4a44932d 100644
--- a/modules/cosmosdb/testcontainers/cosmosdb/mongodb.py
+++ b/modules/cosmosdb/testcontainers/cosmosdb/mongodb.py
@@ -1,10 +1,12 @@
import os
+
from ._emulator import CosmosDBEmulatorContainer
__all__ = ["CosmosDBMongoEndpointContainer"]
ENDPOINT_PORT = 10255
+
class CosmosDBMongoEndpointContainer(CosmosDBEmulatorContainer):
"""
CosmosDB MongoDB enpoint Emulator.
@@ -21,7 +23,7 @@ class CosmosDBMongoEndpointContainer(CosmosDBEmulatorContainer):
def __init__(
self,
- mongodb_version: str = None,
+ mongodb_version: str,
image: str = os.getenv(
"AZURE_COSMOS_EMULATOR_IMAGE", "mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:mongodb"
),
@@ -37,7 +39,7 @@ def port(self) -> str:
The exposed port to the MongoDB endpoint
"""
return self.get_exposed_port(ENDPOINT_PORT)
-
+
def _configure(self) -> None:
super()._configure()
self.with_env("AZURE_COSMOS_EMULATOR_ENABLE_MONGODB_ENDPOINT", self.mongodb_version)
diff --git a/modules/cosmosdb/testcontainers/cosmosdb/nosql.py b/modules/cosmosdb/testcontainers/cosmosdb/nosql.py
index 4030e2abc..d9f2776fe 100644
--- a/modules/cosmosdb/testcontainers/cosmosdb/nosql.py
+++ b/modules/cosmosdb/testcontainers/cosmosdb/nosql.py
@@ -1,13 +1,16 @@
from azure.core.exceptions import ServiceRequestError
from azure.cosmos import CosmosClient as SyncCosmosClient
from azure.cosmos.aio import CosmosClient as AsyncCosmosClient
+
from testcontainers.core.waiting_utils import wait_container_is_ready
from ._emulator import CosmosDBEmulatorContainer
+
__all__ = ["CosmosDBNoSQLEndpointContainer"]
NOSQL_PORT = 8081
+
class CosmosDBNoSQLEndpointContainer(CosmosDBEmulatorContainer):
"""
CosmosDB NoSQL enpoint Emulator.
diff --git a/modules/cosmosdb/tests/test_emulator.py b/modules/cosmosdb/tests/test_emulator.py
index 6ace57a07..542ddd11c 100644
--- a/modules/cosmosdb/tests/test_emulator.py
+++ b/modules/cosmosdb/tests/test_emulator.py
@@ -1,6 +1,7 @@
import pytest
from testcontainers.cosmosdb._emulator import CosmosDBEmulatorContainer
+
def test_runs():
with CosmosDBEmulatorContainer(partition_count=1, bind_ports=False) as emulator:
assert emulator.server_certificate_pem is not None
diff --git a/modules/cosmosdb/tests/test_mongodb.py b/modules/cosmosdb/tests/test_mongodb.py
index b80f8c7c8..a50ee82ea 100644
--- a/modules/cosmosdb/tests/test_mongodb.py
+++ b/modules/cosmosdb/tests/test_mongodb.py
@@ -1,13 +1,15 @@
import pytest
from testcontainers.cosmosdb import CosmosDBMongoEndpointContainer
+
def test_requires_a_version():
with pytest.raises(AssertionError, match="A MongoDB version is required"):
- CosmosDBMongoEndpointContainer()
+ CosmosDBMongoEndpointContainer(mongodb_version=None)
# instanciates
CosmosDBMongoEndpointContainer(mongodb_version="4.0")
+
def test_runs():
with CosmosDBMongoEndpointContainer(mongodb_version="4.0", partition_count=1, bind_ports=False) as emulator:
assert emulator.env["AZURE_COSMOS_EMULATOR_ENABLE_MONGODB_ENDPOINT"] == "4.0"
diff --git a/modules/cosmosdb/tests/test_nosql.py b/modules/cosmosdb/tests/test_nosql.py
index ed91e8672..a9460a1b0 100644
--- a/modules/cosmosdb/tests/test_nosql.py
+++ b/modules/cosmosdb/tests/test_nosql.py
@@ -1,6 +1,7 @@
import pytest
from testcontainers.cosmosdb import CosmosDBNoSQLEndpointContainer
+
def test_runs():
with CosmosDBNoSQLEndpointContainer(partition_count=1, bind_ports=False) as emulator:
assert emulator.get_exposed_port(8081) is not None, "The NoSQL endpoint's port should be exposed"
From c90b36fa71d16d178b45e2e3543027f8a1a763a6 Mon Sep 17 00:00:00 2001
From: Mehdi BEN ABDALLAH <@mbenabda>
Date: Mon, 27 May 2024 14:19:33 +0200
Subject: [PATCH 14/15] skip doctest for the cosmosdb module (the module binds
static host ports, and tests are run concurrently for several python
versions)
---
modules/cosmosdb/testcontainers/cosmosdb/mongodb.py | 6 ++++--
modules/cosmosdb/testcontainers/cosmosdb/nosql.py | 10 +++++-----
2 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/modules/cosmosdb/testcontainers/cosmosdb/mongodb.py b/modules/cosmosdb/testcontainers/cosmosdb/mongodb.py
index b4a44932d..82e8c096b 100644
--- a/modules/cosmosdb/testcontainers/cosmosdb/mongodb.py
+++ b/modules/cosmosdb/testcontainers/cosmosdb/mongodb.py
@@ -13,12 +13,14 @@ class CosmosDBMongoEndpointContainer(CosmosDBEmulatorContainer):
Example:
- .. doctest::
+ .. code-block:: python
>>> from testcontainers.cosmosdb import CosmosDBMongoEndpointContainer
>>> with CosmosDBMongoEndpointContainer(mongodb_version="4.0") as emulator:
- ... print(f"Point your MongoDB client at {emulator.host}:{emulator.port}}")
+ ... print(f"Point your MongoDB client at {emulator.host}:{emulator.port} using key {emulator.key}")
+ ... print(f"and eiher disable TLS server auth or trust the server's self signed cert (emulator.server_certificate_pem)")
+
"""
def __init__(
diff --git a/modules/cosmosdb/testcontainers/cosmosdb/nosql.py b/modules/cosmosdb/testcontainers/cosmosdb/nosql.py
index d9f2776fe..f78469674 100644
--- a/modules/cosmosdb/testcontainers/cosmosdb/nosql.py
+++ b/modules/cosmosdb/testcontainers/cosmosdb/nosql.py
@@ -17,20 +17,20 @@ class CosmosDBNoSQLEndpointContainer(CosmosDBEmulatorContainer):
Example:
- .. doctest::
+ .. code-block:: python
>>> from testcontainers.cosmosdb import CosmosDBNoSQLEndpointContainer
>>> with CosmosDBNoSQLEndpointContainer() as emulator:
- ... db = emulator.insecure_sync_client().create_database_if_not_exists("test")
+ ... db = emulator.insecure_sync_client().create_database_if_not_exists("test")
- .. doctest::
+ .. code-block:: python
>>> from testcontainers.cosmosdb import CosmosDBNoSQLEndpointContainer
>>> from azure.cosmos import CosmosClient
>>> with CosmosDBNoSQLEndpointContainer() as emulator:
- ... client = CosmosClient(url=emulator.url, credential=emulator.key, connection_verify=False)
- ... db = client.create_database_if_not_exists("test")
+ ... client = CosmosClient(url=emulator.url, credential=emulator.key, connection_verify=False)
+ ... db = client.create_database_if_not_exists("test")
"""
From d57fbcc79ec960f7fa87eded9b62a6c82b80b8f8 Mon Sep 17 00:00:00 2001
From: David Ankin
Date: Fri, 28 Jun 2024 04:53:51 -0400
Subject: [PATCH 15/15] poetry.lock - add missing packages from rebase mistake
---
poetry.lock | 298 ++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 290 insertions(+), 8 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index edb5d0374..90a83f33f 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -946,6 +946,27 @@ files = [
{file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"},
]
+[[package]]
+name = "environs"
+version = "9.5.0"
+description = "simplified environment variable parsing"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "environs-9.5.0-py2.py3-none-any.whl", hash = "sha256:1e549569a3de49c05f856f40bce86979e7d5ffbbc4398e7f338574c220189124"},
+ {file = "environs-9.5.0.tar.gz", hash = "sha256:a76307b36fbe856bdca7ee9161e6c466fd7fcffc297109a118c59b54e27e30c9"},
+]
+
+[package.dependencies]
+marshmallow = ">=3.0.0"
+python-dotenv = "*"
+
+[package.extras]
+dev = ["dj-database-url", "dj-email-url", "django-cache-url", "flake8 (==4.0.1)", "flake8-bugbear (==21.9.2)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)", "pytest", "tox"]
+django = ["dj-database-url", "dj-email-url", "django-cache-url"]
+lint = ["flake8 (==4.0.1)", "flake8-bugbear (==21.9.2)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)"]
+tests = ["dj-database-url", "dj-email-url", "django-cache-url", "pytest"]
+
[[package]]
name = "exceptiongroup"
version = "1.2.0"
@@ -1220,7 +1241,7 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4
name = "grpcio"
version = "1.62.1"
description = "HTTP/2-based RPC framework"
-optional = true
+optional = false
python-versions = ">=3.7"
files = [
{file = "grpcio-1.62.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:179bee6f5ed7b5f618844f760b6acf7e910988de77a4f75b95bbfaa8106f3c1e"},
@@ -1385,7 +1406,7 @@ setuptools = "*"
name = "h11"
version = "0.14.0"
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
-optional = true
+optional = false
python-versions = ">=3.7"
files = [
{file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
@@ -1422,7 +1443,7 @@ files = [
name = "httpcore"
version = "1.0.5"
description = "A minimal low-level HTTP client."
-optional = true
+optional = false
python-versions = ">=3.8"
files = [
{file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"},
@@ -1443,7 +1464,7 @@ trio = ["trio (>=0.22.0,<0.26.0)"]
name = "httpx"
version = "0.27.0"
description = "The next generation HTTP client."
-optional = true
+optional = false
python-versions = ">=3.8"
files = [
{file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"},
@@ -1853,6 +1874,25 @@ files = [
{file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"},
]
+[[package]]
+name = "marshmallow"
+version = "3.21.3"
+description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "marshmallow-3.21.3-py3-none-any.whl", hash = "sha256:86ce7fb914aa865001a4b2092c4c2872d13bc347f3d42673272cabfdbad386f1"},
+ {file = "marshmallow-3.21.3.tar.gz", hash = "sha256:4f57c5e050a54d66361e826f94fba213eb10b67b2fdb02c3e0343ce207ba1662"},
+]
+
+[package.dependencies]
+packaging = ">=17.0"
+
+[package.extras]
+dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"]
+docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.3.7)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"]
+tests = ["pytest", "pytz", "simplejson"]
+
[[package]]
name = "mdurl"
version = "0.1.2"
@@ -1864,6 +1904,18 @@ files = [
{file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
]
+[[package]]
+name = "milvus-lite"
+version = "2.4.7"
+description = "A lightweight version of Milvus wrapped with Python."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "milvus_lite-2.4.7-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:c828190118b104b05b8c8e0b5a4147811c86b54b8fb67bc2e726ad10fc0b544e"},
+ {file = "milvus_lite-2.4.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e1537633c39879714fb15082be56a4b97f74c905a6e98e302ec01320561081af"},
+ {file = "milvus_lite-2.4.7-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f016474d663045787dddf1c3aad13b7d8b61fd329220318f858184918143dcbf"},
+]
+
[[package]]
name = "minio"
version = "7.2.5"
@@ -2103,7 +2155,7 @@ setuptools = "*"
name = "numpy"
version = "1.26.4"
description = "Fundamental package for array computing in Python"
-optional = true
+optional = false
python-versions = ">=3.9"
files = [
{file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"},
@@ -2402,6 +2454,93 @@ files = [
{file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
]
+[[package]]
+name = "paho-mqtt"
+version = "2.1.0"
+description = "MQTT version 5.0/3.1.1 client class"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "paho_mqtt-2.1.0-py3-none-any.whl", hash = "sha256:6db9ba9b34ed5bc6b6e3812718c7e06e2fd7444540df2455d2c51bd58808feee"},
+ {file = "paho_mqtt-2.1.0.tar.gz", hash = "sha256:12d6e7511d4137555a3f6ea167ae846af2c7357b10bc6fa4f7c3968fc1723834"},
+]
+
+[package.extras]
+proxy = ["pysocks"]
+
+[[package]]
+name = "pandas"
+version = "2.2.2"
+description = "Powerful data structures for data analysis, time series, and statistics"
+optional = false
+python-versions = ">=3.9"
+files = [
+ {file = "pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce"},
+ {file = "pandas-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7adfc142dac335d8c1e0dcbd37eb8617eac386596eb9e1a1b77791cf2498238"},
+ {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08"},
+ {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0"},
+ {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51"},
+ {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e5a0b00e1e56a842f922e7fae8ae4077aee4af0acb5ae3622bd4b4c30aedf99"},
+ {file = "pandas-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:ddf818e4e6c7c6f4f7c8a12709696d193976b591cc7dc50588d3d1a6b5dc8772"},
+ {file = "pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288"},
+ {file = "pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151"},
+ {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b"},
+ {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee"},
+ {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db"},
+ {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"},
+ {file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"},
+ {file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"},
+ {file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"},
+ {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"},
+ {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"},
+ {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"},
+ {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"},
+ {file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"},
+ {file = "pandas-2.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2"},
+ {file = "pandas-2.2.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9057e6aa78a584bc93a13f0a9bf7e753a5e9770a30b4d758b8d5f2a62a9433cd"},
+ {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863"},
+ {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921"},
+ {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a"},
+ {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92fd6b027924a7e178ac202cfbe25e53368db90d56872d20ffae94b96c7acc57"},
+ {file = "pandas-2.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:640cef9aa381b60e296db324337a554aeeb883ead99dc8f6c18e81a93942f5f4"},
+ {file = "pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54"},
+]
+
+[package.dependencies]
+numpy = [
+ {version = ">=1.23.2", markers = "python_version == \"3.11\""},
+ {version = ">=1.22.4", markers = "python_version < \"3.11\""},
+ {version = ">=1.26.0", markers = "python_version >= \"3.12\""},
+]
+python-dateutil = ">=2.8.2"
+pytz = ">=2020.1"
+tzdata = ">=2022.7"
+
+[package.extras]
+all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"]
+aws = ["s3fs (>=2022.11.0)"]
+clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"]
+compression = ["zstandard (>=0.19.0)"]
+computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"]
+consortium-standard = ["dataframe-api-compat (>=0.1.7)"]
+excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"]
+feather = ["pyarrow (>=10.0.1)"]
+fss = ["fsspec (>=2022.11.0)"]
+gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"]
+hdf5 = ["tables (>=3.8.0)"]
+html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"]
+mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"]
+output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"]
+parquet = ["pyarrow (>=10.0.1)"]
+performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"]
+plot = ["matplotlib (>=3.6.3)"]
+postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"]
+pyarrow = ["pyarrow (>=10.0.1)"]
+spss = ["pyreadstat (>=1.2.0)"]
+sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"]
+test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"]
+xml = ["lxml (>=4.9.2)"]
+
[[package]]
name = "pg8000"
version = "1.30.5"
@@ -2558,7 +2697,7 @@ testing = ["google-api-core[grpc] (>=1.31.5)"]
name = "protobuf"
version = "4.25.3"
description = ""
-optional = true
+optional = false
python-versions = ">=3.8"
files = [
{file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"},
@@ -2897,6 +3036,31 @@ dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pyte
docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"]
tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
+[[package]]
+name = "pymilvus"
+version = "2.4.3"
+description = "Python Sdk for Milvus"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pymilvus-2.4.3-py3-none-any.whl", hash = "sha256:38239e89f8d739f665141d0b80908990b5f59681e889e135c234a4a45669a5c8"},
+ {file = "pymilvus-2.4.3.tar.gz", hash = "sha256:703ac29296cdce03d6dc2aaebbe959e57745c141a94150e371dc36c61c226cc1"},
+]
+
+[package.dependencies]
+environs = "<=9.5.0"
+grpcio = ">=1.49.1,<=1.63.0"
+milvus-lite = ">=2.4.0,<2.5.0"
+pandas = ">=1.2.4"
+protobuf = ">=3.20.0"
+setuptools = ">=67"
+ujson = ">=2.0.0"
+
+[package.extras]
+bulk-writer = ["azure-storage-blob", "minio (>=7.0.0)", "pyarrow (>=12.0.0)", "requests"]
+dev = ["black", "grpcio (==1.62.2)", "grpcio-testing (==1.62.2)", "grpcio-tools (==1.62.2)", "pytest (>=5.3.4)", "pytest-cov (>=2.8.1)", "pytest-timeout (>=1.3.4)", "ruff (>0.4.0)"]
+model = ["milvus-model (>=0.1.0)"]
+
[[package]]
name = "pymongo"
version = "4.6.2"
@@ -3202,6 +3366,20 @@ files = [
[package.dependencies]
six = ">=1.5"
+[[package]]
+name = "python-dotenv"
+version = "1.0.1"
+description = "Read key-value pairs from a .env file and set them as environment variables"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"},
+ {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"},
+]
+
+[package.extras]
+cli = ["click (>=5.0)"]
+
[[package]]
name = "python-keycloak"
version = "3.9.1"
@@ -3226,7 +3404,7 @@ docs = ["Sphinx (>=6.1.0,<7.0.0)", "alabaster (>=0.7.12,<0.8.0)", "commonmark (>
name = "pytz"
version = "2024.1"
description = "World timezone definitions, modern and historical"
-optional = true
+optional = false
python-versions = "*"
files = [
{file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"},
@@ -3843,6 +4021,20 @@ postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"]
pymysql = ["pymysql"]
sqlcipher = ["sqlcipher3_binary"]
+[[package]]
+name = "sqlalchemy-cockroachdb"
+version = "2.0.2"
+description = "CockroachDB dialect for SQLAlchemy"
+optional = false
+python-versions = "*"
+files = [
+ {file = "sqlalchemy-cockroachdb-2.0.2.tar.gz", hash = "sha256:119756eb905855d6a11345b99cfe853031a3fe598a9c4bf35a8ddac9f89fe8cc"},
+ {file = "sqlalchemy_cockroachdb-2.0.2-py3-none-any.whl", hash = "sha256:0d5d50e805b024cb2ccd85423a5c1a367d1a56a5cd0ea47765233fd47665070d"},
+]
+
+[package.dependencies]
+SQLAlchemy = "*"
+
[[package]]
name = "tenacity"
version = "8.2.3"
@@ -3965,6 +4157,93 @@ tzdata = {version = "*", markers = "platform_system == \"Windows\""}
[package.extras]
devenv = ["check-manifest", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3)", "zest.releaser"]
+[[package]]
+name = "ujson"
+version = "5.10.0"
+description = "Ultra fast JSON encoder and decoder for Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "ujson-5.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2601aa9ecdbee1118a1c2065323bda35e2c5a2cf0797ef4522d485f9d3ef65bd"},
+ {file = "ujson-5.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:348898dd702fc1c4f1051bc3aacbf894caa0927fe2c53e68679c073375f732cf"},
+ {file = "ujson-5.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22cffecf73391e8abd65ef5f4e4dd523162a3399d5e84faa6aebbf9583df86d6"},
+ {file = "ujson-5.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26b0e2d2366543c1bb4fbd457446f00b0187a2bddf93148ac2da07a53fe51569"},
+ {file = "ujson-5.10.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:caf270c6dba1be7a41125cd1e4fc7ba384bf564650beef0df2dd21a00b7f5770"},
+ {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a245d59f2ffe750446292b0094244df163c3dc96b3ce152a2c837a44e7cda9d1"},
+ {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:94a87f6e151c5f483d7d54ceef83b45d3a9cca7a9cb453dbdbb3f5a6f64033f5"},
+ {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:29b443c4c0a113bcbb792c88bea67b675c7ca3ca80c3474784e08bba01c18d51"},
+ {file = "ujson-5.10.0-cp310-cp310-win32.whl", hash = "sha256:c18610b9ccd2874950faf474692deee4223a994251bc0a083c114671b64e6518"},
+ {file = "ujson-5.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:924f7318c31874d6bb44d9ee1900167ca32aa9b69389b98ecbde34c1698a250f"},
+ {file = "ujson-5.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a5b366812c90e69d0f379a53648be10a5db38f9d4ad212b60af00bd4048d0f00"},
+ {file = "ujson-5.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:502bf475781e8167f0f9d0e41cd32879d120a524b22358e7f205294224c71126"},
+ {file = "ujson-5.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b91b5d0d9d283e085e821651184a647699430705b15bf274c7896f23fe9c9d8"},
+ {file = "ujson-5.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:129e39af3a6d85b9c26d5577169c21d53821d8cf68e079060602e861c6e5da1b"},
+ {file = "ujson-5.10.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f77b74475c462cb8b88680471193064d3e715c7c6074b1c8c412cb526466efe9"},
+ {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7ec0ca8c415e81aa4123501fee7f761abf4b7f386aad348501a26940beb1860f"},
+ {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab13a2a9e0b2865a6c6db9271f4b46af1c7476bfd51af1f64585e919b7c07fd4"},
+ {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:57aaf98b92d72fc70886b5a0e1a1ca52c2320377360341715dd3933a18e827b1"},
+ {file = "ujson-5.10.0-cp311-cp311-win32.whl", hash = "sha256:2987713a490ceb27edff77fb184ed09acdc565db700ee852823c3dc3cffe455f"},
+ {file = "ujson-5.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:f00ea7e00447918ee0eff2422c4add4c5752b1b60e88fcb3c067d4a21049a720"},
+ {file = "ujson-5.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98ba15d8cbc481ce55695beee9f063189dce91a4b08bc1d03e7f0152cd4bbdd5"},
+ {file = "ujson-5.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9d2edbf1556e4f56e50fab7d8ff993dbad7f54bac68eacdd27a8f55f433578e"},
+ {file = "ujson-5.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6627029ae4f52d0e1a2451768c2c37c0c814ffc04f796eb36244cf16b8e57043"},
+ {file = "ujson-5.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8ccb77b3e40b151e20519c6ae6d89bfe3f4c14e8e210d910287f778368bb3d1"},
+ {file = "ujson-5.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3caf9cd64abfeb11a3b661329085c5e167abbe15256b3b68cb5d914ba7396f3"},
+ {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6e32abdce572e3a8c3d02c886c704a38a1b015a1fb858004e03d20ca7cecbb21"},
+ {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a65b6af4d903103ee7b6f4f5b85f1bfd0c90ba4eeac6421aae436c9988aa64a2"},
+ {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:604a046d966457b6cdcacc5aa2ec5314f0e8c42bae52842c1e6fa02ea4bda42e"},
+ {file = "ujson-5.10.0-cp312-cp312-win32.whl", hash = "sha256:6dea1c8b4fc921bf78a8ff00bbd2bfe166345f5536c510671bccececb187c80e"},
+ {file = "ujson-5.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:38665e7d8290188b1e0d57d584eb8110951a9591363316dd41cf8686ab1d0abc"},
+ {file = "ujson-5.10.0-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:618efd84dc1acbd6bff8eaa736bb6c074bfa8b8a98f55b61c38d4ca2c1f7f287"},
+ {file = "ujson-5.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38d5d36b4aedfe81dfe251f76c0467399d575d1395a1755de391e58985ab1c2e"},
+ {file = "ujson-5.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67079b1f9fb29ed9a2914acf4ef6c02844b3153913eb735d4bf287ee1db6e557"},
+ {file = "ujson-5.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7d0e0ceeb8fe2468c70ec0c37b439dd554e2aa539a8a56365fd761edb418988"},
+ {file = "ujson-5.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:59e02cd37bc7c44d587a0ba45347cc815fb7a5fe48de16bf05caa5f7d0d2e816"},
+ {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2a890b706b64e0065f02577bf6d8ca3b66c11a5e81fb75d757233a38c07a1f20"},
+ {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:621e34b4632c740ecb491efc7f1fcb4f74b48ddb55e65221995e74e2d00bbff0"},
+ {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b9500e61fce0cfc86168b248104e954fead61f9be213087153d272e817ec7b4f"},
+ {file = "ujson-5.10.0-cp313-cp313-win32.whl", hash = "sha256:4c4fc16f11ac1612f05b6f5781b384716719547e142cfd67b65d035bd85af165"},
+ {file = "ujson-5.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:4573fd1695932d4f619928fd09d5d03d917274381649ade4328091ceca175539"},
+ {file = "ujson-5.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a984a3131da7f07563057db1c3020b1350a3e27a8ec46ccbfbf21e5928a43050"},
+ {file = "ujson-5.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73814cd1b9db6fc3270e9d8fe3b19f9f89e78ee9d71e8bd6c9a626aeaeaf16bd"},
+ {file = "ujson-5.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61e1591ed9376e5eddda202ec229eddc56c612b61ac6ad07f96b91460bb6c2fb"},
+ {file = "ujson-5.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2c75269f8205b2690db4572a4a36fe47cd1338e4368bc73a7a0e48789e2e35a"},
+ {file = "ujson-5.10.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7223f41e5bf1f919cd8d073e35b229295aa8e0f7b5de07ed1c8fddac63a6bc5d"},
+ {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc2fd6b3067c0782e7002ac3b38cf48608ee6366ff176bbd02cf969c9c20fe"},
+ {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:232cc85f8ee3c454c115455195a205074a56ff42608fd6b942aa4c378ac14dd7"},
+ {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:cc6139531f13148055d691e442e4bc6601f6dba1e6d521b1585d4788ab0bfad4"},
+ {file = "ujson-5.10.0-cp38-cp38-win32.whl", hash = "sha256:e7ce306a42b6b93ca47ac4a3b96683ca554f6d35dd8adc5acfcd55096c8dfcb8"},
+ {file = "ujson-5.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:e82d4bb2138ab05e18f089a83b6564fee28048771eb63cdecf4b9b549de8a2cc"},
+ {file = "ujson-5.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dfef2814c6b3291c3c5f10065f745a1307d86019dbd7ea50e83504950136ed5b"},
+ {file = "ujson-5.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4734ee0745d5928d0ba3a213647f1c4a74a2a28edc6d27b2d6d5bd9fa4319e27"},
+ {file = "ujson-5.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d47ebb01bd865fdea43da56254a3930a413f0c5590372a1241514abae8aa7c76"},
+ {file = "ujson-5.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dee5e97c2496874acbf1d3e37b521dd1f307349ed955e62d1d2f05382bc36dd5"},
+ {file = "ujson-5.10.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7490655a2272a2d0b072ef16b0b58ee462f4973a8f6bbe64917ce5e0a256f9c0"},
+ {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ba17799fcddaddf5c1f75a4ba3fd6441f6a4f1e9173f8a786b42450851bd74f1"},
+ {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2aff2985cef314f21d0fecc56027505804bc78802c0121343874741650a4d3d1"},
+ {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ad88ac75c432674d05b61184178635d44901eb749786c8eb08c102330e6e8996"},
+ {file = "ujson-5.10.0-cp39-cp39-win32.whl", hash = "sha256:2544912a71da4ff8c4f7ab5606f947d7299971bdd25a45e008e467ca638d13c9"},
+ {file = "ujson-5.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:3ff201d62b1b177a46f113bb43ad300b424b7847f9c5d38b1b4ad8f75d4a282a"},
+ {file = "ujson-5.10.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5b6fee72fa77dc172a28f21693f64d93166534c263adb3f96c413ccc85ef6e64"},
+ {file = "ujson-5.10.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:61d0af13a9af01d9f26d2331ce49bb5ac1fb9c814964018ac8df605b5422dcb3"},
+ {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecb24f0bdd899d368b715c9e6664166cf694d1e57be73f17759573a6986dd95a"},
+ {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fbd8fd427f57a03cff3ad6574b5e299131585d9727c8c366da4624a9069ed746"},
+ {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beeaf1c48e32f07d8820c705ff8e645f8afa690cca1544adba4ebfa067efdc88"},
+ {file = "ujson-5.10.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:baed37ea46d756aca2955e99525cc02d9181de67f25515c468856c38d52b5f3b"},
+ {file = "ujson-5.10.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7663960f08cd5a2bb152f5ee3992e1af7690a64c0e26d31ba7b3ff5b2ee66337"},
+ {file = "ujson-5.10.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:d8640fb4072d36b08e95a3a380ba65779d356b2fee8696afeb7794cf0902d0a1"},
+ {file = "ujson-5.10.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78778a3aa7aafb11e7ddca4e29f46bc5139131037ad628cc10936764282d6753"},
+ {file = "ujson-5.10.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0111b27f2d5c820e7f2dbad7d48e3338c824e7ac4d2a12da3dc6061cc39c8e6"},
+ {file = "ujson-5.10.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:c66962ca7565605b355a9ed478292da628b8f18c0f2793021ca4425abf8b01e5"},
+ {file = "ujson-5.10.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ba43cc34cce49cf2d4bc76401a754a81202d8aa926d0e2b79f0ee258cb15d3a4"},
+ {file = "ujson-5.10.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:ac56eb983edce27e7f51d05bc8dd820586c6e6be1c5216a6809b0c668bb312b8"},
+ {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44bd4b23a0e723bf8b10628288c2c7c335161d6840013d4d5de20e48551773b"},
+ {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c10f4654e5326ec14a46bcdeb2b685d4ada6911050aa8baaf3501e57024b804"},
+ {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0de4971a89a762398006e844ae394bd46991f7c385d7a6a3b93ba229e6dac17e"},
+ {file = "ujson-5.10.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e1402f0564a97d2a52310ae10a64d25bcef94f8dd643fcf5d310219d915484f7"},
+ {file = "ujson-5.10.0.tar.gz", hash = "sha256:b3cd8f3c5d8c7738257f1018880444f7b7d9b66232c64649f562d7ba86ad4bc1"},
+]
+
[[package]]
name = "urllib3"
version = "1.26.18"
@@ -4208,13 +4487,16 @@ kafka = []
keycloak = ["python-keycloak"]
localstack = ["boto3"]
memcached = []
+milvus = []
minio = ["minio"]
mongodb = ["pymongo"]
+mqtt = []
mssql = ["pymssql", "sqlalchemy"]
mysql = ["pymysql", "sqlalchemy"]
nats = ["nats-py"]
neo4j = ["neo4j"]
nginx = []
+ollama = []
opensearch = ["opensearch-py"]
oracle = ["oracledb", "sqlalchemy"]
oracle-free = ["oracledb", "sqlalchemy"]
@@ -4231,4 +4513,4 @@ weaviate = ["weaviate-client"]
[metadata]
lock-version = "2.0"
python-versions = ">=3.9,<4.0"
-content-hash = "4936c8e58fd7f323d1e4d085607f39e418393fc704bcf3d92f33b1fc15385a3f"
+content-hash = "2b87af7b69af2cc83f8198ab0fcfef7ceaf8411a8300c4ca72c0521e5d966445"