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"